github.com/thanos-io/thanos@v0.32.5/pkg/store/cache/inmemory_test.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  // Tests out the index cache implementation.
     5  package storecache
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"math"
    12  	"testing"
    13  
    14  	"github.com/go-kit/log"
    15  	"github.com/hashicorp/golang-lru/simplelru"
    16  	"github.com/oklog/ulid"
    17  	"github.com/prometheus/client_golang/prometheus"
    18  	promtest "github.com/prometheus/client_golang/prometheus/testutil"
    19  	"github.com/prometheus/prometheus/model/labels"
    20  	"github.com/prometheus/prometheus/storage"
    21  
    22  	"github.com/efficientgo/core/testutil"
    23  )
    24  
    25  func TestNewInMemoryIndexCache(t *testing.T) {
    26  	// Should return error on invalid YAML config.
    27  	conf := []byte("invalid")
    28  	cache, err := NewInMemoryIndexCache(log.NewNopLogger(), nil, nil, conf)
    29  	testutil.NotOk(t, err)
    30  	testutil.Equals(t, (*InMemoryIndexCache)(nil), cache)
    31  
    32  	// Should instance an in-memory index cache with default config
    33  	// on empty YAML config.
    34  	conf = []byte{}
    35  	cache, err = NewInMemoryIndexCache(log.NewNopLogger(), nil, nil, conf)
    36  	testutil.Ok(t, err)
    37  	testutil.Equals(t, uint64(DefaultInMemoryIndexCacheConfig.MaxSize), cache.maxSizeBytes)
    38  	testutil.Equals(t, uint64(DefaultInMemoryIndexCacheConfig.MaxItemSize), cache.maxItemSizeBytes)
    39  
    40  	// Should instance an in-memory index cache with specified YAML config.s with units.
    41  	conf = []byte(`
    42  max_size: 1MB
    43  max_item_size: 2KB
    44  `)
    45  	cache, err = NewInMemoryIndexCache(log.NewNopLogger(), nil, nil, conf)
    46  	testutil.Ok(t, err)
    47  	testutil.Equals(t, uint64(1024*1024), cache.maxSizeBytes)
    48  	testutil.Equals(t, uint64(2*1024), cache.maxItemSizeBytes)
    49  
    50  	// Should instance an in-memory index cache with specified YAML config.s with units.
    51  	conf = []byte(`
    52  max_size: 2KB
    53  max_item_size: 1MB
    54  `)
    55  	cache, err = NewInMemoryIndexCache(log.NewNopLogger(), nil, nil, conf)
    56  	testutil.NotOk(t, err)
    57  	testutil.Equals(t, (*InMemoryIndexCache)(nil), cache)
    58  	// testutil.Equals(t, uint64(1024*1024), cache.maxSizeBytes)
    59  	// testutil.Equals(t, uint64(2*1024), cache.maxItemSizeBytes)
    60  
    61  	// testutil.Equals(t, uint64(1024*1024), cache.maxItemSizeBytes)
    62  	// testutil.Equals(t, uint64(2*1024), cache.maxSizeBytes)
    63  }
    64  
    65  func TestInMemoryIndexCache_AvoidsDeadlock(t *testing.T) {
    66  	metrics := prometheus.NewRegistry()
    67  	cache, err := NewInMemoryIndexCacheWithConfig(log.NewNopLogger(), nil, metrics, InMemoryIndexCacheConfig{
    68  		MaxItemSize: sliceHeaderSize + 5,
    69  		MaxSize:     sliceHeaderSize + 5,
    70  	})
    71  	testutil.Ok(t, err)
    72  
    73  	l, err := simplelru.NewLRU(math.MaxInt64, func(key, val interface{}) {
    74  		// Hack LRU to simulate broken accounting: evictions do not reduce current size.
    75  		size := cache.curSize
    76  		cache.onEvict(key, val)
    77  		cache.curSize = size
    78  	})
    79  	testutil.Ok(t, err)
    80  	cache.lru = l
    81  
    82  	cache.StorePostings(ulid.MustNew(0, nil), labels.Label{Name: "test2", Value: "1"}, []byte{42, 33, 14, 67, 11})
    83  
    84  	testutil.Equals(t, uint64(sliceHeaderSize+5), cache.curSize)
    85  	testutil.Equals(t, float64(cache.curSize), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
    86  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
    87  
    88  	// This triggers deadlock logic.
    89  	cache.StorePostings(ulid.MustNew(0, nil), labels.Label{Name: "test1", Value: "1"}, []byte{42})
    90  
    91  	testutil.Equals(t, uint64(sliceHeaderSize+1), cache.curSize)
    92  	testutil.Equals(t, float64(cache.curSize), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
    93  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
    94  }
    95  
    96  func TestInMemoryIndexCache_UpdateItem(t *testing.T) {
    97  	const maxSize = 2 * (sliceHeaderSize + 1)
    98  
    99  	var errorLogs []string
   100  	errorLogger := log.LoggerFunc(func(kvs ...interface{}) error {
   101  		var lvl string
   102  		for i := 0; i < len(kvs); i += 2 {
   103  			if kvs[i] == "level" {
   104  				lvl = fmt.Sprint(kvs[i+1])
   105  				break
   106  			}
   107  		}
   108  		if lvl != "error" {
   109  			return nil
   110  		}
   111  		var buf bytes.Buffer
   112  		defer func() { errorLogs = append(errorLogs, buf.String()) }()
   113  		return log.NewLogfmtLogger(&buf).Log(kvs...)
   114  	})
   115  
   116  	metrics := prometheus.NewRegistry()
   117  	cache, err := NewInMemoryIndexCacheWithConfig(log.NewSyncLogger(errorLogger), nil, metrics, InMemoryIndexCacheConfig{
   118  		MaxItemSize: maxSize,
   119  		MaxSize:     maxSize,
   120  	})
   121  	testutil.Ok(t, err)
   122  
   123  	uid := func(id storage.SeriesRef) ulid.ULID { return ulid.MustNew(uint64(id), nil) }
   124  	lbl := labels.Label{Name: "foo", Value: "bar"}
   125  	matcher := labels.MustNewMatcher(labels.MatchEqual, "foo", "bar")
   126  	ctx := context.Background()
   127  
   128  	for _, tt := range []struct {
   129  		typ string
   130  		set func(storage.SeriesRef, []byte)
   131  		get func(storage.SeriesRef) ([]byte, bool)
   132  	}{
   133  		{
   134  			typ: cacheTypePostings,
   135  			set: func(id storage.SeriesRef, b []byte) { cache.StorePostings(uid(id), lbl, b) },
   136  			get: func(id storage.SeriesRef) ([]byte, bool) {
   137  				hits, _ := cache.FetchMultiPostings(ctx, uid(id), []labels.Label{lbl})
   138  				b, ok := hits[lbl]
   139  
   140  				return b, ok
   141  			},
   142  		},
   143  		{
   144  			typ: cacheTypeSeries,
   145  			set: func(id storage.SeriesRef, b []byte) { cache.StoreSeries(uid(id), id, b) },
   146  			get: func(id storage.SeriesRef) ([]byte, bool) {
   147  				hits, _ := cache.FetchMultiSeries(ctx, uid(id), []storage.SeriesRef{id})
   148  				b, ok := hits[id]
   149  
   150  				return b, ok
   151  			},
   152  		},
   153  		{
   154  			typ: cacheTypeExpandedPostings,
   155  			set: func(id storage.SeriesRef, b []byte) {
   156  				cache.StoreExpandedPostings(uid(id), []*labels.Matcher{matcher}, b)
   157  			},
   158  			get: func(id storage.SeriesRef) ([]byte, bool) {
   159  				return cache.FetchExpandedPostings(ctx, uid(id), []*labels.Matcher{matcher})
   160  			},
   161  		},
   162  	} {
   163  		t.Run(tt.typ, func(t *testing.T) {
   164  			defer func() { errorLogs = nil }()
   165  
   166  			// Set value.
   167  			tt.set(0, []byte{0})
   168  			buf, ok := tt.get(0)
   169  			testutil.Equals(t, true, ok)
   170  			testutil.Equals(t, []byte{0}, buf)
   171  			testutil.Equals(t, float64(sliceHeaderSize+1), promtest.ToFloat64(cache.currentSize.WithLabelValues(tt.typ)))
   172  			testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(tt.typ)))
   173  			testutil.Equals(t, []string(nil), errorLogs)
   174  
   175  			// Set the same value again.
   176  			// NB: This used to over-count the value.
   177  			tt.set(0, []byte{0})
   178  			buf, ok = tt.get(0)
   179  			testutil.Equals(t, true, ok)
   180  			testutil.Equals(t, []byte{0}, buf)
   181  			testutil.Equals(t, float64(sliceHeaderSize+1), promtest.ToFloat64(cache.currentSize.WithLabelValues(tt.typ)))
   182  			testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(tt.typ)))
   183  			testutil.Equals(t, []string(nil), errorLogs)
   184  
   185  			// Set a larger value.
   186  			// NB: This used to deadlock when enough values were over-counted and it
   187  			// couldn't clear enough space -- repeatedly removing oldest after empty.
   188  			tt.set(1, []byte{0, 1})
   189  			buf, ok = tt.get(1)
   190  			testutil.Equals(t, true, ok)
   191  			testutil.Equals(t, []byte{0, 1}, buf)
   192  			testutil.Equals(t, float64(sliceHeaderSize+2), promtest.ToFloat64(cache.currentSize.WithLabelValues(tt.typ)))
   193  			testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(tt.typ)))
   194  			testutil.Equals(t, []string(nil), errorLogs)
   195  
   196  			// Mutations to existing values will be ignored.
   197  			tt.set(1, []byte{1, 2})
   198  			buf, ok = tt.get(1)
   199  			testutil.Equals(t, true, ok)
   200  			testutil.Equals(t, []byte{0, 1}, buf)
   201  			testutil.Equals(t, float64(sliceHeaderSize+2), promtest.ToFloat64(cache.currentSize.WithLabelValues(tt.typ)))
   202  			testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(tt.typ)))
   203  			testutil.Equals(t, []string(nil), errorLogs)
   204  		})
   205  	}
   206  }
   207  
   208  // This should not happen as we hardcode math.MaxInt, but we still add test to check this out.
   209  func TestInMemoryIndexCache_MaxNumberOfItemsHit(t *testing.T) {
   210  	metrics := prometheus.NewRegistry()
   211  	cache, err := NewInMemoryIndexCacheWithConfig(log.NewNopLogger(), nil, metrics, InMemoryIndexCacheConfig{
   212  		MaxItemSize: 2*sliceHeaderSize + 10,
   213  		MaxSize:     2*sliceHeaderSize + 10,
   214  	})
   215  	testutil.Ok(t, err)
   216  
   217  	l, err := simplelru.NewLRU(2, cache.onEvict)
   218  	testutil.Ok(t, err)
   219  	cache.lru = l
   220  
   221  	id := ulid.MustNew(0, nil)
   222  
   223  	cache.StorePostings(id, labels.Label{Name: "test", Value: "123"}, []byte{42, 33})
   224  	cache.StorePostings(id, labels.Label{Name: "test", Value: "124"}, []byte{42, 33})
   225  	cache.StorePostings(id, labels.Label{Name: "test", Value: "125"}, []byte{42, 33})
   226  
   227  	testutil.Equals(t, uint64(2*sliceHeaderSize+4), cache.curSize)
   228  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   229  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   230  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   231  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   232  	testutil.Equals(t, float64(3), promtest.ToFloat64(cache.added.WithLabelValues(cacheTypePostings)))
   233  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.added.WithLabelValues(cacheTypeSeries)))
   234  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.commonMetrics.requestTotal.WithLabelValues(cacheTypePostings)))
   235  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.commonMetrics.requestTotal.WithLabelValues(cacheTypeSeries)))
   236  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.commonMetrics.hitsTotal.WithLabelValues(cacheTypePostings)))
   237  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.commonMetrics.hitsTotal.WithLabelValues(cacheTypeSeries)))
   238  }
   239  
   240  func TestInMemoryIndexCache_Eviction_WithMetrics(t *testing.T) {
   241  	metrics := prometheus.NewRegistry()
   242  	cache, err := NewInMemoryIndexCacheWithConfig(log.NewNopLogger(), nil, metrics, InMemoryIndexCacheConfig{
   243  		MaxItemSize: 2*sliceHeaderSize + 5,
   244  		MaxSize:     2*sliceHeaderSize + 5,
   245  	})
   246  	testutil.Ok(t, err)
   247  
   248  	id := ulid.MustNew(0, nil)
   249  	lbls := labels.Label{Name: "test", Value: "123"}
   250  	ctx := context.Background()
   251  	emptyPostingsHits := map[labels.Label][]byte{}
   252  	emptyPostingsMisses := []labels.Label(nil)
   253  	emptySeriesHits := map[storage.SeriesRef][]byte{}
   254  	emptySeriesMisses := []storage.SeriesRef(nil)
   255  
   256  	pHits, pMisses := cache.FetchMultiPostings(ctx, id, []labels.Label{lbls})
   257  	testutil.Equals(t, emptyPostingsHits, pHits, "no such key")
   258  	testutil.Equals(t, []labels.Label{lbls}, pMisses)
   259  
   260  	// Add sliceHeaderSize + 2 bytes.
   261  	cache.StorePostings(id, lbls, []byte{42, 33})
   262  	testutil.Equals(t, uint64(sliceHeaderSize+2), cache.curSize)
   263  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   264  	testutil.Equals(t, float64(sliceHeaderSize+2), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   265  	testutil.Equals(t, float64(sliceHeaderSize+2+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   266  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   267  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   268  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   269  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   270  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   271  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   272  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   273  
   274  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls})
   275  	testutil.Equals(t, map[labels.Label][]byte{lbls: {42, 33}}, pHits, "key exists")
   276  	testutil.Equals(t, emptyPostingsMisses, pMisses)
   277  
   278  	pHits, pMisses = cache.FetchMultiPostings(ctx, ulid.MustNew(1, nil), []labels.Label{lbls})
   279  	testutil.Equals(t, emptyPostingsHits, pHits, "no such key")
   280  	testutil.Equals(t, []labels.Label{lbls}, pMisses)
   281  
   282  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{{Name: "test", Value: "124"}})
   283  	testutil.Equals(t, emptyPostingsHits, pHits, "no such key")
   284  	testutil.Equals(t, []labels.Label{{Name: "test", Value: "124"}}, pMisses)
   285  
   286  	// Add sliceHeaderSize + 3 more bytes.
   287  	cache.StoreSeries(id, 1234, []byte{222, 223, 224})
   288  	testutil.Equals(t, uint64(2*sliceHeaderSize+5), cache.curSize)
   289  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   290  	testutil.Equals(t, float64(sliceHeaderSize+2), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   291  	testutil.Equals(t, float64(sliceHeaderSize+2+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   292  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   293  	testutil.Equals(t, float64(sliceHeaderSize+3), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   294  	testutil.Equals(t, float64(sliceHeaderSize+3+24), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   295  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   296  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   297  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   298  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   299  
   300  	sHits, sMisses := cache.FetchMultiSeries(ctx, id, []storage.SeriesRef{1234})
   301  	testutil.Equals(t, map[storage.SeriesRef][]byte{1234: {222, 223, 224}}, sHits, "key exists")
   302  	testutil.Equals(t, emptySeriesMisses, sMisses)
   303  
   304  	lbls2 := labels.Label{Name: "test", Value: "124"}
   305  
   306  	// Add sliceHeaderSize + 5 + 16 bytes, should fully evict 2 last items.
   307  	v := []byte{42, 33, 14, 67, 11}
   308  	for i := 0; i < sliceHeaderSize; i++ {
   309  		v = append(v, 3)
   310  	}
   311  	cache.StorePostings(id, lbls2, v)
   312  
   313  	testutil.Equals(t, uint64(2*sliceHeaderSize+5), cache.curSize)
   314  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   315  	testutil.Equals(t, float64(2*sliceHeaderSize+5), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   316  	testutil.Equals(t, float64(2*sliceHeaderSize+5+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   317  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   318  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   319  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   320  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   321  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   322  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings))) // Eviction.
   323  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))   // Eviction.
   324  
   325  	// Evicted.
   326  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls})
   327  	testutil.Equals(t, emptyPostingsHits, pHits, "no such key")
   328  	testutil.Equals(t, []labels.Label{lbls}, pMisses)
   329  
   330  	sHits, sMisses = cache.FetchMultiSeries(ctx, id, []storage.SeriesRef{1234})
   331  	testutil.Equals(t, emptySeriesHits, sHits, "no such key")
   332  	testutil.Equals(t, []storage.SeriesRef{1234}, sMisses)
   333  
   334  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls2})
   335  	testutil.Equals(t, map[labels.Label][]byte{lbls2: v}, pHits)
   336  	testutil.Equals(t, emptyPostingsMisses, pMisses)
   337  
   338  	// Add same item again.
   339  	cache.StorePostings(id, lbls2, v)
   340  
   341  	testutil.Equals(t, uint64(2*sliceHeaderSize+5), cache.curSize)
   342  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   343  	testutil.Equals(t, float64(2*sliceHeaderSize+5), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   344  	testutil.Equals(t, float64(2*sliceHeaderSize+5+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   345  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   346  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   347  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   348  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   349  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   350  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   351  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   352  
   353  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls2})
   354  	testutil.Equals(t, map[labels.Label][]byte{lbls2: v}, pHits)
   355  	testutil.Equals(t, emptyPostingsMisses, pMisses)
   356  
   357  	// Add too big item.
   358  	cache.StorePostings(id, labels.Label{Name: "test", Value: "toobig"}, append(v, 5))
   359  	testutil.Equals(t, uint64(2*sliceHeaderSize+5), cache.curSize)
   360  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   361  	testutil.Equals(t, float64(2*sliceHeaderSize+5), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   362  	testutil.Equals(t, float64(2*sliceHeaderSize+5+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   363  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   364  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   365  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   366  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings))) // Overflow.
   367  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   368  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   369  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   370  
   371  	_, _, ok := cache.lru.RemoveOldest()
   372  	testutil.Assert(t, ok, "something to remove")
   373  
   374  	testutil.Equals(t, uint64(0), cache.curSize)
   375  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   376  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   377  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   378  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   379  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   380  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   381  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   382  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   383  	testutil.Equals(t, float64(2), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   384  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   385  
   386  	_, _, ok = cache.lru.RemoveOldest()
   387  	testutil.Assert(t, !ok, "nothing to remove")
   388  
   389  	lbls3 := labels.Label{Name: "test", Value: "124"}
   390  
   391  	cache.StorePostings(id, lbls3, []byte{})
   392  
   393  	testutil.Equals(t, uint64(sliceHeaderSize), cache.curSize)
   394  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   395  	testutil.Equals(t, float64(sliceHeaderSize), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   396  	testutil.Equals(t, float64(sliceHeaderSize+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   397  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   398  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   399  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   400  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   401  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   402  	testutil.Equals(t, float64(2), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   403  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   404  
   405  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls3})
   406  	testutil.Equals(t, map[labels.Label][]byte{lbls3: {}}, pHits, "key exists")
   407  	testutil.Equals(t, emptyPostingsMisses, pMisses)
   408  
   409  	// nil works and still allocates empty slice.
   410  	lbls4 := labels.Label{Name: "test", Value: "125"}
   411  	cache.StorePostings(id, lbls4, []byte(nil))
   412  
   413  	testutil.Equals(t, 2*uint64(sliceHeaderSize), cache.curSize)
   414  	testutil.Equals(t, float64(2), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypePostings)))
   415  	testutil.Equals(t, 2*float64(sliceHeaderSize), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypePostings)))
   416  	testutil.Equals(t, 2*float64(sliceHeaderSize+55), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypePostings)))
   417  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.current.WithLabelValues(cacheTypeSeries)))
   418  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.currentSize.WithLabelValues(cacheTypeSeries)))
   419  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.totalCurrentSize.WithLabelValues(cacheTypeSeries)))
   420  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypePostings)))
   421  	testutil.Equals(t, float64(0), promtest.ToFloat64(cache.overflow.WithLabelValues(cacheTypeSeries)))
   422  	testutil.Equals(t, float64(2), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypePostings)))
   423  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.evicted.WithLabelValues(cacheTypeSeries)))
   424  
   425  	pHits, pMisses = cache.FetchMultiPostings(ctx, id, []labels.Label{lbls4})
   426  	testutil.Equals(t, map[labels.Label][]byte{lbls4: {}}, pHits, "key exists")
   427  	testutil.Equals(t, emptyPostingsMisses, pMisses)
   428  
   429  	// Other metrics.
   430  	testutil.Equals(t, float64(4), promtest.ToFloat64(cache.added.WithLabelValues(cacheTypePostings)))
   431  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.added.WithLabelValues(cacheTypeSeries)))
   432  	testutil.Equals(t, float64(9), promtest.ToFloat64(cache.commonMetrics.requestTotal.WithLabelValues(cacheTypePostings)))
   433  	testutil.Equals(t, float64(2), promtest.ToFloat64(cache.commonMetrics.requestTotal.WithLabelValues(cacheTypeSeries)))
   434  	testutil.Equals(t, float64(5), promtest.ToFloat64(cache.commonMetrics.hitsTotal.WithLabelValues(cacheTypePostings)))
   435  	testutil.Equals(t, float64(1), promtest.ToFloat64(cache.commonMetrics.hitsTotal.WithLabelValues(cacheTypeSeries)))
   436  }