github.com/pingcap/badger@v1.5.1-0.20230103063557-828f39b09b6d/cache/cache_test.go (about)

     1  package cache
     2  
     3  import (
     4  	"math/rand"
     5  	"runtime"
     6  	"strings"
     7  	"sync"
     8  	"sync/atomic"
     9  	"testing"
    10  	"time"
    11  )
    12  
    13  var wait time.Duration = time.Millisecond * 10
    14  
    15  func TestCacheMaxCost(t *testing.T) {
    16  	key := func() uint64 {
    17  		return uint64(rand.Intn(36 * 36))
    18  	}
    19  	c, err := NewCache(&Config{
    20  		NumCounters: 12960, // 36^2 * 10
    21  		MaxCost:     1e6,   // 1mb
    22  		BufferItems: 64,
    23  		Metrics:     true,
    24  	})
    25  	if err != nil {
    26  		panic(err)
    27  	}
    28  	stop := make(chan struct{}, 8)
    29  	for i := 0; i < 8; i++ {
    30  		go func() {
    31  			for {
    32  				select {
    33  				case <-stop:
    34  					return
    35  				default:
    36  					time.Sleep(time.Millisecond)
    37  
    38  					k := key()
    39  					if _, ok := c.Get(k); !ok {
    40  						val := ""
    41  						if rand.Intn(100) < 10 {
    42  							val = "test"
    43  						} else {
    44  							val = strings.Repeat("a", 1000)
    45  						}
    46  						c.Set(key(), val, int64(2+len(val)))
    47  					}
    48  				}
    49  			}
    50  		}()
    51  	}
    52  	for i := 0; i < 20; i++ {
    53  		time.Sleep(time.Second)
    54  		cacheCost := c.Metrics.CostAdded() - c.Metrics.CostEvicted()
    55  		t.Logf("total cache cost: %d\n", cacheCost)
    56  		if float64(cacheCost) > float64(1e6*1.05) {
    57  			t.Fatal("cache cost exceeding MaxCost")
    58  		}
    59  	}
    60  	for i := 0; i < 8; i++ {
    61  		stop <- struct{}{}
    62  	}
    63  }
    64  
    65  func TestCache(t *testing.T) {
    66  	if _, err := NewCache(&Config{
    67  		NumCounters: 0,
    68  	}); err == nil {
    69  		t.Fatal("numCounters can't be 0")
    70  	}
    71  	if _, err := NewCache(&Config{
    72  		NumCounters: 100,
    73  		MaxCost:     0,
    74  	}); err == nil {
    75  		t.Fatal("maxCost can't be 0")
    76  	}
    77  	if _, err := NewCache(&Config{
    78  		NumCounters: 100,
    79  		MaxCost:     10,
    80  		BufferItems: 0,
    81  	}); err == nil {
    82  		t.Fatal("bufferItems can't be 0")
    83  	}
    84  	if c, err := NewCache(&Config{
    85  		NumCounters: 100,
    86  		MaxCost:     10,
    87  		BufferItems: 64,
    88  		Metrics:     true,
    89  	}); c == nil || err != nil {
    90  		t.Fatal("config should be good")
    91  	}
    92  }
    93  
    94  func TestCacheProcessItems(t *testing.T) {
    95  	m := &sync.Mutex{}
    96  	evicted := make(map[uint64]struct{})
    97  	c, err := NewCache(&Config{
    98  		NumCounters: 100,
    99  		MaxCost:     10,
   100  		BufferItems: 64,
   101  		Cost: func(value interface{}) int64 {
   102  			return int64(value.(int))
   103  		},
   104  		OnEvict: func(key uint64, value interface{}) {
   105  			m.Lock()
   106  			defer m.Unlock()
   107  			evicted[key] = struct{}{}
   108  		},
   109  	})
   110  	if err != nil {
   111  		panic(err)
   112  	}
   113  
   114  	c.Set(1, 1, 0)
   115  	time.Sleep(wait)
   116  	if !c.policy.Has(1) || c.policy.Cost(1) != 1 {
   117  		t.Fatal("cache processItems didn't add new item")
   118  	}
   119  	c.Set(1, 2, 0)
   120  	time.Sleep(wait)
   121  	if c.policy.Cost(1) != 2 {
   122  		t.Fatal("cache processItems didn't update item cost")
   123  	}
   124  	c.Del(1)
   125  	time.Sleep(wait)
   126  	if val, ok := c.store.Get(1); val != nil || ok {
   127  		t.Fatal("cache processItems didn't delete item")
   128  	}
   129  	if c.policy.Has(1) {
   130  		t.Fatal("cache processItems didn't delete item")
   131  	}
   132  	c.Set(2, 2, 3)
   133  	c.Set(3, 3, 3)
   134  	c.Set(5, 3, 3)
   135  	c.Set(5, 3, 5)
   136  	c.Set(1, 3, 3)
   137  	time.Sleep(wait)
   138  	m.Lock()
   139  	if len(evicted) == 0 {
   140  		m.Unlock()
   141  		t.Fatal("cache processItems not evicting or calling OnEvict")
   142  	}
   143  	m.Unlock()
   144  	c.Close()
   145  }
   146  
   147  func TestCacheGet(t *testing.T) {
   148  	c, err := NewCache(&Config{
   149  		NumCounters: 100,
   150  		MaxCost:     10,
   151  		BufferItems: 64,
   152  		Metrics:     true,
   153  	})
   154  	if err != nil {
   155  		panic(err)
   156  	}
   157  	c.Set(1, 1, 1)
   158  	if val, ok := c.Get(1); val == nil || !ok {
   159  		t.Fatal("get should be successful")
   160  	}
   161  	if val, ok := c.Get(2); val != nil || ok {
   162  		t.Fatal("get should not be successful")
   163  	}
   164  	// 0.5 and not 1.0 because we tried Getting each item twice
   165  	if c.Metrics.Ratio() != 0.5 {
   166  		t.Fatal("get should record metrics")
   167  	}
   168  	c = nil
   169  	if val, ok := c.Get(0); val != nil || ok {
   170  		t.Fatal("get should not be successful with nil cache")
   171  	}
   172  }
   173  
   174  func TestCacheSet(t *testing.T) {
   175  	c, err := NewCache(&Config{
   176  		NumCounters: 100,
   177  		MaxCost:     10,
   178  		BufferItems: 64,
   179  		Metrics:     true,
   180  	})
   181  	if err != nil {
   182  		panic(err)
   183  	}
   184  	c.Set(1, 1, 1)
   185  	time.Sleep(wait)
   186  	if val, ok := c.Get(1); val == nil || val.(int) != 1 || !ok {
   187  		t.Fatal("set/get returned wrong value")
   188  	}
   189  
   190  	c.Set(1, 2, 2)
   191  	val, ok := c.store.GetValue(1)
   192  	if val == nil || val.(int) != 2 || !ok {
   193  		t.Fatal("set/update was unsuccessful")
   194  	}
   195  	c.stop <- struct{}{}
   196  	for i := 0; i < setBufSize; i++ {
   197  		c.Set(1, 1, 1)
   198  	}
   199  	close(c.setBuf)
   200  	close(c.stop)
   201  }
   202  
   203  func TestCacheGetOrCompute(t *testing.T) {
   204  	c, err := NewCache(&Config{
   205  		NumCounters: 100,
   206  		MaxCost:     10,
   207  		BufferItems: 64,
   208  		Metrics:     true,
   209  	})
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  	p := runtime.GOMAXPROCS(0)
   214  	start := make(chan struct{})
   215  	var done sync.WaitGroup
   216  	var cnt uint32
   217  	setFlags := make([]bool, p)
   218  	results := make([]int, p)
   219  
   220  	for n := 0; n < p; n++ {
   221  		done.Add(1)
   222  		go func(id int) {
   223  			<-start
   224  			var set bool
   225  			var result interface{}
   226  			for i := 0; i < 10; i++ {
   227  				result, _ = c.GetOrCompute(uint64(0), func() (interface{}, int64, error) {
   228  					set = true
   229  					atomic.AddUint32(&cnt, 1)
   230  					return id, 1, nil
   231  				})
   232  			}
   233  			if set {
   234  				setFlags[id] = true
   235  			}
   236  			results[id] = result.(int)
   237  			done.Done()
   238  		}(n)
   239  	}
   240  	close(start)
   241  	done.Wait()
   242  
   243  	if cnt != 1 {
   244  		t.Fatalf("the factory function is called %d times", cnt)
   245  	}
   246  	setter := -1
   247  	for id, flag := range setFlags {
   248  		if !flag {
   249  			continue
   250  		}
   251  		if setter != -1 {
   252  			t.Fatal("more than one setter success")
   253  		}
   254  		setter = id
   255  	}
   256  	for id, v := range results {
   257  		if v != setter {
   258  			t.Fatalf("%d got different result (%d != %d)", id, v, setter)
   259  		}
   260  	}
   261  }
   262  
   263  func TestCacheDel(t *testing.T) {
   264  	c, err := NewCache(&Config{
   265  		NumCounters: 100,
   266  		MaxCost:     10,
   267  		BufferItems: 64,
   268  	})
   269  	if err != nil {
   270  		panic(err)
   271  	}
   272  	c.Set(1, 1, 1)
   273  	c.Del(1)
   274  	time.Sleep(wait)
   275  	if val, ok := c.Get(1); val != nil || ok {
   276  		t.Fatal("del didn't delete")
   277  	}
   278  	c = nil
   279  	defer func() {
   280  		if r := recover(); r != nil {
   281  			t.Fatal("del panic with nil cache")
   282  		}
   283  	}()
   284  	c.Del(1)
   285  }
   286  
   287  func TestCacheClear(t *testing.T) {
   288  	c, err := NewCache(&Config{
   289  		NumCounters: 100,
   290  		MaxCost:     10,
   291  		BufferItems: 64,
   292  		Metrics:     true,
   293  	})
   294  	if err != nil {
   295  		panic(err)
   296  	}
   297  	for i := uint64(0); i < 10; i++ {
   298  		c.Set(i, i, 1)
   299  	}
   300  	time.Sleep(wait)
   301  	if c.Metrics.KeysAdded() != 10 {
   302  		t.Fatal("range of sets not being processed")
   303  	}
   304  	c.Clear()
   305  	if c.Metrics.KeysAdded() != 0 {
   306  		t.Fatal("clear didn't reset metrics")
   307  	}
   308  	for i := uint64(0); i < 10; i++ {
   309  		if val, ok := c.Get(i); val != nil || ok {
   310  			t.Fatal("clear didn't delete values")
   311  		}
   312  	}
   313  }
   314  
   315  func TestCacheMetrics(t *testing.T) {
   316  	c, err := NewCache(&Config{
   317  		NumCounters: 100,
   318  		MaxCost:     10,
   319  		BufferItems: 64,
   320  		Metrics:     true,
   321  	})
   322  	if err != nil {
   323  		panic(err)
   324  	}
   325  	for i := uint64(0); i < 10; i++ {
   326  		c.Set(i, i, 1)
   327  	}
   328  	time.Sleep(wait)
   329  	m := c.Metrics
   330  	if m.KeysAdded() != 10 {
   331  		t.Fatal("metrics exporting incorrect fields")
   332  	}
   333  }
   334  
   335  func TestMetrics(t *testing.T) {
   336  	newMetrics()
   337  }
   338  
   339  func TestMetricsAddGet(t *testing.T) {
   340  	m := newMetrics()
   341  	m.add(hit, 1, 1)
   342  	m.add(hit, 2, 2)
   343  	m.add(hit, 3, 3)
   344  	if m.Hits() != 6 {
   345  		t.Fatal("add/get error")
   346  	}
   347  	m = nil
   348  	m.add(hit, 1, 1)
   349  	if m.Hits() != 0 {
   350  		t.Fatal("get with nil struct should return 0")
   351  	}
   352  }
   353  
   354  func TestMetricsRatio(t *testing.T) {
   355  	m := newMetrics()
   356  	if m.Ratio() != 0 {
   357  		t.Fatal("ratio with no hits or misses should be 0")
   358  	}
   359  	m.add(hit, 1, 1)
   360  	m.add(hit, 2, 2)
   361  	m.add(miss, 1, 1)
   362  	m.add(miss, 2, 2)
   363  	if m.Ratio() != 0.5 {
   364  		t.Fatal("ratio incorrect")
   365  	}
   366  	m = nil
   367  	if m.Ratio() != 0.0 {
   368  		t.Fatal("ratio with a nil struct should return 0")
   369  	}
   370  }
   371  
   372  func TestMetricsString(t *testing.T) {
   373  	m := newMetrics()
   374  	m.add(hit, 1, 1)
   375  	m.add(miss, 1, 1)
   376  	m.add(keyAdd, 1, 1)
   377  	m.add(keyUpdate, 1, 1)
   378  	m.add(keyEvict, 1, 1)
   379  	m.add(costAdd, 1, 1)
   380  	m.add(costEvict, 1, 1)
   381  	m.add(dropSets, 1, 1)
   382  	m.add(rejectSets, 1, 1)
   383  	m.add(dropGets, 1, 1)
   384  	m.add(keepGets, 1, 1)
   385  	if m.Hits() != 1 || m.Misses() != 1 || m.Ratio() != 0.5 || m.KeysAdded() != 1 ||
   386  		m.KeysUpdated() != 1 || m.KeysEvicted() != 1 || m.CostAdded() != 1 ||
   387  		m.CostEvicted() != 1 || m.SetsDropped() != 1 || m.SetsRejected() != 1 ||
   388  		m.GetsDropped() != 1 || m.GetsKept() != 1 {
   389  		t.Fatal("Metrics wrong value(s)")
   390  	}
   391  	if len(m.String()) == 0 {
   392  		t.Fatal("Metrics.String() empty")
   393  	}
   394  	m = nil
   395  	if len(m.String()) != 0 {
   396  		t.Fatal("Metrics.String() should be empty with nil struct")
   397  	}
   398  	if stringFor(doNotUse) != "unidentified" {
   399  		t.Fatal("stringFor() not handling doNotUse type")
   400  	}
   401  }
   402  
   403  func TestCacheMetricsClear(t *testing.T) {
   404  	c, err := NewCache(&Config{
   405  		NumCounters: 100,
   406  		MaxCost:     10,
   407  		BufferItems: 64,
   408  		Metrics:     true,
   409  	})
   410  	if err != nil {
   411  		panic(err)
   412  	}
   413  	c.Set(1, 1, 1)
   414  	stop := make(chan struct{})
   415  	go func() {
   416  		for {
   417  			select {
   418  			case <-stop:
   419  				return
   420  			default:
   421  				c.Get(1)
   422  			}
   423  		}
   424  	}()
   425  	time.Sleep(wait)
   426  	c.Clear()
   427  	stop <- struct{}{}
   428  	c.Metrics = nil
   429  	c.Metrics.Clear()
   430  }