github.com/thanos-io/thanos@v0.32.5/internal/cortex/chunk/cache/fifo_cache_test.go (about)

     1  // Copyright (c) The Cortex Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package cache
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strconv"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/go-kit/log"
    14  	"github.com/prometheus/client_golang/prometheus/testutil"
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestFifoCacheEviction(t *testing.T) {
    20  	const (
    21  		cnt     = 10
    22  		evicted = 5
    23  	)
    24  	itemTemplate := &cacheEntry{
    25  		key:   "00",
    26  		value: []byte("00"),
    27  	}
    28  
    29  	tests := []struct {
    30  		name string
    31  		cfg  FifoCacheConfig
    32  	}{
    33  		{
    34  			name: "test-memory-eviction",
    35  			cfg:  FifoCacheConfig{MaxSizeBytes: strconv.FormatInt(int64(cnt*sizeOf(itemTemplate)), 10), Validity: 1 * time.Minute},
    36  		},
    37  		{
    38  			name: "test-items-eviction",
    39  			cfg:  FifoCacheConfig{MaxSizeItems: cnt, Validity: 1 * time.Minute},
    40  		},
    41  	}
    42  
    43  	for _, test := range tests {
    44  		c := NewFifoCache(test.name, test.cfg, nil, log.NewNopLogger())
    45  		ctx := context.Background()
    46  
    47  		// Check put / get works
    48  		keys := []string{}
    49  		values := [][]byte{}
    50  		for i := 0; i < cnt; i++ {
    51  			key := fmt.Sprintf("%02d", i)
    52  			value := make([]byte, len(key))
    53  			copy(value, key)
    54  			keys = append(keys, key)
    55  			values = append(values, value)
    56  		}
    57  		c.Store(ctx, keys, values)
    58  		require.Len(t, c.entries, cnt)
    59  
    60  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(1))
    61  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(cnt))
    62  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(0))
    63  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(cnt))
    64  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
    65  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
    66  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(0))
    67  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(0))
    68  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
    69  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(cnt*sizeOf(itemTemplate)))
    70  
    71  		for i := 0; i < cnt; i++ {
    72  			key := fmt.Sprintf("%02d", i)
    73  			value, ok := c.Get(ctx, key)
    74  			require.True(t, ok)
    75  			require.Equal(t, []byte(key), value)
    76  		}
    77  
    78  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(1))
    79  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(cnt))
    80  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(0))
    81  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(cnt))
    82  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
    83  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
    84  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(cnt))
    85  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(0))
    86  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
    87  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(cnt*sizeOf(itemTemplate)))
    88  
    89  		// Check evictions
    90  		keys = []string{}
    91  		values = [][]byte{}
    92  		for i := cnt - evicted; i < cnt+evicted; i++ {
    93  			key := fmt.Sprintf("%02d", i)
    94  			value := make([]byte, len(key))
    95  			copy(value, key)
    96  			keys = append(keys, key)
    97  			values = append(values, value)
    98  		}
    99  		c.Store(ctx, keys, values)
   100  		require.Len(t, c.entries, cnt)
   101  
   102  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(2))
   103  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(cnt+evicted))
   104  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(evicted))
   105  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(cnt))
   106  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
   107  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
   108  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(cnt))
   109  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(0))
   110  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
   111  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(cnt*sizeOf(itemTemplate)))
   112  
   113  		for i := 0; i < cnt-evicted; i++ {
   114  			_, ok := c.Get(ctx, fmt.Sprintf("%02d", i))
   115  			require.False(t, ok)
   116  		}
   117  		for i := cnt - evicted; i < cnt+evicted; i++ {
   118  			key := fmt.Sprintf("%02d", i)
   119  			value, ok := c.Get(ctx, key)
   120  			require.True(t, ok)
   121  			require.Equal(t, []byte(key), value)
   122  		}
   123  
   124  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(2))
   125  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(cnt+evicted))
   126  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(evicted))
   127  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(cnt))
   128  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
   129  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
   130  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(cnt*2+evicted))
   131  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(cnt-evicted))
   132  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
   133  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(cnt*sizeOf(itemTemplate)))
   134  
   135  		// Check updates work
   136  		keys = []string{}
   137  		values = [][]byte{}
   138  		for i := cnt; i < cnt+evicted; i++ {
   139  			keys = append(keys, fmt.Sprintf("%02d", i))
   140  			vstr := fmt.Sprintf("%02d", i*2)
   141  			value := make([]byte, len(vstr))
   142  			copy(value, vstr)
   143  			values = append(values, value)
   144  		}
   145  		c.Store(ctx, keys, values)
   146  		require.Len(t, c.entries, cnt)
   147  
   148  		for i := cnt; i < cnt+evicted; i++ {
   149  			value, ok := c.Get(ctx, fmt.Sprintf("%02d", i))
   150  			require.True(t, ok)
   151  			require.Equal(t, []byte(fmt.Sprintf("%02d", i*2)), value)
   152  		}
   153  
   154  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(3))
   155  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(cnt+evicted))
   156  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(evicted))
   157  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(cnt))
   158  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
   159  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
   160  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(cnt*2+evicted*2))
   161  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(cnt-evicted))
   162  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
   163  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(cnt*sizeOf(itemTemplate)))
   164  
   165  		c.Stop()
   166  	}
   167  }
   168  
   169  func TestFifoCacheExpiry(t *testing.T) {
   170  	key1, key2, key3, key4 := "01", "02", "03", "04"
   171  	data1, data2, data3 := genBytes(24), []byte("testdata"), genBytes(8)
   172  
   173  	memorySz := sizeOf(&cacheEntry{key: key1, value: data1}) +
   174  		sizeOf(&cacheEntry{key: key2, value: data2}) +
   175  		sizeOf(&cacheEntry{key: key3, value: data3})
   176  
   177  	tests := []struct {
   178  		name string
   179  		cfg  FifoCacheConfig
   180  	}{
   181  		{
   182  			name: "test-memory-expiry",
   183  			cfg:  FifoCacheConfig{MaxSizeBytes: strconv.FormatInt(int64(memorySz), 10), Validity: 5 * time.Millisecond},
   184  		},
   185  		{
   186  			name: "test-items-expiry",
   187  			cfg:  FifoCacheConfig{MaxSizeItems: 3, Validity: 5 * time.Millisecond},
   188  		},
   189  	}
   190  
   191  	for _, test := range tests {
   192  		c := NewFifoCache(test.name, test.cfg, nil, log.NewNopLogger())
   193  		ctx := context.Background()
   194  
   195  		c.Store(ctx,
   196  			[]string{key1, key2, key4, key3, key2, key1},
   197  			[][]byte{genBytes(16), []byte("dummy"), genBytes(20), data3, data2, data1})
   198  
   199  		value, ok := c.Get(ctx, key1)
   200  		require.True(t, ok)
   201  		require.Equal(t, data1, value)
   202  
   203  		_, ok = c.Get(ctx, key4)
   204  		require.False(t, ok)
   205  
   206  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(1))
   207  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(5))
   208  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(2))
   209  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(3))
   210  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
   211  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
   212  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(2))
   213  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(1))
   214  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(0))
   215  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(memorySz))
   216  
   217  		// Expire the item.
   218  		time.Sleep(5 * time.Millisecond)
   219  		_, ok = c.Get(ctx, key1)
   220  		require.False(t, ok)
   221  
   222  		assert.Equal(t, testutil.ToFloat64(c.entriesAdded), float64(1))
   223  		assert.Equal(t, testutil.ToFloat64(c.entriesAddedNew), float64(5))
   224  		assert.Equal(t, testutil.ToFloat64(c.entriesEvicted), float64(2))
   225  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(3))
   226  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(len(c.entries)))
   227  		assert.Equal(t, testutil.ToFloat64(c.entriesCurrent), float64(c.lru.Len()))
   228  		assert.Equal(t, testutil.ToFloat64(c.totalGets), float64(3))
   229  		assert.Equal(t, testutil.ToFloat64(c.totalMisses), float64(2))
   230  		assert.Equal(t, testutil.ToFloat64(c.staleGets), float64(1))
   231  		assert.Equal(t, testutil.ToFloat64(c.memoryBytes), float64(memorySz))
   232  
   233  		c.Stop()
   234  	}
   235  }
   236  
   237  func genBytes(n uint8) []byte {
   238  	arr := make([]byte, n)
   239  	for i := range arr {
   240  		arr[i] = byte(i)
   241  	}
   242  	return arr
   243  }
   244  
   245  func TestBytesParsing(t *testing.T) {
   246  	tests := []struct {
   247  		input    string
   248  		expected uint64
   249  	}{
   250  		{input: "", expected: 0},
   251  		{input: "123", expected: 123},
   252  		{input: "1234567890", expected: 1234567890},
   253  		{input: "25k", expected: 25000},
   254  		{input: "25K", expected: 25000},
   255  		{input: "25kb", expected: 25000},
   256  		{input: "25kB", expected: 25000},
   257  		{input: "25Kb", expected: 25000},
   258  		{input: "25KB", expected: 25000},
   259  		{input: "25kib", expected: 25600},
   260  		{input: "25KiB", expected: 25600},
   261  		{input: "25m", expected: 25000000},
   262  		{input: "25M", expected: 25000000},
   263  		{input: "25mB", expected: 25000000},
   264  		{input: "25MB", expected: 25000000},
   265  		{input: "2.5MB", expected: 2500000},
   266  		{input: "25MiB", expected: 26214400},
   267  		{input: "25mib", expected: 26214400},
   268  		{input: "2.5mib", expected: 2621440},
   269  		{input: "25g", expected: 25000000000},
   270  		{input: "25G", expected: 25000000000},
   271  		{input: "25gB", expected: 25000000000},
   272  		{input: "25Gb", expected: 25000000000},
   273  		{input: "25GiB", expected: 26843545600},
   274  		{input: "25gib", expected: 26843545600},
   275  	}
   276  	for _, test := range tests {
   277  		output, err := parsebytes(test.input)
   278  		assert.Nil(t, err)
   279  		assert.Equal(t, test.expected, output)
   280  	}
   281  }