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 }