Skip to content

Commit 6be8b32

Browse files
authored
CBG-5020 Avoid double-counting memory correction (#7900)
1 parent 12666a4 commit 6be8b32

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

db/revision_cache_lru.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -940,13 +940,10 @@ func (rc *LRURevisionCache) evictBasedOffMemoryUsage(ctx context.Context) int64
940940
// list is empty, nothing more to evict but stats are wrong so zero stats and return
941941
base.DebugfCtx(ctx, base.KeyCache, "Revision cache memory stats inconsistent for this shard, resetting to zero")
942942
correctionVal := rc.currMemoryUsage.Value()
943-
// Correct overall memory stat across shards to remove this shards memory usage. We cannot set this
944-
// to 0 as other shards usage is included in this
943+
// Correct overall memory stat across shards to remove this shards memory usage, and set this shard to zero
945944
rc.cacheMemoryBytesStat.Add(-correctionVal)
946-
// Set this shards memory usage to zero. We can set this to 0 given this count is for
947-
// this particular shard.
948945
rc.currMemoryUsage.Set(0)
949-
break
946+
return numItemsRemoved
950947
}
951948
}
952949
revKey := IDAndRev{DocID: value.id, RevID: value.revID, CollectionID: value.collectionID}

db/revision_cache_test.go

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,31 +2447,77 @@ func TestEvictionWhenStaleCVRemoved(t *testing.T) {
24472447
// TestUpdateDeltaRevCacheMemoryStatPanic:
24482448
// - Test will interleave underlying rev cache operations for UpdateDelta process and Remove item process to reproduce a race between
24492449
// threads updating a delta (and that thread updating the cache memory stats) and the underlying value being removed/evicted from the cache
2450-
func TestUpdateDeltaRevCacheMemoryStatPanic(t *testing.T) {
2450+
func TestUpdateDeltaRevCacheMemoryStatPanicSingleEntry(t *testing.T) {
24512451
cacheHitCounter, cacheMissCounter, getDocumentCounter, getRevisionCounter, cacheNumItems, memoryBytesCounted := base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}
24522452
backingStoreMap := CreateTestSingleBackingStoreMap(&testBackingStore{nil, &getDocumentCounter, &getRevisionCounter}, testCollectionID)
24532453
cacheOptions := &RevisionCacheOptions{
24542454
MaxItemCount: 5000,
2455-
MaxBytes: 125,
2455+
MaxBytes: 500,
24562456
}
24572457
cache := NewLRURevisionCache(cacheOptions, backingStoreMap, &cacheHitCounter, &cacheMissCounter, &cacheNumItems, &memoryBytesCounted)
24582458

24592459
firstDelta := bytes.Repeat([]byte("a"), 1000)
24602460
ctx := base.TestCtx(t)
24612461

2462-
// Trigger load into cache
2462+
// Trigger load into cache.
24632463
docRev, err := cache.GetWithRev(ctx, "doc1", "1-abc", testCollectionID, RevCacheIncludeDelta)
24642464
require.NoError(t, err, "Error adding to cache")
24652465

2466-
revCacheDelta := newRevCacheDelta(firstDelta, "1-abc", docRev, false, nil)
2466+
revCacheDelta2 := newRevCacheDelta(firstDelta, "1-abc", docRev, false, nil)
24672467

24682468
// Thread 1: UpdateDelta - start
24692469
value := cache.getValue(ctx, "doc1", "1-abc", testCollectionID, false)
24702470
if value != nil {
24712471
// Thread 2: Remove - start - drop value underneath UpdateDelta thread
24722472
cache.RemoveWithRev(ctx, "doc1", "1-abc", testCollectionID)
24732473
// Thread 2: Remove - end
2474-
outGoingBytes := value.updateDelta(revCacheDelta)
2474+
outGoingBytes := value.updateDelta(revCacheDelta2)
2475+
if outGoingBytes != 0 {
2476+
cache.currMemoryUsage.Add(outGoingBytes)
2477+
cache.cacheMemoryBytesStat.Add(outGoingBytes)
2478+
}
2479+
// check for memory based eviction
2480+
cache.revCacheMemoryBasedEviction(ctx)
2481+
}
2482+
// Thread 1: UpdateDelta - end
2483+
2484+
assert.Equal(t, 0, cache.lruList.Len())
2485+
assert.Equal(t, int64(0), memoryBytesCounted.Value())
2486+
}
2487+
2488+
// TestUpdateDeltaRevCacheMemoryStatPanic:
2489+
// - Test will interleave underlying rev cache operations for UpdateDelta process and Remove item process to reproduce a race between
2490+
// threads updating a delta (and that thread updating the cache memory stats) and the underlying value being removed/evicted from the cache
2491+
func TestUpdateDeltaRevCacheMemoryStatPanicMultipleEntries(t *testing.T) {
2492+
cacheHitCounter, cacheMissCounter, getDocumentCounter, getRevisionCounter, cacheNumItems, memoryBytesCounted := base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}, base.SgwIntStat{}
2493+
backingStoreMap := CreateTestSingleBackingStoreMap(&testBackingStore{nil, &getDocumentCounter, &getRevisionCounter}, testCollectionID)
2494+
cacheOptions := &RevisionCacheOptions{
2495+
MaxItemCount: 5000,
2496+
MaxBytes: 500,
2497+
}
2498+
cache := NewLRURevisionCache(cacheOptions, backingStoreMap, &cacheHitCounter, &cacheMissCounter, &cacheNumItems, &memoryBytesCounted)
2499+
2500+
firstDelta := bytes.Repeat([]byte("a"), 1000)
2501+
ctx := base.TestCtx(t)
2502+
2503+
// Trigger load into cache. Add one revision (doc1) without a delta that's less than 500 bytes, then a
2504+
// revision (doc2) with a delta update that will exceed memory limit
2505+
// when added. Both should be removed and cache memory stats should be zero.
2506+
_, err := cache.GetWithRev(ctx, "doc1", "1-abc", testCollectionID, RevCacheIncludeDelta)
2507+
require.NoError(t, err, "Error adding to cache")
2508+
2509+
docRev2, err := cache.GetWithRev(ctx, "doc2", "1-abc", testCollectionID, RevCacheIncludeDelta)
2510+
require.NoError(t, err, "Error adding to cache")
2511+
2512+
revCacheDelta2 := newRevCacheDelta(firstDelta, "1-abc", docRev2, false, nil)
2513+
2514+
// Thread 1: UpdateDelta - start
2515+
value := cache.getValue(ctx, "doc2", "1-abc", testCollectionID, false)
2516+
if value != nil {
2517+
// Thread 2: Remove - start - drop value underneath UpdateDelta thread
2518+
cache.RemoveWithRev(ctx, "doc2", "1-abc", testCollectionID)
2519+
// Thread 2: Remove - end
2520+
outGoingBytes := value.updateDelta(revCacheDelta2)
24752521
if outGoingBytes != 0 {
24762522
cache.currMemoryUsage.Add(outGoingBytes)
24772523
cache.cacheMemoryBytesStat.Add(outGoingBytes)

0 commit comments

Comments
 (0)