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