feat(cache): add cache options with customizable expiry settings

This commit is contained in:
Chang lue Tsen 2025-05-11 04:39:22 -04:00
parent 9e20b270e7
commit 42a99c4925
3 changed files with 93 additions and 7 deletions

49
pkg/cache/cacheopt.go vendored Normal file
View File

@ -0,0 +1,49 @@
package cache
import "time"
const (
defaultExpiry = time.Hour * 24 * 7
defaultNotFoundExpiry = time.Minute
)
type (
// Options is used to store the cache options.
Options struct {
Expiry time.Duration
NotFoundExpiry time.Duration
}
// Option defines the method to customize an Options.
Option func(o *Options)
)
func newOptions(opts ...Option) Options {
var o Options
for _, opt := range opts {
opt(&o)
}
if o.Expiry <= 0 {
o.Expiry = defaultExpiry
}
if o.NotFoundExpiry <= 0 {
o.NotFoundExpiry = defaultNotFoundExpiry
}
return o
}
// WithExpiry returns a func to customize an Options with given expiry.
func WithExpiry(expiry time.Duration) Option {
return func(o *Options) {
o.Expiry = expiry
}
}
// WithNotFoundExpiry returns a func to customize an Options with given not found expiry.
func WithNotFoundExpiry(expiry time.Duration) Option {
return func(o *Options) {
o.NotFoundExpiry = expiry
}
}

28
pkg/cache/cacheopt_test.go vendored Normal file
View File

@ -0,0 +1,28 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCacheOptions(t *testing.T) {
t.Run("default options", func(t *testing.T) {
o := newOptions()
assert.Equal(t, defaultExpiry, o.Expiry)
assert.Equal(t, defaultNotFoundExpiry, o.NotFoundExpiry)
})
t.Run("with expiry", func(t *testing.T) {
o := newOptions(WithExpiry(time.Second))
assert.Equal(t, time.Second, o.Expiry)
assert.Equal(t, defaultNotFoundExpiry, o.NotFoundExpiry)
})
t.Run("with not found expiry", func(t *testing.T) {
o := newOptions(WithNotFoundExpiry(time.Second))
assert.Equal(t, defaultExpiry, o.Expiry)
assert.Equal(t, time.Second, o.NotFoundExpiry)
})
}

23
pkg/cache/gorm.go vendored
View File

@ -5,12 +5,16 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
"time"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"gorm.io/gorm" "gorm.io/gorm"
) )
var ErrNotFound = redis.Nil var (
// ErrNotFound is the error when cache not found.
ErrNotFound = redis.Nil
)
type ( type (
// ExecCtxFn defines the sql exec method. // ExecCtxFn defines the sql exec method.
@ -23,16 +27,21 @@ type (
QueryCtxFn func(conn *gorm.DB, v interface{}) error QueryCtxFn func(conn *gorm.DB, v interface{}) error
CachedConn struct { CachedConn struct {
db *gorm.DB db *gorm.DB
cache *redis.Client cache *redis.Client
expiry time.Duration
notFoundExpiry time.Duration
} }
) )
// NewConn returns a CachedConn with a redis cluster cache. // NewConn returns a CachedConn with a redis cluster cache.
func NewConn(db *gorm.DB, c *redis.Client) CachedConn { func NewConn(db *gorm.DB, c *redis.Client, opts ...Option) CachedConn {
o := newOptions(opts...)
return CachedConn{ return CachedConn{
db: db, db: db,
cache: c, cache: c,
expiry: o.Expiry,
notFoundExpiry: o.NotFoundExpiry,
} }
} }
@ -65,7 +74,7 @@ func (cc CachedConn) SetCache(key string, v interface{}) error {
return err return err
} }
// set redis key // set redis key
return cc.cache.Set(context.Background(), key, val, 0).Err() return cc.cache.Set(context.Background(), key, val, cc.expiry).Err()
} }
// ExecCtx runs given exec on given keys, and returns execution result. // ExecCtx runs given exec on given keys, and returns execution result.