github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/inode/dir_stress_test.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package inode 5 6 import ( 7 "fmt" 8 "sync" 9 "testing" 10 "time" 11 12 "github.com/swiftstack/ProxyFS/headhunter" 13 "github.com/swiftstack/ProxyFS/trackedlock" 14 ) 15 16 const ( 17 testDirStressBasenamePrefix = "__TestDirStress_" // To be appended with Nonce + "_" + ThreadIndex + "_" + LinkIndex (for this Thread) 18 19 testDirStressDelayBetweenLinkAndUnlink time.Duration = 1 * time.Microsecond 20 testDirStressDelayBetweenThreadStarts time.Duration = 1 * time.Microsecond 21 22 testDirStressNumIterations uint64 = 3 23 testDirStressNumLinksPerIteration uint64 = 3 24 testDirStressNumThreads uint64 = 30 25 26 testDirStressVolumeName = "TestVolume" 27 ) 28 29 type testDirStressGlobalsStruct struct { 30 trackedlock.Mutex // Used to emulate the exclusive lock used in package fs on RootDirInodeNumber 31 nonce uint64 32 waitGroup sync.WaitGroup 33 err []error 34 } 35 36 var testDirStressGlobals = &testDirStressGlobalsStruct{} 37 38 func TestDirStressWhileStarved(t *testing.T) { 39 testDirStress(t, true) 40 } 41 42 func TestDirStressWhileNotStarved(t *testing.T) { 43 testDirStress(t, false) 44 } 45 46 func testDirStress(t *testing.T, starvationMode bool) { 47 var ( 48 err error 49 headhunterVolumeHandle headhunter.VolumeHandle 50 threadIndex uint64 51 ) 52 53 testSetup(t, starvationMode) 54 55 headhunterVolumeHandle, err = headhunter.FetchVolumeHandle(testDirStressVolumeName) 56 if nil != err { 57 t.Fatalf("headhunter.FetchVolumeHandle(\"%s\") failed: %v", testDirStressVolumeName, err) 58 } 59 60 testDirStressGlobals.nonce = headhunterVolumeHandle.FetchNonce() 61 62 testDirStressGlobals.waitGroup.Add(int(testDirStressNumThreads)) 63 64 testDirStressGlobals.err = make([]error, testDirStressNumThreads) 65 66 for threadIndex = uint64(0); threadIndex < testDirStressNumThreads; threadIndex++ { 67 time.Sleep(testDirStressDelayBetweenThreadStarts) 68 69 go testDirStressThread(threadIndex) 70 } 71 72 testDirStressGlobals.waitGroup.Wait() 73 74 for _, err = range testDirStressGlobals.err { 75 if nil != err { 76 t.Fatal(err) 77 } 78 } 79 80 testTeardown(t) 81 } 82 83 func testDirStressThread(threadIndex uint64) { 84 var ( 85 basename string 86 err error 87 fileInodeNumber InodeNumber 88 iteration uint64 89 linkIndex uint64 90 toDestroyInodeNumber InodeNumber 91 volumeHandle VolumeHandle 92 ) 93 94 defer testDirStressGlobals.waitGroup.Done() 95 96 volumeHandle, err = FetchVolumeHandle(testDirStressVolumeName) 97 if nil != err { 98 testDirStressGlobals.Lock() 99 testDirStressGlobals.err[threadIndex] = fmt.Errorf("FetchVolumeHandle(\"%s\") failed: %v", testDirStressVolumeName, err) 100 testDirStressGlobals.Unlock() 101 return 102 } 103 104 for iteration = uint64(0); iteration < testDirStressNumIterations; iteration++ { 105 fileInodeNumber, err = volumeHandle.CreateFile(PosixModePerm, InodeUserID(0), InodeGroupID(0)) 106 if nil != err { 107 testDirStressGlobals.Lock() 108 testDirStressGlobals.err[threadIndex] = fmt.Errorf("volumeHandle.CreateFile(PosixModePerm, InodeUserID(0), InodeGroupID(0)) failed: %v", err) 109 testDirStressGlobals.Unlock() 110 return 111 } 112 113 for linkIndex = uint64(0); linkIndex < testDirStressNumLinksPerIteration; linkIndex++ { 114 basename = fmt.Sprintf("%s%016X_%016X_%016X", testDirStressBasenamePrefix, testDirStressGlobals.nonce, threadIndex, linkIndex) 115 116 testDirStressGlobals.Lock() 117 err = volumeHandle.Link(RootDirInodeNumber, basename, fileInodeNumber, false) 118 if nil != err { 119 testDirStressGlobals.err[threadIndex] = fmt.Errorf("volumeHandle.Link(RootDirInodeNumber, \"%s\", fileInodeNumber, false) failed: %v", basename, err) 120 testDirStressGlobals.Unlock() 121 return 122 } 123 testDirStressGlobals.Unlock() 124 125 time.Sleep(testDirStressDelayBetweenLinkAndUnlink) 126 127 testDirStressGlobals.Lock() 128 toDestroyInodeNumber, err = volumeHandle.Unlink(RootDirInodeNumber, basename, false) 129 if nil != err { 130 testDirStressGlobals.err[threadIndex] = fmt.Errorf("volumeHandle.Unlink(RootDirInodeNumber, \"%s\", false) failed: %v", basename, err) 131 testDirStressGlobals.Unlock() 132 return 133 } 134 // Note that the originally created fileInode is not in any Directory, so unlinking the only dirEntry to it drops LinkCount to zero 135 if fileInodeNumber != toDestroyInodeNumber { 136 testDirStressGlobals.err[threadIndex] = fmt.Errorf("volumeHandle.Unlink(RootDirInodeNumber, \"%s\", false) should have returned toDestroyInodeNumber == fileInodeNumber", basename) 137 testDirStressGlobals.Unlock() 138 return 139 } 140 testDirStressGlobals.Unlock() 141 } 142 143 err = volumeHandle.Destroy(fileInodeNumber) 144 if nil != err { 145 testDirStressGlobals.Lock() 146 testDirStressGlobals.err[threadIndex] = fmt.Errorf("volumeHandle.Destroy(fileInodeNumber) failed: %v", err) 147 testDirStressGlobals.Unlock() 148 return 149 } 150 } 151 152 testDirStressGlobals.Lock() 153 testDirStressGlobals.err[threadIndex] = nil 154 testDirStressGlobals.Unlock() 155 }