Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CBG-4032: memory based rev cache implementation #7040

Merged
merged 9 commits into from
Aug 29, 2024
Merged
7 changes: 7 additions & 0 deletions base/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,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"`
gregns1 marked this conversation as resolved.
Show resolved Hide resolved
// 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 @@ -1373,6 +1375,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 @@ -1422,6 +1428,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 @@ -136,6 +136,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
Loading