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  }