@@ -97,6 +97,10 @@ func (sc *ShardedLRURevisionCache) RemoveRevOnly(ctx context.Context, docID, rev
9797 sc .getShard (docID ).RemoveRevOnly (ctx , docID , revID , collectionID )
9898}
9999
100+ func (sc * ShardedLRURevisionCache ) RemoveCVOnly (ctx context.Context , docID string , cv * Version , collectionID uint32 ) {
101+ sc .getShard (docID ).RemoveCVOnly (ctx , docID , cv , collectionID )
102+ }
103+
100104// An LRU cache of document revision bodies, together with their channel access.
101105type LRURevisionCache struct {
102106 backingStores map [uint32 ]RevisionCacheBackingStore
@@ -533,6 +537,10 @@ func (rc *LRURevisionCache) RemoveWithCV(ctx context.Context, docID string, cv *
533537 rc .removeFromCacheByCV (ctx , docID , cv , collectionID )
534538}
535539
540+ func (rc * LRURevisionCache ) RemoveCVOnly (ctx context.Context , docID string , cv * Version , collectionID uint32 ) {
541+ rc .removeFromCVLookup (ctx , docID , cv , collectionID )
542+ }
543+
536544// RemoveRevOnly removes a rev from revision cache lookup map, if present.
537545func (rc * LRURevisionCache ) RemoveRevOnly (ctx context.Context , docID , revID string , collectionID uint32 ) {
538546 // This will only remove the entry from the rev lookup map, not the lru list
@@ -587,11 +595,21 @@ func (rc *LRURevisionCache) removeFromRevLookup(ctx context.Context, docID, revI
587595 key := IDAndRev {DocID : docID , RevID : revID , CollectionID : collectionID }
588596 rc .lock .Lock ()
589597 defer rc .lock .Unlock ()
590- // only delete from rev lookup map, if we delete underlying element in list, the now elem in the HLV lookup map
598+ // only delete from rev lookup map, if we delete underlying element in list, the elem in the HLV lookup map
591599 // will never be evicted leading to potential unbounded growth of HLV lookup map
592600 delete (rc .cache , key )
593601}
594602
603+ // removeFromCVLookup will only remove the entry from the CV lookup map, if present. Underlying element must stay in list for eviction to work.
604+ func (rc * LRURevisionCache ) removeFromCVLookup (ctx context.Context , docID string , cv * Version , collectionID uint32 ) {
605+ key := IDandCV {DocID : docID , Source : cv .SourceID , Version : cv .Value , CollectionID : collectionID }
606+ rc .lock .Lock ()
607+ defer rc .lock .Unlock ()
608+ // only delete from cv lookup map, if we delete underlying element in list, the item left in the revID lookup map for
609+ // this item will never be evicted leading to potential unbounded growth of revID lookup map
610+ delete (rc .hlvCache , key )
611+ }
612+
595613// removeValue removes a value from the revision cache, if present and the value matches the the value. If there's an item in the revision cache with a matching docID and revID but the document is different, this item will not be removed from the rev cache.
596614func (rc * LRURevisionCache ) removeValue (value * revCacheValue ) {
597615 rc .lock .Lock ()
@@ -627,7 +645,16 @@ func (rc *LRURevisionCache) _numberCapacityEviction() (numItemsEvicted int64, nu
627645 }
628646 hlvKey := IDandCV {DocID : value .id , Source : value .cv .SourceID , Version : value .cv .Value , CollectionID : value .collectionID }
629647 revKey := IDAndRev {DocID : value .id , RevID : value .revID , CollectionID : value .collectionID }
630- delete (rc .hlvCache , hlvKey )
648+ if elem := rc .hlvCache [hlvKey ]; elem != nil {
649+ revValue := elem .Value .(* revCacheValue )
650+ // we need to check if the value pointed to by the cv lookup map is the same value we're evicting, this is
651+ // because we can currently have two items with the same docID and CV, but different revIDs due to
652+ // local wins conflict resolution not generating a new CV but generating a new revID.
653+ if revValue .revID == value .revID {
654+ // this cv lookup item matches the value we're evicting, so remove it
655+ delete (rc .hlvCache , hlvKey )
656+ }
657+ }
631658 if elem := rc .cache [revKey ]; elem != nil {
632659 revValue := elem .Value .(* revCacheValue )
633660 // we need to check if the value pointed to by the rev lookup map is the same value we're evicting, this is
@@ -888,7 +915,17 @@ func (rc *LRURevisionCache) performEviction(ctx context.Context) {
888915 }
889916 revKey := IDAndRev {DocID : value .id , RevID : value .revID , CollectionID : value .collectionID }
890917 hlvKey := IDandCV {DocID : value .id , Source : value .cv .SourceID , Version : value .cv .Value , CollectionID : value .collectionID }
891- delete (rc .hlvCache , hlvKey )
918+ // same below but for hlv lookup map
919+ if elem := rc .hlvCache [hlvKey ]; elem != nil {
920+ revValue := elem .Value .(* revCacheValue )
921+ // we need to check if the value pointed to by the cv lookup map is the same value we're evicting, this is
922+ // because we can currently have two items with the same docID and CV, but different revIDs due to
923+ // local wins conflict resolution not generating a new CV but generating a new revID.
924+ if revValue .revID == value .revID {
925+ // this cv lookup item matches the value we're evicting, so remove it
926+ delete (rc .hlvCache , hlvKey )
927+ }
928+ }
892929 if elem := rc .cache [revKey ]; elem != nil {
893930 revValue := elem .Value .(* revCacheValue )
894931 // we need to check if the value pointed to by the rev lookup map is the same value we're evicting, this is
0 commit comments