Skip to content

Commit

Permalink
feat: Add more metrics (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
viccon authored Jun 11, 2024
1 parent c5285ba commit d956721
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 4 deletions.
6 changes: 3 additions & 3 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ func (c *Client[T]) getShard(key string) *shard[T] {
func (c *Client[T]) get(key string) (value T, exists, ignore, refresh bool) {
shard := c.getShard(key)
val, exists, ignore, refresh := shard.get(key)
c.reportCacheHits(exists)
c.reportCacheHits(exists, ignore, refresh)
return val, exists, ignore, refresh
}

// Get retrieves a single value from the cache.
func (c *Client[T]) Get(key string) (T, bool) {
shard := c.getShard(key)
val, ok, ignore, _ := shard.get(key)
c.reportCacheHits(ok)
val, ok, ignore, refresh := shard.get(key)
c.reportCacheHits(ok, ignore, refresh)
return val, ok && !ignore
}

Expand Down
7 changes: 7 additions & 0 deletions distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ func distributedFetch[V, T any](c *Client[T], key string, fetchFn FetchFn[V]) Fe
// Check if the record is fresh enough to not need a refresh.
if !c.distributedEarlyRefreshes || c.clock.Since(record.CreatedAt) < c.distributedRefreshAfterDuration {
if record.IsMissingRecord {
c.reportDistributedMissingRecord()
return record.Value, ErrNotFound
}
return record.Value, nil
}
c.reportDistributedRefresh()
stale, hasStale = record.Value, true
}

Expand Down Expand Up @@ -179,15 +181,20 @@ func distributedBatchFetch[V, T any](c *Client[T], keyFn KeyFn, fetchFn BatchFet
if !c.distributedEarlyRefreshes || c.clock.Since(record.CreatedAt) < c.distributedRefreshAfterDuration {
// We never wan't to return missing records.
if !record.IsMissingRecord {
c.reportDistributedMissingRecord()
fresh[id] = record.Value
}
continue
}

idsToRefresh = append(idsToRefresh, id)
c.reportDistributedRefresh()

// We never wan't to return missing records.
if !record.IsMissingRecord {
stale[id] = record.Value
} else {
c.reportDistributedMissingRecord()
}
}

Expand Down
40 changes: 39 additions & 1 deletion metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ type MetricsRecorder interface {
CacheHit()
// CacheMiss is called for every key that results in a cache miss.
CacheMiss()
// Refresh is called when a get operation results in a refresh.
Refresh()
// MissingRecord is called every time the cache is asked to
// lookup a key which has been marked as missing.
MissingRecord()
// ForcedEviction is called when the cache reaches its capacity, and has to
// evict keys in order to write a new one.
ForcedEviction()
Expand All @@ -24,6 +29,12 @@ type DistributedMetricsRecorder interface {
DistributedCacheHit()
// DistributedCacheHit is called for every key that results in a cache miss.
DistributedCacheMiss()
// DistributedRefresh is called when we retrieve a record from
// the distributed storage that should be refreshed.
DistributedRefresh()
// DistributetedMissingRecord is called when we retrieve a record from the
// distributed storage that has been marked as a missing record.
DistributedMissingRecord()
// DistributedFallback is called when you are using a distributed storage
// with early refreshes, and the call for a value was supposed to refresh it,
// but the call failed. When that happens, the cache fallbacks to the latest
Expand All @@ -39,6 +50,10 @@ func (d *distributedMetricsRecorder) DistributedCacheHit() {}

func (d *distributedMetricsRecorder) DistributedCacheMiss() {}

func (d *distributedMetricsRecorder) DistributedRefresh() {}

func (d *distributedMetricsRecorder) DistributedMissingRecord() {}

func (d *distributedMetricsRecorder) DistributedFallback() {}

func (s *shard[T]) reportForcedEviction() {
Expand All @@ -56,10 +71,19 @@ func (s *shard[T]) reportEntriesEvicted(n int) {
}

// reportCacheHits is used to report cache hits and misses to the metrics recorder.
func (c *Client[T]) reportCacheHits(cacheHit bool) {
func (c *Client[T]) reportCacheHits(cacheHit, missingRecord, refresh bool) {
if c.metricsRecorder == nil {
return
}

if missingRecord {
c.metricsRecorder.MissingRecord()
}

if refresh {
c.metricsRecorder.Refresh()
}

if !cacheHit {
c.metricsRecorder.CacheMiss()
return
Expand Down Expand Up @@ -92,6 +116,20 @@ func (c *Client[T]) reportDistributedCacheHit(cacheHit bool) {
c.metricsRecorder.DistributedCacheHit()
}

func (c *Client[T]) reportDistributedRefresh() {
if c.metricsRecorder == nil {
return
}
c.metricsRecorder.DistributedRefresh()
}

func (c *Client[T]) reportDistributedMissingRecord() {
if c.metricsRecorder == nil {
return
}
c.metricsRecorder.DistributedMissingRecord()
}

func (c *Client[T]) reportDistributedStaleFallback() {
if c.metricsRecorder == nil {
return
Expand Down
14 changes: 14 additions & 0 deletions sturdyc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ type TestMetricsRecorder struct {
sync.Mutex
cacheHits int
cacheMisses int
refreshes int
missingRecords int
evictions int
forcedEvictions int
evictedEntries int
Expand All @@ -52,6 +54,18 @@ func (r *TestMetricsRecorder) CacheMiss() {
r.cacheMisses++
}

func (r *TestMetricsRecorder) Refresh() {
r.Lock()
defer r.Unlock()
r.refreshes++
}

func (r *TestMetricsRecorder) MissingRecord() {
r.Lock()
defer r.Unlock()
r.missingRecords++
}

func (r *TestMetricsRecorder) ObserveCacheSize(_ func() int) {}

func (r *TestMetricsRecorder) CacheBatchRefreshSize(n int) {
Expand Down

0 comments on commit d956721

Please sign in to comment.