github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/cache/lru_striped_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package cache
     5  
     6  import (
     7  	"fmt"
     8  	"hash/maphash"
     9  	"testing"
    10  
    11  	"github.com/cespare/xxhash/v2"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/masterhung0112/hk_server/v5/model"
    16  )
    17  
    18  func makeLRUPredictibleTestData(num int) [][2]string {
    19  	kv := make([][2]string, num)
    20  	for i := 0; i < len(kv); i++ {
    21  		kv[i] = [2]string{
    22  			fmt.Sprintf("%d-key-%d", i, i),
    23  			fmt.Sprintf("%d-val-%d", i, i),
    24  		}
    25  	}
    26  	return kv
    27  }
    28  
    29  func TestNewLRUStriped(t *testing.T) {
    30  	scache, err := NewLRUStriped(LRUOptions{StripedBuckets: 3, Size: 20})
    31  	require.NoError(t, err)
    32  
    33  	cache := scache.(LRUStriped)
    34  
    35  	require.Len(t, cache.buckets, 3)
    36  	assert.Equal(t, 8, cache.buckets[0].size)
    37  	assert.Equal(t, 8, cache.buckets[1].size)
    38  	assert.Equal(t, 8, cache.buckets[2].size)
    39  }
    40  
    41  func TestLRUStripedKeyDistribution(t *testing.T) {
    42  	dataset := makeLRUPredictibleTestData(100)
    43  
    44  	scache, err := NewLRUStriped(LRUOptions{StripedBuckets: 4, Size: len(dataset)})
    45  	require.NoError(t, err)
    46  	cache := scache.(LRUStriped)
    47  	for _, kv := range dataset {
    48  		require.NoError(t, cache.Set(kv[0], kv[1]))
    49  		var out string
    50  		require.NoError(t, cache.Get(kv[0], &out))
    51  		require.Equal(t, kv[1], out)
    52  	}
    53  
    54  	require.Len(t, cache.buckets, 4)
    55  	acc := 0
    56  	for i := 0; i < 4; i++ {
    57  		clen, err := cache.buckets[i].Len()
    58  		acc += clen
    59  		assert.NoError(t, err)
    60  		assert.GreaterOrEqual(t, clen, len(dataset)/2/4, "at least 50%/nbuckets of all keys in each bucket")
    61  	}
    62  	// because of the limited size of each bucket and the nature of our data,
    63  	// we may have around 10% of our keys evicted in this scenario. removing 1% because we cannot predict
    64  	// accurately what is happening with random data.
    65  	assert.GreaterOrEqual(t, acc, len(dataset)-(len(dataset)*1.0/100.0))
    66  }
    67  
    68  func TestLRUStriped_Size(t *testing.T) {
    69  	scache, err := NewLRUStriped(LRUOptions{StripedBuckets: 2, Size: 128})
    70  	require.NoError(t, err)
    71  	cache := scache.(LRUStriped)
    72  	acc := 0
    73  	for _, bucket := range cache.buckets {
    74  		acc += bucket.size
    75  	}
    76  	assert.Equal(t, 128+13+1, acc) // +10% +modulo padding
    77  }
    78  
    79  func TestLRUStriped_HashKey(t *testing.T) {
    80  	scache, err := NewLRUStriped(LRUOptions{StripedBuckets: 2, Size: 128})
    81  	require.NoError(t, err)
    82  	cache := scache.(LRUStriped)
    83  	first := cache.hashkeyMapHash("key")
    84  	cache.hashkeyMapHash("other_key_to_ensure_that_result_it’s_not_dependent_on_previous_input")
    85  	second := cache.hashkeyMapHash("key")
    86  	require.Equal(t, first, second)
    87  }
    88  
    89  func TestLRUStriped_Get(t *testing.T) {
    90  	cache, err := NewLRUStriped(LRUOptions{StripedBuckets: 4, Size: 128})
    91  	require.NoError(t, err)
    92  	var out string
    93  	require.Equal(t, ErrKeyNotFound, cache.Get("key", &out))
    94  	require.Zero(t, out)
    95  
    96  	require.NoError(t, cache.Set("key", "value"))
    97  	require.NoError(t, cache.Get("key", &out))
    98  	require.Equal(t, "value", out)
    99  }
   100  
   101  var hashSink uint64
   102  
   103  func BenchmarkSum64(b *testing.B) {
   104  	cases := []string{
   105  		"1",
   106  		"22",
   107  		"333",
   108  		model.NewId(),
   109  		model.NewId() + model.NewId(),
   110  	}
   111  
   112  	for _, case_ := range cases {
   113  		b.Run(fmt.Sprintf("maphash_string_len_%d", len(case_)), func(b *testing.B) {
   114  			seed := maphash.MakeSeed()
   115  			b.ResetTimer()
   116  			for i := 0; i < b.N; i++ {
   117  				var h maphash.Hash
   118  				h.SetSeed(seed)
   119  				h.WriteString(case_) // documentation and code says it never fails
   120  				hashSink = h.Sum64()
   121  			}
   122  		})
   123  		b.Run(fmt.Sprintf("xxhash_string_len_%d", len(case_)), func(b *testing.B) {
   124  			for i := 0; i < b.N; i++ {
   125  				hashSink = xxhash.Sum64String(case_)
   126  			}
   127  		})
   128  	}
   129  }