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

     1  // Copyright 2018 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  package raftentry
    12  
    13  import (
    14  	"math"
    15  	"math/rand"
    16  	"reflect"
    17  	"sync"
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    22  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    23  	"go.etcd.io/etcd/raft/raftpb"
    24  )
    25  
    26  const noLimit = math.MaxUint64
    27  
    28  func newEntry(index, size uint64) raftpb.Entry {
    29  	data := make([]byte, size)
    30  	if _, err := rand.Read(data); err != nil {
    31  		panic(err)
    32  	}
    33  	return raftpb.Entry{
    34  		Index: index,
    35  		Data:  data,
    36  	}
    37  }
    38  
    39  func newEntries(lo, hi, size uint64) []raftpb.Entry {
    40  	ents := []raftpb.Entry{}
    41  	for i := lo; i < hi; i++ {
    42  		ents = append(ents, newEntry(i, size))
    43  	}
    44  	return ents
    45  }
    46  
    47  func addEntries(c *Cache, rangeID roachpb.RangeID, lo, hi uint64) []raftpb.Entry {
    48  	ents := newEntries(lo, hi, 1)
    49  	c.Add(rangeID, ents, false)
    50  	return ents
    51  }
    52  
    53  func verifyGet(
    54  	t *testing.T,
    55  	c *Cache,
    56  	rangeID roachpb.RangeID,
    57  	lo, hi uint64,
    58  	expEnts []raftpb.Entry,
    59  	expNextIndex uint64,
    60  	allowEviction bool,
    61  ) {
    62  	t.Helper()
    63  	ents, _, nextIndex, _ := c.Scan(nil, rangeID, lo, hi, noLimit)
    64  	if allowEviction && len(ents) == 0 {
    65  		return
    66  	}
    67  	if !(len(expEnts) == 0 && len(ents) == 0) && !reflect.DeepEqual(expEnts, ents) {
    68  		t.Fatalf("expected entries %+v; got %+v", expEnts, ents)
    69  	}
    70  	if nextIndex != expNextIndex {
    71  		t.Fatalf("expected next index %d; got %d", expNextIndex, nextIndex)
    72  	}
    73  	for _, e := range ents {
    74  		found, ok := c.Get(rangeID, e.Index)
    75  		if !ok {
    76  			if allowEviction {
    77  				break
    78  			}
    79  			t.Fatalf("expected to be able to retrieve entry")
    80  		}
    81  		if !reflect.DeepEqual(found, e) {
    82  			t.Fatalf("expected entry %v, but got %v", e, found)
    83  		}
    84  	}
    85  }
    86  
    87  func TestEntryCache(t *testing.T) {
    88  	defer leaktest.AfterTest(t)()
    89  	c := NewCache(100 + 2*uint64(partitionSize))
    90  	rangeID := roachpb.RangeID(2)
    91  	otherRangeID := rangeID + 1
    92  	// Note 9 bytes per entry with data size of 1
    93  	verify := func(rangeID roachpb.RangeID, lo, hi uint64, ents []raftpb.Entry, expNextIndex uint64) {
    94  		t.Helper()
    95  		verifyGet(t, c, rangeID, lo, hi, ents, expNextIndex, false)
    96  	}
    97  	// Add entries for range 1, indexes [1-9).
    98  	ents := addEntries(c, rangeID, 1, 9)
    99  	verifyMetrics(t, c, 8, 72+int64(partitionSize))
   100  	// Fetch all data with an exact match.
   101  	verify(rangeID, 1, 9, ents, 9)
   102  	// Fetch point entry.
   103  	verify(rangeID, 1, 2, ents[0:1], 2)
   104  	// Fetch overlapping first half.
   105  	verify(rangeID, 0, 3, []raftpb.Entry{}, 0)
   106  	// Fetch overlapping second half.
   107  	verify(rangeID, 5, 9, ents[4:], 9)
   108  	// Fetch data from earlier range.
   109  	verify(roachpb.RangeID(1), 1, 11, []raftpb.Entry{}, 1)
   110  	// Fetch data from later range.
   111  	verify(roachpb.RangeID(3), 1, 11, []raftpb.Entry{}, 1)
   112  	// Add another range which we can evict.
   113  	otherEnts := addEntries(c, otherRangeID, 1, 3)
   114  	verifyMetrics(t, c, 10, 90+2*int64(partitionSize))
   115  	verify(otherRangeID, 1, 3, otherEnts[:], 3)
   116  	// Add overlapping entries which will lead to eviction.
   117  	newEnts := addEntries(c, rangeID, 8, 11)
   118  	ents = append(ents[:7], newEnts...)
   119  	verifyMetrics(t, c, 10, 90+int64(partitionSize))
   120  	// Ensure other range got evicted but first range did not.
   121  	verify(rangeID, 1, 11, ents[:], 11)
   122  	verify(otherRangeID, 1, 11, []raftpb.Entry{}, 1)
   123  	// Clear and show that it makes space.
   124  	c.Clear(rangeID, 10)
   125  	verify(rangeID, 10, 11, ents[9:], 11)
   126  	verifyMetrics(t, c, 1, 9+int64(partitionSize))
   127  	// Clear again and show that it still works.
   128  	c.Clear(rangeID, 10)
   129  	verify(rangeID, 10, 11, ents[9:], 11)
   130  	verifyMetrics(t, c, 1, 9+int64(partitionSize))
   131  	// Add entries from before and show that they get cached.
   132  	c.Add(rangeID, ents[5:], false)
   133  	verify(rangeID, 6, 11, ents[5:], 11)
   134  	verifyMetrics(t, c, 5, 45+int64(partitionSize))
   135  	// Add a few repeat entries and show that nothing changes.
   136  	c.Add(rangeID, ents[6:8], false)
   137  	verify(rangeID, 6, 11, ents[5:], 11)
   138  	verifyMetrics(t, c, 5, 45+int64(partitionSize))
   139  	// Add a few repeat entries while truncating.
   140  	// Non-overlapping tail should be evicted.
   141  	c.Add(rangeID, ents[6:8], true)
   142  	verify(rangeID, 6, 11, ents[5:8], 9)
   143  	verifyMetrics(t, c, 3, 27+int64(partitionSize))
   144  }
   145  
   146  func verifyMetrics(t *testing.T, c *Cache, expectedEntries, expectedBytes int64) {
   147  	t.Helper()
   148  	// NB: Cache gauges are updated asynchronously. In the face of concurrent
   149  	// updates gauges may not reflect the current cache state. Force
   150  	// synchrononization of the metrics before using them to validate cache state.
   151  	c.syncGauges()
   152  	if got := c.Metrics().Size.Value(); got != expectedEntries {
   153  		t.Errorf("expected cache to have %d entries, got %d", expectedEntries, got)
   154  	}
   155  	if got := c.Metrics().Bytes.Value(); got != expectedBytes {
   156  		t.Errorf("expected cache to have %d bytes, got %d", expectedBytes, got)
   157  	}
   158  }
   159  
   160  func (c *Cache) syncGauges() {
   161  	c.updateGauges(c.addBytes(0), c.addEntries(0))
   162  }
   163  
   164  func TestIgnoredAdd(t *testing.T) {
   165  	defer leaktest.AfterTest(t)()
   166  	rangeID := roachpb.RangeID(1)
   167  	c := NewCache(100 + uint64(partitionSize))
   168  	// Show that adding entries which are larger than maxBytes is ignored.
   169  	_ = addEntries(c, rangeID, 1, 41)
   170  	verifyGet(t, c, rangeID, 1, 41, nil, 1, false)
   171  	verifyMetrics(t, c, 0, 0)
   172  	// Add some entries so we can show that a non-overlapping add is ignored.
   173  	ents := addEntries(c, rangeID, 4, 7)
   174  	verifyGet(t, c, rangeID, 4, 7, ents, 7, false)
   175  	verifyMetrics(t, c, 3, 27+int64(partitionSize))
   176  	addEntries(c, rangeID, 1, 3)
   177  	verifyMetrics(t, c, 3, 27+int64(partitionSize))
   178  }
   179  
   180  func TestAddAndTruncate(t *testing.T) {
   181  	defer leaktest.AfterTest(t)()
   182  	rangeID := roachpb.RangeID(1)
   183  	c := NewCache(200 + uint64(partitionSize))
   184  	ents := addEntries(c, rangeID, 1, 10)
   185  	verifyGet(t, c, rangeID, 1, 10, ents, 10, false)
   186  	verifyMetrics(t, c, 9, 81+int64(partitionSize))
   187  	// Show that, if specified, adding an overlapping set of entries
   188  	// truncates any at a larger index that is not overwritten.
   189  	c.Add(rangeID, ents[2:6], true /* truncate */)
   190  	verifyGet(t, c, rangeID, 1, 10, ents[:6], 7, false)
   191  	verifyMetrics(t, c, 6, 54+int64(partitionSize))
   192  	// Show that even if the addition is ignored due to size, entries
   193  	// with an equal or larger index are truncated.
   194  	largeEnts := newEntries(5, 6, 300)
   195  	c.Add(rangeID, largeEnts, true /* truncate */)
   196  	verifyGet(t, c, rangeID, 1, 10, ents[:4], 5, false)
   197  	verifyMetrics(t, c, 4, 36+int64(partitionSize))
   198  }
   199  
   200  func TestDrop(t *testing.T) {
   201  	defer leaktest.AfterTest(t)()
   202  	const (
   203  		r1 roachpb.RangeID = 1
   204  		r2 roachpb.RangeID = 2
   205  
   206  		sizeOf9Entries = 81
   207  		partitionSize  = int64(sizeOf9Entries + partitionSize)
   208  	)
   209  	c := NewCache(1 << 10)
   210  	ents1 := addEntries(c, r1, 1, 10)
   211  	verifyGet(t, c, r1, 1, 10, ents1, 10, false)
   212  	verifyMetrics(t, c, 9, partitionSize)
   213  	ents2 := addEntries(c, r2, 1, 10)
   214  	verifyGet(t, c, r2, 1, 10, ents2, 10, false)
   215  	verifyMetrics(t, c, 18, 2*partitionSize)
   216  	c.Drop(r1)
   217  	verifyMetrics(t, c, 9, partitionSize)
   218  	c.Drop(r2)
   219  	verifyMetrics(t, c, 0, 0)
   220  }
   221  
   222  func TestCacheLaterEntries(t *testing.T) {
   223  	c := NewCache(1000)
   224  	rangeID := roachpb.RangeID(1)
   225  	ents := addEntries(c, rangeID, 1, 10)
   226  	verifyGet(t, c, rangeID, 1, 10, ents, 10, false)
   227  	// The previous entries are evicted because they would not have been
   228  	// contiguous with the new entries.
   229  	ents = addEntries(c, rangeID, 11, 21)
   230  	if got, _, _, _ := c.Scan(nil, rangeID, 1, 10, noLimit); len(got) != 0 {
   231  		t.Fatalf("Expected not to get entries from range which preceded new values")
   232  	}
   233  	verifyGet(t, c, rangeID, 11, 21, ents, 21, false)
   234  }
   235  
   236  func TestExceededMaxBytes(t *testing.T) {
   237  	defer leaktest.AfterTest(t)()
   238  	rangeID := roachpb.RangeID(1)
   239  	c := NewCache(100)
   240  	addEntries(c, rangeID, 1, 10)
   241  	ents, _, next, exceeded := c.Scan(nil, rangeID, 1, 10, 18)
   242  	if len(ents) != 2 || next != 3 || !exceeded {
   243  		t.Errorf("expected 2 entries with next=3 and to have exceededMaxBytes, got %d, %d, %v",
   244  			len(ents), next, exceeded)
   245  	}
   246  }
   247  
   248  func TestEntryCacheClearTo(t *testing.T) {
   249  	defer leaktest.AfterTest(t)()
   250  	rangeID := roachpb.RangeID(1)
   251  	c := NewCache(100)
   252  	c.Add(rangeID, []raftpb.Entry{newEntry(20, 1), newEntry(21, 1)}, true)
   253  	c.Clear(rangeID, 21)
   254  	c.Clear(rangeID, 18)
   255  	if ents, _, _, _ := c.Scan(nil, rangeID, 2, 21, noLimit); len(ents) != 0 {
   256  		t.Errorf("expected no entries after clearTo")
   257  	}
   258  	if ents, _, _, _ := c.Scan(nil, rangeID, 21, 22, noLimit); len(ents) != 1 {
   259  		t.Errorf("expected entry 22 to remain in the cache clearTo")
   260  	}
   261  	c.Clear(rangeID, 23) // past the end
   262  	if ents, _, _, _ := c.Scan(nil, rangeID, 21, 22, noLimit); len(ents) != 0 {
   263  		t.Errorf("expected e//ntry 22 to be cleared")
   264  	}
   265  	if _, ok := c.Get(rangeID, 21); ok {
   266  		t.Errorf("didn't expect to get any entry")
   267  	}
   268  	verifyMetrics(t, c, 0, int64(partitionSize))
   269  	c.Clear(rangeID, 22)
   270  }
   271  
   272  func TestMaxBytesLimit(t *testing.T) {
   273  	c := NewCache(1 << 32)
   274  	if c.maxBytes != (1<<31 - 1) {
   275  		t.Fatalf("maxBytes cannot be larger than %d", 1<<31)
   276  	}
   277  }
   278  
   279  func TestConcurrentEvictions(t *testing.T) {
   280  	// This tests for safety in the face of concurrent updates.
   281  	// The main goroutine randomly chooses a free partition for a read or write.
   282  	// Concurrent operations will lead to evictions. Reads verify that either the
   283  	// data is read and correct or is not read. At the end, all the ranges are
   284  	// cleared and we ensure that the entry count is zero.
   285  
   286  	// NB: N is chosen based on the race detector's limit of 8128 goroutines.
   287  	const N = 8000
   288  	const numRanges = 200
   289  	const maxEntriesPerWrite = 111
   290  	rangeData := make(map[roachpb.RangeID][]raftpb.Entry)
   291  	rangeInUse := make(map[roachpb.RangeID]bool)
   292  	c := NewCache(1000)
   293  	rangeDoneChan := make(chan roachpb.RangeID)
   294  	pickRange := func() (r roachpb.RangeID) {
   295  		for {
   296  			r = roachpb.RangeID(rand.Intn(numRanges))
   297  			if !rangeInUse[r] {
   298  				break
   299  			}
   300  		}
   301  		rangeInUse[r] = true
   302  		return r
   303  	}
   304  	var wg sync.WaitGroup
   305  	doRead := func(r roachpb.RangeID) {
   306  		ents := rangeData[r]
   307  		offset := rand.Intn(len(ents))
   308  		length := rand.Intn(len(ents) - offset)
   309  		lo := ents[offset].Index
   310  		hi := lo + uint64(length)
   311  		wg.Add(1)
   312  		go func() {
   313  			time.Sleep(time.Duration(rand.Intn(int(time.Microsecond))))
   314  			verifyGet(t, c, r, lo, hi, ents[offset:offset+length], hi, true)
   315  			rangeDoneChan <- r
   316  			wg.Done()
   317  		}()
   318  	}
   319  	doWrite := func(r roachpb.RangeID) {
   320  		ents := rangeData[r]
   321  		offset := rand.Intn(len(ents)+1) - 1
   322  		length := rand.Intn(maxEntriesPerWrite)
   323  		var toAdd []raftpb.Entry
   324  		if offset >= 0 && offset < len(ents) {
   325  			lo := ents[offset].Index
   326  			hi := lo + uint64(length)
   327  			toAdd = newEntries(lo, hi, 1)
   328  			ents = append(ents[:offset], toAdd...)
   329  		} else {
   330  			lo := uint64(offset + 2)
   331  			hi := lo + uint64(length)
   332  			toAdd = newEntries(lo, hi, 1)
   333  			ents = toAdd
   334  		}
   335  		rangeData[r] = ents
   336  		wg.Add(1)
   337  		go func() {
   338  			time.Sleep(time.Duration(rand.Intn(int(time.Microsecond))))
   339  			c.Add(r, toAdd, true)
   340  			rangeDoneChan <- r
   341  			wg.Done()
   342  		}()
   343  	}
   344  	for i := 0; i < N; i++ {
   345  		for len(rangeInUse) > numRanges/2 {
   346  			delete(rangeInUse, <-rangeDoneChan)
   347  		}
   348  		r := pickRange()
   349  		if read := rand.Intn(2) == 1; read && len(rangeData[r]) > 0 {
   350  			doRead(r)
   351  		} else {
   352  			doWrite(r)
   353  		}
   354  	}
   355  	go func() { wg.Wait(); close(rangeDoneChan) }()
   356  	for r := range rangeDoneChan {
   357  		delete(rangeInUse, r)
   358  	}
   359  	// Clear the data and ensure that the cache stats are valid.
   360  	for r, data := range rangeData {
   361  		if len(data) == 0 {
   362  			continue
   363  		}
   364  		c.Clear(r, data[len(data)-1].Index+1)
   365  	}
   366  	verifyMetrics(t, c, 0, int64(len(c.parts))*int64(partitionSize))
   367  }
   368  
   369  func TestHeadWrappingForward(t *testing.T) {
   370  	defer leaktest.AfterTest(t)()
   371  	rangeID := roachpb.RangeID(1)
   372  	c := NewCache(200 + uint64(partitionSize))
   373  	ents := addEntries(c, rangeID, 1, 8)
   374  	// Clear some space at the front of the ringBuf.
   375  	c.Clear(rangeID, 4)
   376  	verifyMetrics(t, c, 4, 36+int64(partitionSize))
   377  	// Fill in space at the front of the ringBuf.
   378  	ents = append(ents[3:4], addEntries(c, rangeID, 5, 20)...)
   379  	verifyMetrics(t, c, 16, 144+int64(partitionSize))
   380  	verifyGet(t, c, rangeID, 4, 20, ents, 20, false)
   381  	// Realloc copying from the wrapped around ringBuf.
   382  	ents = append(ents, addEntries(c, rangeID, 20, 22)...)
   383  	verifyGet(t, c, rangeID, 18, 22, ents[14:], 22, false)
   384  }
   385  
   386  func TestHeadWrappingBackwards(t *testing.T) {
   387  	defer leaktest.AfterTest(t)()
   388  	rangeID := roachpb.RangeID(1)
   389  	c := NewCache(100 + uint64(partitionSize))
   390  	ents := addEntries(c, rangeID, 3, 5)
   391  	c.Clear(rangeID, 4)
   392  	ents = append(addEntries(c, rangeID, 1, 4), ents[1:]...)
   393  	verifyGet(t, c, rangeID, 1, 5, ents, 5, false)
   394  }
   395  
   396  func TestPanicOnNonContiguousRange(t *testing.T) {
   397  	defer leaktest.AfterTest(t)()
   398  	c := NewCache(100)
   399  	defer func() {
   400  		if r := recover(); r == nil {
   401  			t.Errorf("Expected panic with non-contiguous range")
   402  		}
   403  	}()
   404  	c.Add(1, []raftpb.Entry{newEntry(1, 1), newEntry(3, 1)}, true)
   405  }
   406  
   407  func TestEntryCacheEviction(t *testing.T) {
   408  	defer leaktest.AfterTest(t)()
   409  	rangeID, rangeID2 := roachpb.RangeID(1), roachpb.RangeID(2)
   410  	c := NewCache(140 + uint64(partitionSize))
   411  	c.Add(rangeID, []raftpb.Entry{newEntry(1, 40), newEntry(2, 40)}, true)
   412  	ents, _, hi, _ := c.Scan(nil, rangeID, 1, 3, noLimit)
   413  	if len(ents) != 2 || hi != 3 {
   414  		t.Errorf("expected both entries; got %+v, %d", ents, hi)
   415  	}
   416  	if c.entries != 2 {
   417  		t.Errorf("expected size=2; got %d", c.entries)
   418  	}
   419  	// Add another entry to the same range. This will exceed the size limit and
   420  	// lead to eviction.
   421  	c.Add(rangeID, []raftpb.Entry{newEntry(3, 40)}, true)
   422  	ents, _, hi, _ = c.Scan(nil, rangeID, 1, 4, noLimit)
   423  	if len(ents) != 0 || hi != 1 {
   424  		t.Errorf("expected no entries; got %+v, %d", ents, hi)
   425  	}
   426  	if _, ok := c.Get(rangeID, 1); ok {
   427  		t.Errorf("didn't expect to get evicted entry")
   428  	}
   429  	if c.entries != 1 {
   430  		t.Errorf("expected size=1; got %d", c.entries)
   431  	}
   432  	ents, _, hi, _ = c.Scan(nil, rangeID, 3, 4, noLimit)
   433  	if len(ents) != 1 || hi != 4 {
   434  		t.Errorf("expected the new entry; got %+v, %d", ents, hi)
   435  	}
   436  	c.Add(rangeID, []raftpb.Entry{newEntry(3, 1)}, true)
   437  	verifyMetrics(t, c, 1, c.Metrics().Bytes.Value())
   438  	c.Add(rangeID2, []raftpb.Entry{newEntry(20, 1), newEntry(21, 1)}, true)
   439  	ents, _, hi, _ = c.Scan(nil, rangeID2, 20, 22, noLimit)
   440  	if len(ents) != 2 || hi != 22 {
   441  		t.Errorf("expected both entries; got %+v, %d", ents, hi)
   442  	}
   443  	verifyMetrics(t, c, 3, c.Metrics().Bytes.Value())
   444  	// Evict from rangeID by adding more to rangeID 2.
   445  	c.Add(rangeID2, []raftpb.Entry{newEntry(20, 35), newEntry(21, 35)}, true)
   446  	if _, ok := c.Get(rangeID, 3); ok {
   447  		t.Errorf("didn't expect to get evicted entry")
   448  	}
   449  }
   450  
   451  // TestConcurrentUpdates ensures that concurrent updates to the same do not
   452  // race with each other.
   453  func TestConcurrentUpdates(t *testing.T) {
   454  	defer leaktest.AfterTest(t)()
   455  	c := NewCache(10000)
   456  	const r1 roachpb.RangeID = 1
   457  	ents := []raftpb.Entry{newEntry(20, 35), newEntry(21, 35)}
   458  	// Test using both Clear and Drop to remove the added entries.
   459  	for _, clearMethod := range []struct {
   460  		name  string
   461  		clear func()
   462  	}{
   463  		{"drop", func() { c.Drop(r1) }},
   464  		{"clear", func() { c.Clear(r1, ents[len(ents)-1].Index+1) }},
   465  	} {
   466  		t.Run(clearMethod.name, func(t *testing.T) {
   467  			// NB: N is chosen based on the race detector's limit of 8128 goroutines.
   468  			const N = 8000
   469  			var wg sync.WaitGroup
   470  			wg.Add(N)
   471  			for i := 0; i < N; i++ {
   472  				go func(i int) {
   473  					if i%2 == 1 {
   474  						c.Add(r1, ents, true)
   475  					} else {
   476  						clearMethod.clear()
   477  					}
   478  					wg.Done()
   479  				}(i)
   480  			}
   481  			wg.Wait()
   482  			clearMethod.clear()
   483  			// Clear does not evict the partition struct itself so we expect the cache
   484  			// to have a partition's initial byte size when using Clear and nothing
   485  			// when using Drop.
   486  			switch clearMethod.name {
   487  			case "drop":
   488  				verifyMetrics(t, c, 0, 0)
   489  			case "clear":
   490  				verifyMetrics(t, c, 0, int64(initialSize.bytes()))
   491  			}
   492  		})
   493  	}
   494  }
   495  
   496  func TestPartitionList(t *testing.T) {
   497  	var l partitionList
   498  	first := l.pushFront(1)
   499  	l.remove(first)
   500  	if l.back() != nil {
   501  		t.Fatalf("Expected back to be nil after removing the only element")
   502  	}
   503  	defer func() {
   504  		if r := recover(); r == nil {
   505  			t.Fatalf("Expected panic when removing list root")
   506  		}
   507  	}()
   508  	l.remove(&l.root)
   509  }
   510  
   511  // TestConcurrentClearAddGet exercises the case where a partition is
   512  // concurrently written to as well as read and evicted from. Partitions are
   513  // created and added to the cache lazily upon calls to Add. Because of the two
   514  // level locking, a partition may be created and added to the cache before the
   515  // Add call proceeds to lock the partition and the entries to the buffer.
   516  // During this period a concurrent read operation may discover the uninitialized
   517  // empty partition. This test attempts to exercise these scenarios and ensure
   518  // that they are safe.
   519  func TestConcurrentAddGetAndEviction(t *testing.T) {
   520  	defer leaktest.AfterTest(t)()
   521  	const N = 1000
   522  	var wg sync.WaitGroup
   523  	doAction := func(action func()) {
   524  		wg.Add(1)
   525  		go func() {
   526  			defer wg.Done()
   527  			for i := 0; i < N; i++ {
   528  				action()
   529  			}
   530  		}()
   531  	}
   532  	// A cache size of 1000 is chosen relative to the below entry size of 500
   533  	// so that each add operation will lead to the eviction of the other
   534  	// partition.
   535  	c := NewCache(1000)
   536  	ents := []raftpb.Entry{newEntry(1, 500)}
   537  	doAddAndGetToRange := func(rangeID roachpb.RangeID) {
   538  		doAction(func() { c.Add(rangeID, ents, true) })
   539  		doAction(func() { c.Get(rangeID, ents[0].Index) })
   540  	}
   541  	doAddAndGetToRange(1)
   542  	doAddAndGetToRange(2)
   543  	wg.Wait()
   544  }
   545  
   546  func BenchmarkEntryCache(b *testing.B) {
   547  	rangeID := roachpb.RangeID(1)
   548  	ents := make([]raftpb.Entry, 1000)
   549  	for i := range ents {
   550  		ents[i] = newEntry(uint64(i+1), 8)
   551  	}
   552  	b.ResetTimer()
   553  	for i := 0; i < b.N; i++ {
   554  		b.StopTimer()
   555  		c := NewCache(uint64(15 * len(ents) * len(ents[0].Data)))
   556  		for i := roachpb.RangeID(0); i < 10; i++ {
   557  			if i != rangeID {
   558  				c.Add(i, ents, true)
   559  			}
   560  		}
   561  		b.StartTimer()
   562  		c.Add(rangeID, ents, true)
   563  		_, _, _, _ = c.Scan(nil, rangeID, 0, uint64(len(ents)-10), noLimit)
   564  		c.Clear(rangeID, uint64(len(ents)-10))
   565  	}
   566  }
   567  
   568  func BenchmarkEntryCacheClearTo(b *testing.B) {
   569  	rangeID := roachpb.RangeID(1)
   570  	ents := make([]raftpb.Entry, 1000)
   571  	for i := range ents {
   572  		ents[i] = newEntry(uint64(i+1), 8)
   573  	}
   574  	b.ResetTimer()
   575  	for i := 0; i < b.N; i++ {
   576  		b.StopTimer()
   577  		c := NewCache(uint64(10 * len(ents) * len(ents[0].Data)))
   578  		c.Add(rangeID, ents, true)
   579  		b.StartTimer()
   580  		c.Clear(rangeID, uint64(len(ents)-10))
   581  	}
   582  }