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  }