Skip to content

Commit

Permalink
CBG-4032: memory based rev cache implementation (#7040)
Browse files Browse the repository at this point in the history
* CBG-4032: memory based rev cache implementation

* make test pass with default collection + missing return param

* more issues with default collection test

* touch ups on comment + remove unused function

* updated for review

* further tidy up

* lint fix after refector test

* fixes from rebase

* updates based off review + some more refactoring
  • Loading branch information
gregns1 authored and bbrks committed Sep 26, 2024
1 parent 4619e80 commit 82003f7
Show file tree
Hide file tree
Showing 9 changed files with 572 additions and 89 deletions.
7 changes: 7 additions & 0 deletions base/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ type CacheStats struct {
RevisionCacheHits *SgwIntStat `json:"rev_cache_hits"`
// The total number of revision cache misses.
RevisionCacheMisses *SgwIntStat `json:"rev_cache_misses"`
// Total memory used by the rev cache
RevisionCacheTotalMemory *SgwIntStat `json:"revision_cache_total_memory"`
// The current length of the pending skipped sequence slice.
SkippedSeqLen *SgwIntStat `json:"skipped_seq_len"`
// The current capacity of the skipped sequence slice
Expand Down Expand Up @@ -1339,6 +1341,10 @@ func (d *DbStats) initCacheStats() error {
if err != nil {
return err
}
resUtil.RevisionCacheTotalMemory, err = NewIntStat(SubsystemCacheKey, "revision_cache_total_memory", StatUnitNoUnits, RevCacheMemoryDesc, StatAddedVersion3dot2dot1, StatDeprecatedVersionNotDeprecated, StatStabilityCommitted, labelKeys, labelVals, prometheus.GaugeValue, 0)
if err != nil {
return err
}
resUtil.SkippedSeqLen, err = NewIntStat(SubsystemCacheKey, "skipped_seq_len", StatUnitNoUnits, SkippedSeqLengthDesc, StatAddedVersion3dot0dot0, StatDeprecatedVersionNotDeprecated, StatStabilityCommitted, labelKeys, labelVals, prometheus.GaugeValue, 0)
if err != nil {
return err
Expand Down Expand Up @@ -1388,6 +1394,7 @@ func (d *DbStats) unregisterCacheStats() {
prometheus.Unregister(d.CacheStats.RevisionCacheBypass)
prometheus.Unregister(d.CacheStats.RevisionCacheHits)
prometheus.Unregister(d.CacheStats.RevisionCacheMisses)
prometheus.Unregister(d.CacheStats.RevisionCacheTotalMemory)
prometheus.Unregister(d.CacheStats.SkippedSeqLen)
prometheus.Unregister(d.CacheStats.ViewQueries)
}
Expand Down
2 changes: 2 additions & 0 deletions base/stats_descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ const (
RevCacheMissesDesc = "The total number of revision cache misses. This metric can be used to calculate the ratio of revision cache misses: " +
"Rev Cache Miss Ratio = rev_cache_misses / (rev_cache_hits + rev_cache_misses)"

RevCacheMemoryDesc = "The approximation of total memory taken up by rev cache for documents. This is measured by the raw document body, the channels allocated to a document and its revision history."

SkippedSeqLengthDesc = "The current length of the pending skipped sequence slice."

SkippedSeqCapDesc = "The current capacity of the skipped sequence slice."
Expand Down
27 changes: 21 additions & 6 deletions db/database_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,8 +402,13 @@ func TestGetRemovedAsUser(t *testing.T) {
// Manually remove the temporary backup doc from the bucket
// Manually flush the rev cache
// After expiry from the rev cache and removal of doc backup, try again
cacheHitCounter, cacheMissCounter, cacheNumItems := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(DefaultRevisionCacheShardCount, DefaultRevisionCacheSize, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems)
cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStat := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems, db.DatabaseContext.DbStats.Cache().RevisionCacheTotalMemory
cacheOptions := &RevisionCacheOptions{
MaxBytes: 0,
MaxItemCount: DefaultRevisionCacheSize,
ShardCount: DefaultRevisionCacheShardCount,
}
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(cacheOptions, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStat)
err = collection.PurgeOldRevisionJSON(ctx, "doc1", rev2id)
assert.NoError(t, err, "Purge old revision JSON")

Expand Down Expand Up @@ -754,8 +759,13 @@ func TestGetRemoved(t *testing.T) {
// Manually remove the temporary backup doc from the bucket
// Manually flush the rev cache
// After expiry from the rev cache and removal of doc backup, try again
cacheHitCounter, cacheMissCounter, cacheNumItems := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(DefaultRevisionCacheShardCount, DefaultRevisionCacheSize, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems)
cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStat := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems, db.DatabaseContext.DbStats.Cache().RevisionCacheTotalMemory
cacheOptions := &RevisionCacheOptions{
MaxBytes: 0,
MaxItemCount: DefaultRevisionCacheSize,
ShardCount: DefaultRevisionCacheShardCount,
}
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(cacheOptions, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStat)
err = collection.PurgeOldRevisionJSON(ctx, "doc1", rev2id)
assert.NoError(t, err, "Purge old revision JSON")

Expand Down Expand Up @@ -823,8 +833,13 @@ func TestGetRemovedAndDeleted(t *testing.T) {
// Manually remove the temporary backup doc from the bucket
// Manually flush the rev cache
// After expiry from the rev cache and removal of doc backup, try again
cacheHitCounter, cacheMissCounter, cacheNumItems := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(DefaultRevisionCacheShardCount, DefaultRevisionCacheSize, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems)
cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStats := db.DatabaseContext.DbStats.Cache().RevisionCacheHits, db.DatabaseContext.DbStats.Cache().RevisionCacheMisses, db.DatabaseContext.DbStats.Cache().RevisionCacheNumItems, db.DatabaseContext.DbStats.Cache().RevisionCacheTotalMemory
cacheOptions := &RevisionCacheOptions{
MaxBytes: 0,
MaxItemCount: DefaultRevisionCacheSize,
ShardCount: DefaultRevisionCacheShardCount,
}
collection.dbCtx.revisionCache = NewShardedLRURevisionCache(cacheOptions, backingStoreMap, cacheHitCounter, cacheMissCounter, cacheNumItems, memoryCacheStats)
err = collection.PurgeOldRevisionJSON(ctx, "doc1", rev2id)
assert.NoError(t, err, "Purge old revision JSON")

Expand Down
24 changes: 15 additions & 9 deletions db/revision_cache_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,33 @@ func NewRevisionCache(cacheOptions *RevisionCacheOptions, backingStores map[uint
cacheOptions = DefaultRevisionCacheOptions()
}

if cacheOptions.Size == 0 {
if cacheOptions.MaxItemCount == 0 {
bypassStat := cacheStats.RevisionCacheBypass
return NewBypassRevisionCache(backingStores, bypassStat)
}

cacheHitStat := cacheStats.RevisionCacheHits
cacheMissStat := cacheStats.RevisionCacheMisses
cacheNumItemsStat := cacheStats.RevisionCacheNumItems
cacheMemoryStat := cacheStats.RevisionCacheTotalMemory

if cacheOptions.ShardCount > 1 {
return NewShardedLRURevisionCache(cacheOptions.ShardCount, cacheOptions.Size, backingStores, cacheHitStat, cacheMissStat, cacheNumItemsStat)
return NewShardedLRURevisionCache(cacheOptions, backingStores, cacheHitStat, cacheMissStat, cacheNumItemsStat, cacheMemoryStat)
}

return NewLRURevisionCache(cacheOptions.Size, backingStores, cacheHitStat, cacheMissStat, cacheNumItemsStat)
return NewLRURevisionCache(cacheOptions, backingStores, cacheHitStat, cacheMissStat, cacheNumItemsStat, cacheMemoryStat)
}

type RevisionCacheOptions struct {
Size uint32
ShardCount uint16
MaxItemCount uint32
MaxBytes int64
ShardCount uint16
}

func DefaultRevisionCacheOptions() *RevisionCacheOptions {
return &RevisionCacheOptions{
Size: DefaultRevisionCacheSize,
ShardCount: DefaultRevisionCacheShardCount,
MaxItemCount: DefaultRevisionCacheSize,
ShardCount: DefaultRevisionCacheShardCount,
}
}

Expand Down Expand Up @@ -163,7 +165,8 @@ type DocumentRevision struct {
Attachments AttachmentsMeta
Delta *RevisionDelta
Deleted bool
Removed bool // True if the revision is a removal.
Removed bool // True if the revision is a removal.
MemoryBytes int64 // storage of the doc rev bytes measurement, includes size of delta when present too
}

// MutableBody returns a deep copy of the given document revision as a plain body (without any special properties)
Expand Down Expand Up @@ -317,17 +320,20 @@ type RevisionDelta struct {
ToChannels base.Set // Full list of channels for the to revision
RevisionHistory []string // Revision history from parent of ToRevID to source revID, in descending order
ToDeleted bool // Flag if ToRevID is a tombstone
totalDeltaBytes int64 // totalDeltaBytes is the total bytes for channels, revisions and body on the delta itself
}

func newRevCacheDelta(deltaBytes []byte, fromRevID string, toRevision DocumentRevision, deleted bool, toRevAttStorageMeta []AttachmentStorageMeta) RevisionDelta {
return RevisionDelta{
revDelta := RevisionDelta{
ToRevID: toRevision.RevID,
DeltaBytes: deltaBytes,
AttachmentStorageMeta: toRevAttStorageMeta,
ToChannels: toRevision.Channels,
RevisionHistory: toRevision.History.parseAncestorRevisions(fromRevID),
ToDeleted: deleted,
}
revDelta.CalculateDeltaBytes()
return revDelta
}

// This is the RevisionCacheLoaderFunc callback for the context's RevisionCache.
Expand Down
Loading

0 comments on commit 82003f7

Please sign in to comment.