github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/cache/cache_test.go (about)

     1  // Copyright 2014 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  //
    11  // This code is based on: https://github.com/golang/groupcache/
    12  
    13  package cache
    14  
    15  import (
    16  	"bytes"
    17  	"reflect"
    18  	"testing"
    19  
    20  	"github.com/biogo/store/llrb"
    21  	_ "github.com/cockroachdb/cockroach/pkg/util/log" // for flags
    22  )
    23  
    24  type testKey string
    25  
    26  // Compare implements llrb.Comparable.
    27  func (tk testKey) Compare(b llrb.Comparable) int {
    28  	return bytes.Compare([]byte(tk), []byte(b.(testKey)))
    29  }
    30  
    31  var getTests = []struct {
    32  	name       string
    33  	keyToAdd   testKey
    34  	keyToGet   testKey
    35  	expectedOk bool
    36  }{
    37  	{"string_hit", "myKey", "myKey", true},
    38  	{"string_miss", "myKey", "nonsense", false},
    39  }
    40  
    41  func noEviction(size int, key, value interface{}) bool {
    42  	return false
    43  }
    44  
    45  func evictTwoOrMore(size int, key, value interface{}) bool {
    46  	return size > 1
    47  }
    48  
    49  func evictThreeOrMore(size int, key, value interface{}) bool {
    50  	return size > 2
    51  }
    52  
    53  func TestCacheGet(t *testing.T) {
    54  	for _, tt := range getTests {
    55  		mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
    56  		mc.Add(tt.keyToAdd, 1234)
    57  		val, ok := mc.Get(tt.keyToGet)
    58  		if ok != tt.expectedOk {
    59  			t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok)
    60  		} else if ok && val != 1234 {
    61  			t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val)
    62  		}
    63  	}
    64  }
    65  
    66  func TestCacheClear(t *testing.T) {
    67  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
    68  	mc.Add(testKey("a"), 1)
    69  	mc.Add(testKey("b"), 2)
    70  	mc.Clear()
    71  	if _, ok := mc.Get(testKey("a")); ok {
    72  		t.Error("expected cache cleared")
    73  	}
    74  	if _, ok := mc.Get(testKey("b")); ok {
    75  		t.Error("expected cache cleared")
    76  	}
    77  	mc.Add(testKey("a"), 1)
    78  	if _, ok := mc.Get(testKey("a")); !ok {
    79  		t.Error("expected reinsert to succeed")
    80  	}
    81  }
    82  
    83  func TestCacheDel(t *testing.T) {
    84  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
    85  	mc.Add(testKey("myKey"), 1234)
    86  	if val, ok := mc.Get(testKey("myKey")); !ok {
    87  		t.Fatal("TestDel returned no match")
    88  	} else if val != 1234 {
    89  		t.Fatalf("TestDel failed. Expected %d, got %v", 1234, val)
    90  	}
    91  
    92  	mc.Del(testKey("myKey"))
    93  	if _, ok := mc.Get(testKey("myKey")); ok {
    94  		t.Fatal("TestRemove returned a removed entry")
    95  	}
    96  }
    97  
    98  func TestCacheAddDelEntry(t *testing.T) {
    99  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   100  	e := &Entry{Key: testKey("myKey"), Value: 1234}
   101  	mc.AddEntry(e)
   102  	if val, ok := mc.Get(testKey("myKey")); !ok {
   103  		t.Fatal("TestDel returned no match")
   104  	} else if val != 1234 {
   105  		t.Fatalf("TestDel failed. Expected %d, got %v", 1234, val)
   106  	}
   107  
   108  	mc.DelEntry(e)
   109  	if _, ok := mc.Get(testKey("myKey")); ok {
   110  		t.Fatal("TestRemove returned a removed entry")
   111  	}
   112  }
   113  
   114  func TestCacheEviction(t *testing.T) {
   115  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: evictTwoOrMore})
   116  	// Insert two keys into cache which only holds 1.
   117  	mc.Add(testKey("a"), 1234)
   118  	val, ok := mc.Get(testKey("a"))
   119  	if !ok || val.(int) != 1234 {
   120  		t.Fatal("expected get to succeed with value 1234")
   121  	}
   122  	mc.Add(testKey("b"), 4321)
   123  	val, ok = mc.Get(testKey("b"))
   124  	if !ok || val.(int) != 4321 {
   125  		t.Fatal("expected get to succeed with value 4321")
   126  	}
   127  	// Verify eviction of first key.
   128  	if _, ok = mc.Get(testKey("a")); ok {
   129  		t.Fatal("unexpected success getting evicted key")
   130  	}
   131  }
   132  
   133  func TestCacheLRU(t *testing.T) {
   134  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: evictThreeOrMore})
   135  	// Insert two keys into cache.
   136  	mc.Add(testKey("a"), 1)
   137  	mc.Add(testKey("b"), 2)
   138  	// Get "a" now to make it more recently used.
   139  	if _, ok := mc.Get(testKey("a")); !ok {
   140  		t.Fatal("failed to get key a")
   141  	}
   142  	// Add another entry to evict; should evict key "b".
   143  	mc.Add(testKey("c"), 3)
   144  	// Verify eviction of least recently used key "b".
   145  	if _, ok := mc.Get(testKey("b")); ok {
   146  		t.Fatal("unexpected success getting evicted key")
   147  	}
   148  }
   149  
   150  func TestCacheFIFO(t *testing.T) {
   151  	mc := NewUnorderedCache(Config{Policy: CacheFIFO, ShouldEvict: evictThreeOrMore})
   152  	// Insert two keys into cache.
   153  	mc.Add(testKey("a"), 1)
   154  	mc.Add(testKey("b"), 2)
   155  	// Get "a" now to make it more recently used.
   156  	if _, ok := mc.Get(testKey("a")); !ok {
   157  		t.Fatal("failed to get key a")
   158  	}
   159  	// Add another entry to evict; should evict key "a" still, as that was first in.
   160  	mc.Add(testKey("c"), 3)
   161  	// Verify eviction of first key "a".
   162  	if _, ok := mc.Get(testKey("a")); ok {
   163  		t.Fatal("unexpected success getting evicted key")
   164  	}
   165  }
   166  
   167  func TestOrderedCache(t *testing.T) {
   168  	oc := NewOrderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   169  	oc.Add(testKey("a"), 1)
   170  	oc.Add(testKey("b"), 2)
   171  
   172  	// Verify hit & miss.
   173  	if v, ok := oc.Get(testKey("a")); !ok || v.(int) != 1 {
   174  		t.Error("failed to fetch value for key \"a\"")
   175  	}
   176  	if _, ok := oc.Get(testKey("c")); ok {
   177  		t.Error("unexpected success fetching \"c\"")
   178  	}
   179  
   180  	// Try binary searches for ceil and floor to key direct.
   181  	if _, v, ok := oc.Ceil(testKey("a")); !ok || v.(int) != 1 {
   182  		t.Error("expected success fetching key directly")
   183  	}
   184  	if _, v, ok := oc.Floor(testKey("a")); !ok || v.(int) != 1 {
   185  		t.Error("expected success fetching key directly")
   186  	}
   187  
   188  	// Test ceil and floor operation with empty key.
   189  	if _, v, ok := oc.Ceil(testKey("")); !ok || v.(int) != 1 {
   190  		t.Error("expected fetch of key \"a\" for ceil of empty key")
   191  	}
   192  	if _, _, ok := oc.Floor(testKey("")); ok {
   193  		t.Error("unexpected success fetching floor of empty key")
   194  	}
   195  
   196  	// Test ceil and floor operation with midway key.
   197  	if _, v, ok := oc.Ceil(testKey("aa")); !ok || v.(int) != 2 {
   198  		t.Error("expected fetch of key \"b\" for ceil of midway key")
   199  	}
   200  	if _, v, ok := oc.Floor(testKey("aa")); !ok || v.(int) != 1 {
   201  		t.Error("expected fetch of key \"a\" for floor of midway key")
   202  	}
   203  
   204  	// Test ceil and floor operation with maximum key.
   205  	if _, _, ok := oc.Ceil(testKey("c")); ok {
   206  		t.Error("unexpected success fetching ceil of maximum key")
   207  	}
   208  	if _, v, ok := oc.Floor(testKey("c")); !ok || v.(int) != 2 {
   209  		t.Error("expected fetch of key \"b\" for floor of maximum key")
   210  	}
   211  
   212  	// Test do over entire cache.
   213  	expKeys, collectKeys := []string{"a", "b"}, []string{}
   214  	expVals, collectVals := []int{1, 2}, []int{}
   215  	collect := func(k, v interface{}) bool {
   216  		collectKeys = append(collectKeys, string(k.(testKey)))
   217  		collectVals = append(collectVals, v.(int))
   218  		return false
   219  	}
   220  
   221  	oc.Do(collect)
   222  	if !reflect.DeepEqual(expKeys, collectKeys) {
   223  		t.Errorf("expected do to find keys %v, found %v", expKeys, collectKeys)
   224  	}
   225  	if !reflect.DeepEqual(expVals, collectVals) {
   226  		t.Errorf("expected do to find values %v, found %v", expVals, collectVals)
   227  	}
   228  
   229  	// Test doRange over range ["a","b").
   230  	expKeys, collectKeys = []string{"a"}, []string{}
   231  	expVals, collectVals = []int{1}, []int{}
   232  
   233  	oc.DoRange(collect, testKey("a"), testKey("b"))
   234  	if !reflect.DeepEqual(expKeys, collectKeys) {
   235  		t.Errorf("expected do to find keys %v, found %v", expKeys, collectKeys)
   236  	}
   237  	if !reflect.DeepEqual(expVals, collectVals) {
   238  		t.Errorf("expected do to find values %v, found %v", expVals, collectVals)
   239  	}
   240  }
   241  
   242  func TestOrderedCacheClear(t *testing.T) {
   243  	oc := NewOrderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   244  	oc.Add(testKey("a"), 1)
   245  	oc.Add(testKey("b"), 2)
   246  	oc.Clear()
   247  	if _, ok := oc.Get(testKey("a")); ok {
   248  		t.Error("expected cache cleared")
   249  	}
   250  	if _, ok := oc.Get(testKey("b")); ok {
   251  		t.Error("expected cache cleared")
   252  	}
   253  	oc.Add(testKey("a"), 1)
   254  	if _, ok := oc.Get(testKey("a")); !ok {
   255  		t.Error("expected reinsert to succeed")
   256  	}
   257  }
   258  
   259  func TestIntervalCache(t *testing.T) {
   260  	ic := NewIntervalCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   261  	key1 := ic.NewKey([]byte("a"), []byte("b"))
   262  	key2 := ic.NewKey([]byte("a"), []byte("c"))
   263  	key3 := ic.NewKey([]byte("d"), []byte("d\x00"))
   264  	ic.Add(key1, 1)
   265  	ic.Add(key2, 2)
   266  	ic.Add(key3, 3)
   267  
   268  	// Verify hit & miss.
   269  	if v, ok := ic.Get(key1); !ok || v.(int) != 1 {
   270  		t.Error("failed to fetch value for key \"a\"-\"b\"")
   271  	}
   272  	if v, ok := ic.Get(key2); !ok || v.(int) != 2 {
   273  		t.Error("failed to fetch value for key \"a\"-\"c\"")
   274  	}
   275  	if v, ok := ic.Get(key3); !ok || v.(int) != 3 {
   276  		t.Error("failed to fetch value for key \"d\"")
   277  	}
   278  	if _, ok := ic.Get(ic.NewKey([]byte("a"), []byte("a\x00"))); ok {
   279  		t.Error("unexpected success fetching \"a\"")
   280  	}
   281  
   282  	// Verify replacement on adding identical key.
   283  	ic.Add(key1, 3)
   284  	if v, ok := ic.Get(key1); !ok || v.(int) != 3 {
   285  		t.Error("failed to fetch value for key \"a\"-\"b\"")
   286  	}
   287  }
   288  
   289  func TestIntervalCacheOverlap(t *testing.T) {
   290  	ic := NewIntervalCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   291  	ic.Add(ic.NewKey([]byte("a"), []byte("c")), 1)
   292  	ic.Add(ic.NewKey([]byte("c"), []byte("e")), 2)
   293  	ic.Add(ic.NewKey([]byte("b"), []byte("g")), 3)
   294  	ic.Add(ic.NewKey([]byte("d"), []byte("e")), 4)
   295  	ic.Add(ic.NewKey([]byte("b"), []byte("d")), 5)
   296  	ic.Add(ic.NewKey([]byte("e"), []byte("g")), 6)
   297  	ic.Add(ic.NewKey([]byte("f"), []byte("i")), 7)
   298  	ic.Add(ic.NewKey([]byte("g"), []byte("i")), 8)
   299  	ic.Add(ic.NewKey([]byte("f"), []byte("h")), 9)
   300  	ic.Add(ic.NewKey([]byte("i"), []byte("j")), 10)
   301  
   302  	expValues := []interface{}{3, 2, 4, 6, 7, 9}
   303  	values := []interface{}{}
   304  	for _, o := range ic.GetOverlaps([]byte("d"), []byte("g")) {
   305  		values = append(values, o.Value)
   306  	}
   307  	if !reflect.DeepEqual(expValues, values) {
   308  		t.Errorf("expected overlap values %+v, got %+v", expValues, values)
   309  	}
   310  }
   311  
   312  func TestIntervalCacheClear(t *testing.T) {
   313  	ic := NewIntervalCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   314  	key1 := ic.NewKey([]byte("a"), []byte("c"))
   315  	key2 := ic.NewKey([]byte("c"), []byte("e"))
   316  	ic.Add(key1, 1)
   317  	ic.Add(key2, 2)
   318  	ic.Clear()
   319  	if _, ok := ic.Get(key1); ok {
   320  		t.Error("expected cache cleared")
   321  	}
   322  	if _, ok := ic.Get(key2); ok {
   323  		t.Error("expected cache cleared")
   324  	}
   325  	if l := ic.Len(); l != 0 {
   326  		t.Errorf("expected cleared cache to have len 0, found %d", l)
   327  	}
   328  	ic.Add(key1, 1)
   329  	if _, ok := ic.Get(key1); !ok {
   330  		t.Error("expected reinsert to succeed")
   331  	}
   332  }
   333  
   334  func TestIntervalCacheClearWithAdjustedBounds(t *testing.T) {
   335  	ic := NewIntervalCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   336  	entry1 := &Entry{Key: ic.NewKey([]byte("a"), []byte("bb")), Value: 1}
   337  	ic.AddEntry(entry1)
   338  	entry1.Key.(*IntervalKey).End = []byte("b")
   339  	entry2 := &Entry{Key: ic.NewKey([]byte("b"), []byte("e")), Value: 2}
   340  	ic.AddEntry(entry2)
   341  	entry2.Key.(*IntervalKey).End = []byte("c")
   342  	entry2Right := &Entry{Key: ic.NewKey([]byte("c\x00"), []byte("e")), Value: 3}
   343  	ic.AddEntry(entry2Right)
   344  	entry3 := &Entry{Key: ic.NewKey([]byte("c"), []byte("c\x00")), Value: 4}
   345  	ic.AddEntry(entry3)
   346  	ic.Clear()
   347  	if l := ic.Len(); l != 0 {
   348  		t.Errorf("expected cleared cache to have len 0, found %d", l)
   349  	}
   350  }
   351  
   352  func benchmarkCache(b *testing.B, c *baseCache, keys []interface{}) {
   353  	b.ResetTimer()
   354  	for i := 0; i < b.N; i++ {
   355  		for j := 0; j < len(keys); j++ {
   356  			c.Add(keys[j], j)
   357  		}
   358  		c.Clear()
   359  	}
   360  }
   361  
   362  func BenchmarkUnorderedCache(b *testing.B) {
   363  	mc := NewUnorderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   364  	testKeys := []interface{}{
   365  		testKey("a"),
   366  		testKey("b"),
   367  		testKey("c"),
   368  		testKey("d"),
   369  		testKey("e"),
   370  	}
   371  	benchmarkCache(b, &mc.baseCache, testKeys)
   372  }
   373  
   374  func BenchmarkOrderedCache(b *testing.B) {
   375  	oc := NewOrderedCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   376  	testKeys := []interface{}{
   377  		testKey("a"),
   378  		testKey("b"),
   379  		testKey("c"),
   380  		testKey("d"),
   381  		testKey("e"),
   382  	}
   383  	benchmarkCache(b, &oc.baseCache, testKeys)
   384  }
   385  
   386  func BenchmarkIntervalCache(b *testing.B) {
   387  	ic := NewIntervalCache(Config{Policy: CacheLRU, ShouldEvict: noEviction})
   388  	testKeys := []interface{}{
   389  		ic.NewKey([]byte("a"), []byte("c")),
   390  		ic.NewKey([]byte("b"), []byte("d")),
   391  		ic.NewKey([]byte("c"), []byte("e")),
   392  		ic.NewKey([]byte("d"), []byte("f")),
   393  		ic.NewKey([]byte("e"), []byte("g")),
   394  	}
   395  	benchmarkCache(b, &ic.baseCache, testKeys)
   396  }