From 6f54afb45ed61a7e38fad9b65beaf23ab203d7fb Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 21 Dec 2022 21:23:10 +0100 Subject: [PATCH] feat: add WithOnCacheMiss option This is similar to WithOnCacheHit. It is also triggered on error (as this is technically a cache miss). This is useful if you want to keep metrics about hit and miss without having another middleware. --- cache.go | 1 + cache_test.go | 23 ++++++++++++++++++++++- examples/options/main.go | 13 ++++++++++--- option.go | 18 +++++++++++++++++- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/cache.go b/cache.go index 11ab064..219d470 100644 --- a/cache.go +++ b/cache.go @@ -88,6 +88,7 @@ func cache( if err != persist.ErrCacheMiss { cfg.logger.Errorf("get cache error: %s, cache key: %s", err, cacheKey) } + cfg.missCacheCallback(c) } // cache miss, then call the backend diff --git a/cache_test.go b/cache_test.go index 85827a9..33f0517 100644 --- a/cache_test.go +++ b/cache_test.go @@ -2,17 +2,18 @@ package cache import ( "fmt" - "github.com/stretchr/testify/require" "math/rand" "net/http" "net/http/httptest" "sync" + "sync/atomic" "testing" "time" "github.com/chenyahui/gin-cache/persist" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func init() { @@ -53,6 +54,26 @@ func TestCacheByRequestPath(t *testing.T) { assert.Equal(t, w1.Code, w2.Code) } +func TestCacheHitMissCallback(t *testing.T) { + var cacheHitCount, cacheMissCount int32 + memoryStore := persist.NewMemoryStore(1 * time.Minute) + cachePathMiddleware := CacheByRequestPath(memoryStore, 3*time.Second, + WithOnHitCache(func(c *gin.Context) { + atomic.AddInt32(&cacheHitCount, 1) + }), + WithOnMissCache(func(c *gin.Context) { + atomic.AddInt32(&cacheMissCount, 1) + }), + ) + + mockHttpRequest(cachePathMiddleware, "/cache?uid=u1", true) + mockHttpRequest(cachePathMiddleware, "/cache?uid=u2", true) + mockHttpRequest(cachePathMiddleware, "/cache?uid=u3", true) + + assert.Equal(t, cacheHitCount, int32(2)) + assert.Equal(t, cacheMissCount, int32(1)) +} + func TestCacheDuration(t *testing.T) { memoryStore := persist.NewMemoryStore(1 * time.Minute) cacheURIMiddleware := CacheByRequestURI(memoryStore, 3*time.Second) diff --git a/examples/options/main.go b/examples/options/main.go index f4838a1..ddf3ae5 100644 --- a/examples/options/main.go +++ b/examples/options/main.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "sync/atomic" + "time" + cache "github.com/chenyahui/gin-cache" "github.com/chenyahui/gin-cache/persist" "github.com/gin-gonic/gin" - "sync/atomic" - "time" ) func main() { @@ -14,7 +15,7 @@ func main() { memoryStore := persist.NewMemoryStore(1 * time.Minute) - var cacheHitCount int32 + var cacheHitCount, cacheMissCount int32 app.GET("/hello", cache.CacheByRequestURI( memoryStore, @@ -22,6 +23,9 @@ func main() { cache.WithOnHitCache(func(c *gin.Context) { atomic.AddInt32(&cacheHitCount, 1) }), + cache.WithOnMissCache(func(c *gin.Context) { + atomic.AddInt32(&cacheMissCount, 1) + }), ), func(c *gin.Context) { c.String(200, "hello world") @@ -31,6 +35,9 @@ func main() { app.GET("/get_hit_count", func(c *gin.Context) { c.String(200, fmt.Sprintf("total hit count: %d", cacheHitCount)) }) + app.GET("/get_miss_count", func(c *gin.Context) { + c.String(200, fmt.Sprintf("total miss count: %d", cacheMissCount)) + }) if err := app.Run(":8080"); err != nil { panic(err) diff --git a/option.go b/option.go index d303bfb..4c4f034 100644 --- a/option.go +++ b/option.go @@ -12,7 +12,8 @@ type Config struct { getCacheStrategyByRequest GetCacheStrategyByRequest - hitCacheCallback OnHitCacheCallback + hitCacheCallback OnHitCacheCallback + missCacheCallback OnMissCacheCallback beforeReplyWithCacheCallback BeforeReplyWithCacheCallback @@ -27,6 +28,7 @@ func newConfigByOpts(opts ...Option) *Config { cfg := &Config{ logger: Discard{}, hitCacheCallback: defaultHitCacheCallback, + missCacheCallback: defaultMissCacheCallback, beforeReplyWithCacheCallback: defaultBeforeReplyWithCacheCallback, shareSingleFlightCallback: defaultShareSingleFlightCallback, } @@ -86,6 +88,20 @@ func WithOnHitCache(cb OnHitCacheCallback) Option { } } +// OnMissCacheCallback define the callback when use cache +type OnMissCacheCallback func(c *gin.Context) + +var defaultMissCacheCallback = func(c *gin.Context) {} + +// WithOnMissCache will be called when cache miss. +func WithOnMissCache(cb OnMissCacheCallback) Option { + return func(c *Config) { + if cb != nil { + c.missCacheCallback = cb + } + } +} + type BeforeReplyWithCacheCallback func(c *gin.Context, cache *ResponseCache) var defaultBeforeReplyWithCacheCallback = func(c *gin.Context, cache *ResponseCache) {}