github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/nbs/fd_cache_test.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2017 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package nbs
    23  
    24  import (
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"sort"
    30  	"sync"
    31  	"testing"
    32  
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  func TestFDCache(t *testing.T) {
    38  	dir := makeTempDir(t)
    39  	defer os.RemoveAll(dir)
    40  
    41  	paths := [3]string{}
    42  	for i := range paths {
    43  		name := fmt.Sprintf("file%d", i)
    44  		paths[i] = filepath.Join(dir, name)
    45  		err := ioutil.WriteFile(paths[i], []byte(name), 0644)
    46  		require.NoError(t, err)
    47  	}
    48  
    49  	refNoError := func(fc *fdCache, p string, assert *assert.Assertions) *os.File {
    50  		f, err := fc.RefFile(p)
    51  		require.NoError(t, err)
    52  		assert.NotNil(f)
    53  		return f
    54  	}
    55  
    56  	t.Run("ConcurrentOpen", func(t *testing.T) {
    57  		assert := assert.New(t)
    58  		concurrency := 3
    59  		fc := newFDCache(3)
    60  		defer fc.Drop()
    61  
    62  		trigger := make(chan struct{})
    63  		wg := sync.WaitGroup{}
    64  		for i := 0; i < concurrency; i++ {
    65  			wg.Add(1)
    66  			go func() {
    67  				defer wg.Done()
    68  				<-trigger
    69  				fc.RefFile(paths[0])
    70  			}()
    71  		}
    72  		close(trigger)
    73  		wg.Wait()
    74  
    75  		present := fc.reportEntries()
    76  		if assert.Len(present, 1) {
    77  			ce := fc.cache[present[0]]
    78  			assert.EqualValues(concurrency, ce.refCount)
    79  		}
    80  	})
    81  
    82  	t.Run("NoEvictions", func(t *testing.T) {
    83  		assert := assert.New(t)
    84  		fc := newFDCache(2)
    85  		defer fc.Drop()
    86  		f := refNoError(fc, paths[0], assert)
    87  
    88  		f2 := refNoError(fc, paths[1], assert)
    89  		assert.NotEqual(f, f2)
    90  
    91  		dup := refNoError(fc, paths[0], assert)
    92  		assert.Equal(f, dup)
    93  	})
    94  
    95  	t.Run("Evictions", func(t *testing.T) {
    96  		assert := assert.New(t)
    97  		fc := newFDCache(1)
    98  		defer fc.Drop()
    99  
   100  		f0 := refNoError(fc, paths[0], assert)
   101  		f1 := refNoError(fc, paths[1], assert)
   102  		assert.NotEqual(f0, f1)
   103  
   104  		// f0 wasn't evicted, because that doesn't happen until UnrefFile()
   105  		dup := refNoError(fc, paths[0], assert)
   106  		assert.Equal(f0, dup)
   107  
   108  		expected := sort.StringSlice(paths[:2])
   109  		sort.Sort(expected)
   110  		assert.EqualValues(expected, fc.reportEntries())
   111  
   112  		// Unreffing f1 now should evict it
   113  		err := fc.UnrefFile(paths[1])
   114  		require.NoError(t, err)
   115  		assert.EqualValues(paths[:1], fc.reportEntries())
   116  
   117  		// Bring f1 back so we can test multiple evictions in a row
   118  		f1 = refNoError(fc, paths[1], assert)
   119  		assert.NotEqual(f0, f1)
   120  
   121  		// After adding f3, we should be able to evict both f0 and f1
   122  		f2 := refNoError(fc, paths[2], assert)
   123  		assert.NotEqual(f0, f2)
   124  		assert.NotEqual(f1, f2)
   125  
   126  		err = fc.UnrefFile(paths[0])
   127  		require.NoError(t, err)
   128  		err = fc.UnrefFile(paths[0])
   129  		require.NoError(t, err)
   130  		err = fc.UnrefFile(paths[1])
   131  		require.NoError(t, err)
   132  
   133  		assert.EqualValues(paths[2:], fc.reportEntries())
   134  	})
   135  }