github.com/swiftstack/proxyfs@v0.0.0-20201223034610-5434d919416e/fs/metadata_stress_test.go (about) 1 package fs 2 3 import ( 4 "strconv" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/swiftstack/ProxyFS/inode" 10 "github.com/swiftstack/ProxyFS/utils" 11 ) 12 13 // 14 // Code related to multiple test threads 15 // 16 17 const testDirName = "MetaDataStressTestDir" 18 19 var testDirInodeNumber inode.InodeNumber 20 21 func testSetupForStress(t *testing.T, starvationMode bool) { 22 var err error 23 testSetup(t, starvationMode) 24 testDirInodeNumber, err = testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inode.RootDirInodeNumber, testDirName, inode.PosixModePerm) 25 if nil != err { 26 t.Fatalf("Failed to create %s: %v", testDirName, err) 27 } 28 } 29 30 // 31 // Metadata stress tests 32 // 33 34 func TestStressMetaDataOpsWhileStarved(t *testing.T) { 35 testStressMetaDataOps(t, true) 36 } 37 38 func TestStressMetaDataOpsWhileNotStarved(t *testing.T) { 39 testStressMetaDataOps(t, false) 40 } 41 42 func testStressMetaDataOps(t *testing.T, starvationMode bool) { 43 if testing.Short() { 44 t.Skip("skipping stress test.") 45 } 46 47 globalSyncPt = make(chan testRequest) 48 49 testSetupForStress(t, starvationMode) 50 testTwoThreadsCreateUnlink(t) 51 testTeardown(t) 52 testSetupForStress(t, starvationMode) 53 testTwoThreadsCreateCreate(t) 54 testTeardown(t) 55 testSetupForStress(t, starvationMode) 56 testTeardown(t) 57 testSetupForStress(t, starvationMode) 58 testMultiThreadCreateAndLookup(t) 59 testTeardown(t) 60 testSetupForStress(t, starvationMode) 61 testMultiThreadCreateAndReaddir(t) 62 testTeardown(t) 63 testSetupForStress(t, starvationMode) 64 testCreateReWriteNoFlush(t) 65 testTeardown(t) 66 testSetupForStress(t, starvationMode) 67 testCreateSeqWriteNoFlush(t) 68 testTeardown(t) 69 } 70 71 type testOpTyp int 72 73 const ( 74 nilTestOp testOpTyp = iota 75 createTestOp 76 createLoopTestOp 77 lookupPathLoopTestOp 78 mkdirTestOp 79 readdirLoopTestOp 80 getContainerLoopTestOp 81 rmdirTestOp 82 stopThreadTestOp 83 unlinkTestOp 84 unlinkLoopTestOp 85 reWriteNoFlushLoopTestOp 86 seqWriteNoFlushLoopTestOp 87 ) 88 89 type testRequest struct { 90 opType testOpTyp // Operation type 91 name1 string 92 loopCount int // Number of times to do operation. 0 means enforce min/max 93 minimumLoopCount int // Minimum number of times to do infinite operation. 94 maximumLoopCount int // Maximum number of times to do infinite operation. 95 loopDelay time.Duration 96 inodeNumber inode.InodeNumber 97 bufPtr *[]byte 98 offset uint64 99 length uint64 100 t *testing.T 101 } 102 103 type testResponse struct { 104 err error 105 inodeNumber inode.InodeNumber 106 } 107 108 // Per thread structure storing channel information 109 type threadInfo struct { 110 sync.Mutex 111 startedNode chan bool 112 requestForThread chan *testRequest 113 operationStatus chan *testResponse 114 endLoop bool // Flag used to signal an infinite loop test to stop 115 } 116 117 var globalSyncPt chan testRequest // Channel used to synchronize test threads to simulate multiple threads 118 119 // Map of threads and channels used for communication 120 var threadMap map[int]*threadInfo 121 122 // Setup thread stuctures based on number of threads test wants 123 func setupThreadMap(threadCount int) { 124 threadMap = make(map[int]*threadInfo) 125 126 for i := 0; i < threadCount; i++ { 127 thread := &threadInfo{startedNode: make(chan bool), requestForThread: make(chan *testRequest), operationStatus: make(chan *testResponse)} 128 threadMap[i] = thread 129 } 130 } 131 132 func setupThreads(threadCount int) { 133 setupThreadMap(threadCount) 134 135 // Start threads and wait for them 136 for i := range threadMap { 137 go threadNode(i) 138 _ = <-threadMap[i].startedNode 139 } 140 } 141 142 func stopThreads(t *testing.T) { 143 for i := range threadMap { 144 145 // Tell thread to exit 146 request := &testRequest{opType: stopThreadTestOp, t: t, inodeNumber: testDirInodeNumber} 147 sendRequestToThread(i, t, request) 148 } 149 } 150 151 func loopOp(fileRequest *testRequest, threadID int, inodeNumber inode.InodeNumber) (err error) { 152 var ( 153 areMoreEntries bool 154 containerEnts []ContainerEntry 155 dirEnts []inode.DirEntry 156 fName string 157 infiniteLoopCount int // Useful for debugging 158 lastBasename string 159 localLoopCount int 160 loopCount int 161 loopDelay time.Duration 162 maxEntries uint64 163 minimumLoopCount int 164 maximumLoopCount int 165 more bool 166 name1 string 167 numEntries uint64 168 offset uint64 169 totalEntriesRead uint64 170 ) 171 172 name1 = fileRequest.name1 173 loopCount = fileRequest.loopCount 174 minimumLoopCount = fileRequest.minimumLoopCount 175 maximumLoopCount = fileRequest.maximumLoopCount 176 loopDelay = fileRequest.loopDelay 177 178 // Loop doing operation loopCount times. If it is an infinite loop we loop until signaled to stop. 179 // 180 // minimumLoopCount is used with infiniteLoop to make sure the loop executes at least minimumLoopCount times 181 // before returning. 182 for { 183 fName = name1 + "-" + strconv.Itoa(localLoopCount) 184 switch fileRequest.opType { 185 case createLoopTestOp: 186 _, err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, fName, inode.PosixModePerm) 187 case lookupPathLoopTestOp: 188 _, err = testVolumeStruct.LookupPath(inode.InodeRootUserID, inode.InodeGroupID(0), nil, fName) 189 case readdirLoopTestOp: 190 areMoreEntries = true 191 lastBasename = "" 192 maxEntries = 10 193 totalEntriesRead = 0 // Useful for debugging 194 for areMoreEntries { 195 dirEnts, numEntries, more, err = testVolumeStruct.Readdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, maxEntries, lastBasename) 196 if nil != err { 197 return 198 } 199 lastBasename = dirEnts[len(dirEnts)-1].Basename 200 areMoreEntries = more 201 totalEntriesRead += numEntries 202 } 203 case getContainerLoopTestOp: 204 areMoreEntries = true 205 lastBasename = "" 206 maxEntries = 10 207 totalEntriesRead = 0 // Useful for debugging 208 for areMoreEntries { 209 containerEnts, err = testVolumeStruct.MiddlewareGetContainer(testDirName, maxEntries, lastBasename, "", "", "") 210 if nil != err { 211 return 212 } 213 if 0 == len(containerEnts) { 214 areMoreEntries = false 215 } else { 216 lastBasename = containerEnts[len(containerEnts)-1].Basename 217 totalEntriesRead += uint64(len(containerEnts)) 218 } 219 } 220 case unlinkLoopTestOp: 221 err = testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, fName) 222 case reWriteNoFlushLoopTestOp: 223 _, _ = testVolumeStruct.Write(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, fileRequest.offset, *fileRequest.bufPtr, nil) 224 case seqWriteNoFlushLoopTestOp: 225 offset = fileRequest.length * uint64(localLoopCount) 226 _, _ = testVolumeStruct.Write(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, offset, *fileRequest.bufPtr, nil) 227 } 228 localLoopCount++ 229 infiniteLoopCount++ 230 231 // The infinite loop case breaks when control thread signals this thread to stop 232 // and we have at least hit our minimumLoopCount. 233 if 0 == loopCount { 234 if 0 == maximumLoopCount { 235 if localLoopCount >= minimumLoopCount { 236 threadMap[threadID].Lock() 237 if threadMap[threadID].endLoop == true { 238 threadMap[threadID].Unlock() 239 break 240 } 241 threadMap[threadID].Unlock() 242 } 243 } else { 244 if localLoopCount == maximumLoopCount { 245 break 246 } else { 247 if localLoopCount >= minimumLoopCount { 248 threadMap[threadID].Lock() 249 if threadMap[threadID].endLoop == true { 250 threadMap[threadID].Unlock() 251 break 252 } 253 threadMap[threadID].Unlock() 254 } 255 } 256 } 257 } else { 258 if localLoopCount == loopCount { 259 break 260 } 261 } 262 263 time.Sleep(loopDelay) 264 } 265 266 return 267 } 268 269 // Test thread. Just waits on channel and does operation requested. 270 func threadNode(threadID int) { 271 272 // Tell control thread we are up and set channel to read. 273 threadMap[threadID].startedNode <- true 274 var request chan *testRequest 275 request = threadMap[threadID].requestForThread 276 277 // Wait for an operation 278 for { 279 fileRequest := <-request 280 name1 := fileRequest.name1 281 inodeNumber := fileRequest.inodeNumber 282 283 switch fileRequest.opType { 284 case stopThreadTestOp: 285 return 286 287 case createTestOp: 288 response := &testResponse{} 289 response.inodeNumber, response.err = testVolumeStruct.Create(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, 290 name1, inode.PosixModePerm) 291 threadMap[threadID].operationStatus <- response 292 293 case createLoopTestOp: 294 // Loop creating files loopCount times. 295 err := loopOp(fileRequest, threadID, inodeNumber) 296 response := &testResponse{err: err} 297 threadMap[threadID].operationStatus <- response 298 299 case lookupPathLoopTestOp: 300 // Loop doing LookupPath of files loopCount times. 301 err := loopOp(fileRequest, threadID, inodeNumber) 302 response := &testResponse{err: err} 303 threadMap[threadID].operationStatus <- response 304 305 case mkdirTestOp: 306 newInodeNumber, err := testVolumeStruct.Mkdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, name1, inode.PosixModePerm) 307 response := &testResponse{err: err, inodeNumber: newInodeNumber} 308 threadMap[threadID].operationStatus <- response 309 310 case readdirLoopTestOp: 311 // Loop doing readdir of files loopCount times. 312 err := loopOp(fileRequest, threadID, inodeNumber) 313 response := &testResponse{err: err} 314 threadMap[threadID].operationStatus <- response 315 316 case getContainerLoopTestOp: 317 // Loop doing MiddlewareGetContainer of objects loopCount times. 318 err := loopOp(fileRequest, threadID, inodeNumber) 319 response := &testResponse{err: err} 320 threadMap[threadID].operationStatus <- response 321 322 case rmdirTestOp: 323 err := testVolumeStruct.Rmdir(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, name1) 324 response := &testResponse{err: err} 325 threadMap[threadID].operationStatus <- response 326 327 case unlinkTestOp: 328 err := testVolumeStruct.Unlink(inode.InodeRootUserID, inode.InodeGroupID(0), nil, inodeNumber, name1) 329 response := &testResponse{err: err} 330 threadMap[threadID].operationStatus <- response 331 332 case unlinkLoopTestOp: 333 // Loop unlinking files loopCount times. 334 err := loopOp(fileRequest, threadID, inodeNumber) 335 response := &testResponse{err: err} 336 threadMap[threadID].operationStatus <- response 337 338 case reWriteNoFlushLoopTestOp: 339 // Loop writing and rewriting a file loopCount times. 340 err := loopOp(fileRequest, threadID, inodeNumber) 341 response := &testResponse{err: err} 342 threadMap[threadID].operationStatus <- response 343 344 case seqWriteNoFlushLoopTestOp: 345 // Loop writing and rewriting a file loopCount times. 346 err := loopOp(fileRequest, threadID, inodeNumber) 347 response := &testResponse{err: err} 348 threadMap[threadID].operationStatus <- response 349 } 350 } 351 } 352 353 // Set flag telling thread doing infinite loop to exit. 354 func setEndLoopFlag(threadID int) { 355 threadMap[threadID].Lock() 356 threadMap[threadID].endLoop = true 357 threadMap[threadID].Unlock() 358 } 359 360 func sendRequestToThread(threadID int, t *testing.T, request *testRequest) { 361 // Clear endLoop flag before sending request 362 threadMap[threadID].Lock() 363 threadMap[threadID].endLoop = false 364 threadMap[threadID].Unlock() 365 366 threadMap[threadID].requestForThread <- request 367 368 // We do not wait until the operation completes before returning. 369 } 370 371 // Test that two threads can grab a lock *exclusive* and the second thread 372 // only gets lock after first one has done Unlock(). 373 func testTwoThreadsCreateUnlink(t *testing.T) { 374 var numThreads = 2 375 376 // Initialize worker threads 377 setupThreads(numThreads) 378 379 // Tell thread 0 to loop creating files of the pattern "testfile*" 380 request := &testRequest{opType: createLoopTestOp, t: t, name1: "testfile", 381 inodeNumber: testDirInodeNumber} 382 sendRequestToThread(0, t, request) 383 384 // Create the file from thread 1 385 request = &testRequest{opType: createTestOp, t: t, name1: "TestNormalFile", 386 inodeNumber: testDirInodeNumber} 387 sendRequestToThread(1, t, request) 388 _ = <-threadMap[1].operationStatus 389 390 // Unlink the file from thread 1 391 request = &testRequest{opType: unlinkTestOp, t: t, name1: "TestNormalFile", 392 inodeNumber: testDirInodeNumber} 393 sendRequestToThread(1, t, request) 394 _ = <-threadMap[1].operationStatus 395 396 // Tell thread 0 to stop creating files 397 setEndLoopFlag(0) 398 _ = <-threadMap[0].operationStatus 399 400 // Stop worker threads 401 stopThreads(t) 402 } 403 404 // Test that two threads can grab a lock *exclusive* and the second thread 405 // only gets lock after first one has done Unlock(). 406 func testTwoThreadsCreateCreate(t *testing.T) { 407 var numThreads = 2 408 409 // Initialize worker threads 410 setupThreads(numThreads) 411 412 for i := 0; i < numThreads; i++ { 413 // Tell thread 0 to loop creating files of the pattern "testfile*" 414 request := &testRequest{opType: createLoopTestOp, t: t, name1: "testfile-" + strconv.Itoa(i), 415 inodeNumber: testDirInodeNumber} 416 sendRequestToThread(i, t, request) 417 } 418 419 time.Sleep(100 * time.Millisecond) 420 421 // Tell threads to stop creating files 422 for i := 0; i < numThreads; i++ { 423 setEndLoopFlag(i) 424 _ = <-threadMap[i].operationStatus 425 } 426 427 // Stop worker threads 428 stopThreads(t) 429 } 430 431 // Test that two threads can grab a lock *exclusive* and the second thread 432 // only gets lock after first one has done Unlock(). 433 func testMultiThreadCreate(t *testing.T) { 434 var numThreads = 3 435 nameOfTest := utils.GetFnName() 436 437 // Initialize worker threads 438 setupThreads(numThreads) 439 440 // Unlink existing files 441 for i := 0; i < numThreads; i++ { 442 request := &testRequest{opType: unlinkLoopTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 443 inodeNumber: testDirInodeNumber, loopCount: 5} 444 sendRequestToThread(i, t, request) 445 } 446 // Wait for unlinkLoopTestOp to complete 447 for i := 0; i < numThreads; i++ { 448 _ = <-threadMap[i].operationStatus 449 } 450 451 // Create files 452 for i := 0; i < numThreads; i++ { 453 request := &testRequest{opType: createLoopTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 454 inodeNumber: testDirInodeNumber, loopCount: 5} 455 sendRequestToThread(i, t, request) 456 } 457 // Wait for createLoopTestOp to complete 458 for i := 0; i < numThreads; i++ { 459 _ = <-threadMap[i].operationStatus 460 } 461 462 // Now unlink the files 463 for i := 0; i < numThreads; i++ { 464 request := &testRequest{opType: unlinkLoopTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 465 inodeNumber: testDirInodeNumber, loopCount: 5} 466 sendRequestToThread(i, t, request) 467 } 468 // Wait for unlinkLoopTestOp to complete 469 for i := 0; i < numThreads; i++ { 470 _ = <-threadMap[i].operationStatus 471 } 472 473 // Stop worker threads 474 stopThreads(t) 475 } 476 477 // Test one thread doing Create() in loop and two threads 478 // doing Lookup() 479 func testMultiThreadCreateAndLookup(t *testing.T) { 480 var numThreads = 3 481 nameOfTest := utils.GetFnName() 482 483 // Initialize worker threads 484 setupThreads(numThreads) 485 486 // Create a subdirectory to use 487 request1 := &testRequest{opType: mkdirTestOp, t: t, name1: nameOfTest + "-subdir", 488 inodeNumber: testDirInodeNumber} 489 sendRequestToThread(0, t, request1) 490 mkdirResponse := <-threadMap[0].operationStatus 491 492 // Tell thread 0 to loop creating files of the pattern nameOfTest 493 request2 := &testRequest{opType: createLoopTestOp, t: t, name1: nameOfTest, 494 inodeNumber: mkdirResponse.inodeNumber, loopCount: 10} 495 sendRequestToThread(0, t, request2) 496 497 // Tell thread 1 to loop doing 35 Lookups 498 request3 := &testRequest{opType: lookupPathLoopTestOp, t: t, name1: nameOfTest, 499 inodeNumber: mkdirResponse.inodeNumber, loopCount: 35} 500 sendRequestToThread(1, t, request3) 501 502 // Tell thread 2 to loop doing 35 Lookups 503 request4 := &testRequest{opType: lookupPathLoopTestOp, t: t, name1: nameOfTest, 504 inodeNumber: mkdirResponse.inodeNumber, loopCount: 35} 505 sendRequestToThread(2, t, request4) 506 507 // Wait for threads to complete 508 for i := 0; i < numThreads; i++ { 509 _ = <-threadMap[i].operationStatus 510 } 511 512 // Tell thread 0 to loop unlinking test files created during test 513 // and wait for it to complete 514 request5 := &testRequest{opType: unlinkLoopTestOp, t: t, name1: nameOfTest, 515 inodeNumber: mkdirResponse.inodeNumber, loopCount: 10} 516 sendRequestToThread(0, t, request5) 517 _ = <-threadMap[0].operationStatus 518 519 // Remove subdirectory 520 request6 := &testRequest{opType: rmdirTestOp, t: t, name1: nameOfTest + "-subdir", 521 inodeNumber: testDirInodeNumber} 522 sendRequestToThread(0, t, request6) 523 _ = <-threadMap[0].operationStatus 524 525 // Stop worker threads 526 stopThreads(t) 527 } 528 529 // Test one thread doing Create() in loop and nine other threads doing Readdir 530 func testMultiThreadCreateAndReaddir(t *testing.T) { 531 var numThreads = 10 532 nameOfTest := utils.GetFnName() 533 534 // Initialize worker threads 535 setupThreads(numThreads) 536 537 // Create a subdirectory to use 538 request1 := &testRequest{opType: mkdirTestOp, t: t, name1: nameOfTest + "-subdir", 539 inodeNumber: testDirInodeNumber} 540 sendRequestToThread(0, t, request1) 541 mkdirResponse := <-threadMap[0].operationStatus 542 543 // Tell thread 0 to loop creating files of the pattern nameOfTest in the subdirectory. 544 // Create a minimum of at least 200 before stopping. 545 request2 := &testRequest{opType: createLoopTestOp, t: t, name1: nameOfTest, 546 inodeNumber: mkdirResponse.inodeNumber, minimumLoopCount: 200, maximumLoopCount: 400, loopDelay: 5 * time.Microsecond} 547 sendRequestToThread(0, t, request2) 548 549 // Pause a few milliseconds between operations 550 time.Sleep(10 * time.Millisecond) 551 552 // Tell threads 1 to numThreads to loop doing 35 readdirs 553 for i := 1; i < numThreads; i++ { 554 request3 := &testRequest{opType: readdirLoopTestOp, t: t, name1: nameOfTest, 555 inodeNumber: mkdirResponse.inodeNumber, loopCount: 35} 556 sendRequestToThread(i, t, request3) 557 } 558 559 // Wait until threads 1 to numThreads complete 560 for i := 1; i < numThreads; i++ { 561 _ = <-threadMap[i].operationStatus 562 } 563 564 // Tell threads 1 to numThreads to loop doing 15 getContainers 565 for i := 1; i < numThreads; i++ { 566 request3 := &testRequest{opType: getContainerLoopTestOp, t: t, name1: nameOfTest, 567 inodeNumber: mkdirResponse.inodeNumber, loopCount: 15} 568 sendRequestToThread(i, t, request3) 569 } 570 571 // Wait until threads 1 to numThreads complete 572 for i := 1; i < numThreads; i++ { 573 _ = <-threadMap[i].operationStatus 574 } 575 576 // Tell thread 0 to stop doing Creates in an infinite loop 577 setEndLoopFlag(0) 578 579 // Wait for thread 0 to complete 580 _ = <-threadMap[0].operationStatus 581 582 // Now tell thread 1 to do one more readdirLoopTestOp to make sure we can read 200 entries 583 request4 := &testRequest{opType: readdirLoopTestOp, t: t, name1: nameOfTest, 584 inodeNumber: mkdirResponse.inodeNumber, loopCount: 1} 585 sendRequestToThread(1, t, request4) 586 _ = <-threadMap[1].operationStatus 587 588 // Now tell thread 1 to do one more getContainerLoopTestOp to make sure we can read 200 entries 589 request5 := &testRequest{opType: getContainerLoopTestOp, t: t, name1: nameOfTest, 590 inodeNumber: mkdirResponse.inodeNumber, loopCount: 1} 591 sendRequestToThread(1, t, request5) 592 _ = <-threadMap[1].operationStatus 593 594 // Stop worker threads 595 stopThreads(t) 596 } 597 598 // Test numThreads doing create(), loop doing rewrites() of same offset and location and no flush 599 func testCreateReWriteNoFlush(t *testing.T) { 600 // NOTE: This test uses a lot of memory and will cause a OOM. Be careful 601 // increasing numThreads, size of write buffer and number of overwrites. 602 var numThreads = 50 603 604 fileInodes := make([]inode.InodeNumber, numThreads) // Map to store each inode created 605 nameOfTest := utils.GetFnName() 606 607 // Initialize worker threads 608 setupThreads(numThreads) 609 610 // Create a subdirectory to use 611 request4 := &testRequest{opType: mkdirTestOp, t: t, name1: nameOfTest + "-subdir", 612 inodeNumber: testDirInodeNumber} 613 sendRequestToThread(0, t, request4) 614 mkdirResponse := <-threadMap[0].operationStatus 615 616 // Create files used for writes 617 for i := 0; i < numThreads; i++ { 618 request5 := &testRequest{opType: createTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 619 inodeNumber: mkdirResponse.inodeNumber, loopCount: 5} 620 sendRequestToThread(i, t, request5) 621 } 622 // Wait for createTestOp to complete and store inode number created 623 for i := 0; i < numThreads; i++ { 624 response := <-threadMap[i].operationStatus 625 fileInodes[i] = response.inodeNumber 626 } 627 628 var bufLen uint64 = 11 * 1024 * 1024 629 bufToWrite := make([]byte, bufLen, bufLen) 630 631 // Write to files without doing a flush. We write 11MB starting from offset 0. 632 // We rewrite the same location numOverWrites times. 633 numOverWrites := 1 634 minNumberOfLoops := 1 635 writeOffset := uint64(0) 636 for i := 0; i < numThreads; i++ { 637 request6 := &testRequest{opType: reWriteNoFlushLoopTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 638 inodeNumber: fileInodes[i], loopCount: numOverWrites, minimumLoopCount: minNumberOfLoops, 639 offset: writeOffset, length: bufLen, bufPtr: &bufToWrite} 640 sendRequestToThread(i, t, request6) 641 } 642 643 // Wait until threads complete 644 for i := 0; i < numThreads; i++ { 645 _ = <-threadMap[i].operationStatus 646 } 647 648 // Stop worker threads 649 stopThreads(t) 650 } 651 652 // Test numThreads doing create, loop doing sequential 1MB writes and no flush. 653 // NOTE: The writes are not ordered - they may not actually happen sequentially. 654 func testCreateSeqWriteNoFlush(t *testing.T) { 655 // NOTE: This test uses a lot of memory and will cause a OOM. Be careful 656 // increasing numThreads, size of write buffer and number of overwrites. 657 var numThreads = 25 658 659 fileInodes := make([]inode.InodeNumber, numThreads) // Map to store each inode created 660 nameOfTest := utils.GetFnName() 661 662 // Initialize worker threads 663 setupThreads(numThreads) 664 665 // Create a subdirectory to use 666 request4 := &testRequest{opType: mkdirTestOp, t: t, name1: nameOfTest + "-subdir", 667 inodeNumber: testDirInodeNumber} 668 sendRequestToThread(0, t, request4) 669 mkdirResponse := <-threadMap[0].operationStatus 670 671 // Create files used for writes 672 for i := 0; i < numThreads; i++ { 673 request5 := &testRequest{opType: createTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 674 inodeNumber: mkdirResponse.inodeNumber, loopCount: 11} 675 sendRequestToThread(i, t, request5) 676 } 677 // Wait for createTestOp to complete and store inode number created 678 for i := 0; i < numThreads; i++ { 679 response := <-threadMap[i].operationStatus 680 fileInodes[i] = response.inodeNumber 681 } 682 683 // Each write will be 1MB 684 var bufLen uint64 = 1 * 1024 * 1024 685 bufToWrite := make([]byte, bufLen, bufLen) 686 687 // Write to files without doing a flush. We issue 1MB writes sequentially 688 // although they can finish in any order. 689 numOfWrites := 11 690 minNumberOfLoops := 11 691 writeOffset := uint64(0) 692 for i := 0; i < numThreads; i++ { 693 request6 := &testRequest{opType: seqWriteNoFlushLoopTestOp, t: t, name1: nameOfTest + "-" + strconv.Itoa(i), 694 inodeNumber: fileInodes[i], loopCount: numOfWrites, minimumLoopCount: minNumberOfLoops, 695 offset: writeOffset, length: bufLen, bufPtr: &bufToWrite} 696 sendRequestToThread(i, t, request6) 697 } 698 699 // Wait until threads complete 700 for i := 0; i < numThreads; i++ { 701 _ = <-threadMap[i].operationStatus 702 } 703 704 // Stop worker threads 705 stopThreads(t) 706 }