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 }