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 }