github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/fsworkout/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/swiftstack/ProxyFS/conf"
    11  	"github.com/swiftstack/ProxyFS/fs"
    12  	"github.com/swiftstack/ProxyFS/inode"
    13  	"github.com/swiftstack/ProxyFS/trackedlock"
    14  	"github.com/swiftstack/ProxyFS/transitions"
    15  )
    16  
    17  const (
    18  	dirInodeNamePrefix  = "__fsworkout_dir_"
    19  	fileInodeNamePrefix = "__fsworkout_file_"
    20  )
    21  
    22  var (
    23  	doNextStepChan  chan bool
    24  	inodesPerThread uint64
    25  	measureCreate   bool
    26  	measureDestroy  bool
    27  	measureStat     bool
    28  	perThreadDir    bool
    29  	rootDirMutex    trackedlock.Mutex
    30  	stepErrChan     chan error
    31  	threads         uint64
    32  	volumeHandle    fs.VolumeHandle
    33  	volumeName      string
    34  )
    35  
    36  func usage(file *os.File) {
    37  	fmt.Fprintf(file, "Usage:\n")
    38  	fmt.Fprintf(file, "    %v [cCsSdD] threads inodes-per-thread conf-file [section.option=value]*\n", os.Args[0])
    39  	fmt.Fprintf(file, "  where:\n")
    40  	fmt.Fprintf(file, "    c                       run create  test in common root dir\n")
    41  	fmt.Fprintf(file, "    C                       run create  test in per thread  dir\n")
    42  	fmt.Fprintf(file, "    s                       run stat    test in common root dir\n")
    43  	fmt.Fprintf(file, "    S                       run stat    test in per thread  dir\n")
    44  	fmt.Fprintf(file, "    d                       run destroy test in common root dir\n")
    45  	fmt.Fprintf(file, "    D                       run destroy test in per thread  dir\n")
    46  	fmt.Fprintf(file, "    threads                 number of threads\n")
    47  	fmt.Fprintf(file, "    inodes-per-thread       number of inodes each thread will reference\n")
    48  	fmt.Fprintf(file, "    conf-file               input to conf.MakeConfMapFromFile()\n")
    49  	fmt.Fprintf(file, "    [section.option=value]* optional input to conf.UpdateFromStrings()\n")
    50  	fmt.Fprintf(file, "\n")
    51  	fmt.Fprintf(file, "Note: Precisely one test selector must be specified\n")
    52  	fmt.Fprintf(file, "      It is expected that c, s, then d are run in sequence\n")
    53  	fmt.Fprintf(file, "                  or that C, S, then D are run in sequence\n")
    54  	fmt.Fprintf(file, "      It is expected that cleanproxyfs is run before & after the sequence\n")
    55  }
    56  
    57  func main() {
    58  	var (
    59  		confMap                      conf.ConfMap
    60  		durationOfMeasuredOperations time.Duration
    61  		err                          error
    62  		latencyPerOpInMilliSeconds   float64
    63  		opsPerSecond                 float64
    64  		primaryPeer                  string
    65  		threadIndex                  uint64
    66  		timeAfterMeasuredOperations  time.Time
    67  		timeBeforeMeasuredOperations time.Time
    68  		volumeGroupToCheck           string
    69  		volumeGroupToUse             string
    70  		volumeGroupList              []string
    71  		volumeList                   []string
    72  		whoAmI                       string
    73  	)
    74  
    75  	// Parse arguments
    76  
    77  	if 5 > len(os.Args) {
    78  		usage(os.Stderr)
    79  		os.Exit(1)
    80  	}
    81  
    82  	switch os.Args[1] {
    83  	case "c":
    84  		measureCreate = true
    85  	case "C":
    86  		measureCreate = true
    87  		perThreadDir = true
    88  	case "s":
    89  		measureStat = true
    90  	case "S":
    91  		measureStat = true
    92  		perThreadDir = true
    93  	case "d":
    94  		measureDestroy = true
    95  	case "D":
    96  		measureDestroy = true
    97  		perThreadDir = true
    98  	default:
    99  		fmt.Fprintf(os.Stderr, "os.Args[1] ('%v') must be one of 'c', 'C', 'r', 'R', 'd', or 'D'\n", os.Args[1])
   100  		os.Exit(1)
   101  	}
   102  
   103  	threads, err = strconv.ParseUint(os.Args[2], 10, 64)
   104  	if nil != err {
   105  		fmt.Fprintf(os.Stderr, "strconv.ParseUint(\"%v\", 10, 64) of threads failed: %v\n", os.Args[2], err)
   106  		os.Exit(1)
   107  	}
   108  	if 0 == threads {
   109  		fmt.Fprintf(os.Stderr, "threads must be a positive number\n")
   110  		os.Exit(1)
   111  	}
   112  
   113  	inodesPerThread, err = strconv.ParseUint(os.Args[3], 10, 64)
   114  	if nil != err {
   115  		fmt.Fprintf(os.Stderr, "strconv.ParseUint(\"%v\", 10, 64) of inodes-per-thread failed: %v\n", os.Args[3], err)
   116  		os.Exit(1)
   117  	}
   118  	if 0 == inodesPerThread {
   119  		fmt.Fprintf(os.Stderr, "inodes-per-thread must be a positive number\n")
   120  		os.Exit(1)
   121  	}
   122  
   123  	confMap, err = conf.MakeConfMapFromFile(os.Args[4])
   124  	if nil != err {
   125  		fmt.Fprintf(os.Stderr, "conf.MakeConfMapFromFile(\"%v\") failed: %v\n", os.Args[4], err)
   126  		os.Exit(1)
   127  	}
   128  
   129  	if 5 < len(os.Args) {
   130  		err = confMap.UpdateFromStrings(os.Args[5:])
   131  		if nil != err {
   132  			fmt.Fprintf(os.Stderr, "confMap.UpdateFromStrings(%#v) failed: %v\n", os.Args[5:], err)
   133  			os.Exit(1)
   134  		}
   135  	}
   136  
   137  	// Upgrade confMap if necessary
   138  	err = transitions.UpgradeConfMapIfNeeded(confMap)
   139  	if nil != err {
   140  		fmt.Fprintf(os.Stderr, "Failed to upgrade config: %v", err)
   141  		os.Exit(1)
   142  	}
   143  
   144  	// Start up needed ProxyFS components
   145  
   146  	err = transitions.Up(confMap)
   147  	if nil != err {
   148  		fmt.Fprintf(os.Stderr, "transitions.Up() failed: %v\n", err)
   149  		os.Exit(1)
   150  	}
   151  
   152  	// Select first Volume of the first "active" VolumeGroup in [FSGlobals]VolumeGroupList
   153  	whoAmI, err = confMap.FetchOptionValueString("Cluster", "WhoAmI")
   154  	if nil != err {
   155  		fmt.Fprintf(os.Stderr, "confMap.FetchOptionValueString(\"Cluster\", \"WhoAmI\") failed: %v\n", err)
   156  		os.Exit(1)
   157  	}
   158  
   159  	volumeGroupList, err = confMap.FetchOptionValueStringSlice("FSGlobals", "VolumeGroupList")
   160  	if nil != err {
   161  		fmt.Fprintf(os.Stderr, "confMap.FetchOptionValueStringSlice(\"FSGlobals\", \"VolumeGroupList\") failed: %v\n", err)
   162  		os.Exit(1)
   163  	}
   164  
   165  	volumeGroupToUse = ""
   166  
   167  	for _, volumeGroupToCheck = range volumeGroupList {
   168  		primaryPeer, err = confMap.FetchOptionValueString("VolumeGroup:"+volumeGroupToCheck, "PrimaryPeer")
   169  		if nil != err {
   170  			fmt.Fprintf(os.Stderr, "confMap.FetchOptionValueString(\"VolumeGroup:%s\", \"PrimaryPeer\") failed: %v\n", volumeGroupToCheck, err)
   171  			os.Exit(1)
   172  		}
   173  		if whoAmI == primaryPeer {
   174  			volumeGroupToUse = volumeGroupToCheck
   175  			break
   176  		}
   177  	}
   178  
   179  	if "" == volumeGroupToUse {
   180  		fmt.Fprintf(os.Stderr, "confMap didn't contain an \"active\" VolumeGroup")
   181  		os.Exit(1)
   182  	}
   183  
   184  	volumeList, err = confMap.FetchOptionValueStringSlice("VolumeGroup:"+volumeGroupToUse, "VolumeList")
   185  	if nil != err {
   186  		fmt.Fprintf(os.Stderr, "confMap.FetchOptionValueStringSlice(\"VolumeGroup:%s\", \"PrimaryPeer\") failed: %v\n", volumeGroupToUse, err)
   187  		os.Exit(1)
   188  	}
   189  	if 1 > len(volumeList) {
   190  		fmt.Fprintf(os.Stderr, "confMap.FetchOptionValueStringSlice(\"VolumeGroup:%s\", \"VolumeList\") returned empty volumeList", volumeGroupToUse)
   191  		os.Exit(1)
   192  	}
   193  
   194  	volumeName = volumeList[0]
   195  
   196  	volumeHandle, err = fs.FetchVolumeHandleByVolumeName(volumeName)
   197  	if nil != err {
   198  		fmt.Fprintf(os.Stderr, "fs.FetchVolumeHandleByVolumeName(\"%value\",) failed: %v\n", volumeName, err)
   199  		os.Exit(1)
   200  	}
   201  
   202  	// Perform tests
   203  
   204  	stepErrChan = make(chan error, 0)   //threads)
   205  	doNextStepChan = make(chan bool, 0) //threads)
   206  
   207  	// Do initialization step
   208  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   209  		go fsWorkout(threadIndex)
   210  	}
   211  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   212  		err = <-stepErrChan
   213  		if nil != err {
   214  			fmt.Fprintf(os.Stderr, "fsWorkout() initialization step returned: %v\n", err)
   215  			os.Exit(1)
   216  		}
   217  	}
   218  
   219  	// Do measured operations step
   220  	timeBeforeMeasuredOperations = time.Now()
   221  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   222  		doNextStepChan <- true
   223  	}
   224  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   225  		err = <-stepErrChan
   226  		if nil != err {
   227  			fmt.Fprintf(os.Stderr, "fsWorkout() measured operations step returned: %v\n", err)
   228  			os.Exit(1)
   229  		}
   230  	}
   231  	timeAfterMeasuredOperations = time.Now()
   232  
   233  	// Do shutdown step
   234  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   235  		doNextStepChan <- true
   236  	}
   237  	for threadIndex = uint64(0); threadIndex < threads; threadIndex++ {
   238  		err = <-stepErrChan
   239  		if nil != err {
   240  			fmt.Fprintf(os.Stderr, "fsWorkout() shutdown step returned: %v\n", err)
   241  			os.Exit(1)
   242  		}
   243  	}
   244  
   245  	// Stop ProxyFS components launched above
   246  
   247  	err = transitions.Down(confMap)
   248  	if nil != err {
   249  		fmt.Fprintf(os.Stderr, "transitions.Down() failed: %v\n", err)
   250  		os.Exit(1)
   251  	}
   252  
   253  	// Report results
   254  
   255  	durationOfMeasuredOperations = timeAfterMeasuredOperations.Sub(timeBeforeMeasuredOperations)
   256  
   257  	opsPerSecond = float64(threads*inodesPerThread*1000*1000*1000) / float64(durationOfMeasuredOperations.Nanoseconds())
   258  	latencyPerOpInMilliSeconds = float64(durationOfMeasuredOperations.Nanoseconds()) / float64(inodesPerThread*1000*1000)
   259  
   260  	fmt.Printf("opsPerSecond = %10.2f\n", opsPerSecond)
   261  	fmt.Printf("latencyPerOp = %10.2f ms\n", latencyPerOpInMilliSeconds)
   262  }
   263  
   264  func fsWorkout(threadIndex uint64) {
   265  	var (
   266  		dirInodeName    string
   267  		dirInodeNumber  inode.InodeNumber
   268  		err             error
   269  		fileInodeName   []string
   270  		fileInodeNumber inode.InodeNumber
   271  		i               uint64
   272  	)
   273  
   274  	// Do initialization step
   275  	if perThreadDir {
   276  		dirInodeName = fmt.Sprintf("%s%016X", dirInodeNamePrefix, threadIndex)
   277  		if measureCreate {
   278  			dirInodeNumber, err = volumeHandle.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, dirInodeName, inode.PosixModePerm)
   279  			if nil != err {
   280  				stepErrChan <- err
   281  				runtime.Goexit()
   282  			}
   283  		} else { // measureStat || measureDestroy
   284  			dirInodeNumber, err = volumeHandle.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, dirInodeName)
   285  			if nil != err {
   286  				stepErrChan <- err
   287  				runtime.Goexit()
   288  			}
   289  		}
   290  	} else { // !perThreadDir
   291  		dirInodeNumber = inode.RootDirInodeNumber
   292  	}
   293  	fileInodeName = make([]string, inodesPerThread)
   294  	for i = 0; i < inodesPerThread; i++ {
   295  		fileInodeName[i] = fmt.Sprintf("%s%016X_%016X", fileInodeNamePrefix, threadIndex, i)
   296  	}
   297  
   298  	// Indicate initialization step is done
   299  	stepErrChan <- nil
   300  
   301  	// Await signal to proceed with measured operations step
   302  	_ = <-doNextStepChan
   303  
   304  	// Do measured operations
   305  	for i = 0; i < inodesPerThread; i++ {
   306  		if measureCreate {
   307  			fileInodeNumber, err = volumeHandle.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirInodeNumber, fileInodeName[i], inode.PosixModePerm)
   308  			if nil != err {
   309  				stepErrChan <- err
   310  				runtime.Goexit()
   311  			}
   312  		} else if measureStat {
   313  			fileInodeNumber, err = volumeHandle.Lookup(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirInodeNumber, fileInodeName[i])
   314  			if nil != err {
   315  				stepErrChan <- err
   316  				runtime.Goexit()
   317  			}
   318  			_, err = volumeHandle.Getstat(inode.InodeRootUserID, inode.InodeGroupID(0), nil, fileInodeNumber)
   319  			if nil != err {
   320  				stepErrChan <- err
   321  				runtime.Goexit()
   322  			}
   323  		} else { // measureDestroy
   324  			err = volumeHandle.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, dirInodeNumber, fileInodeName[i])
   325  			if nil != err {
   326  				stepErrChan <- err
   327  				runtime.Goexit()
   328  			}
   329  		}
   330  	}
   331  
   332  	// Indicate measured operations step is done
   333  	stepErrChan <- nil
   334  
   335  	// Await signal to proceed with shutdown step
   336  	_ = <-doNextStepChan
   337  
   338  	// Do shutdown step
   339  	if perThreadDir && measureDestroy {
   340  		err = volumeHandle.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, dirInodeName)
   341  		if nil != err {
   342  			stepErrChan <- err
   343  			runtime.Goexit()
   344  		}
   345  	}
   346  
   347  	// Indicate shutdown step is done
   348  	stepErrChan <- nil
   349  }