diff --git a/cache/cache.go b/cache/cache.go index 1b807ea0..9a50f7bc 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -7,7 +7,7 @@ import ( var globalCache Cache -type SetValueFunc func() (interface{}, time.Duration, error) +type SetValueFunc func() (interface{}, error) type Cache interface { // Has 检测缓存是否存在 diff --git a/cache/redis/cache.go b/cache/redis/cache.go index e4677fc0..cbb34776 100644 --- a/cache/redis/cache.go +++ b/cache/redis/cache.go @@ -5,9 +5,10 @@ import ( "github.com/dobyte/due/v2/cache" "github.com/dobyte/due/v2/errors" "github.com/dobyte/due/v2/utils/xconv" + "github.com/dobyte/due/v2/utils/xrand" + "github.com/dobyte/due/v2/utils/xreflect" "github.com/go-redis/redis/v8" "golang.org/x/sync/singleflight" - "reflect" "time" ) @@ -110,18 +111,20 @@ func (c *Cache) GetSet(ctx context.Context, key string, fn cache.SetValueFunc) c } rst, _, _ := c.sfg.Do(key+":set", func() (interface{}, error) { - val, expiration, err := fn() + val, err := fn() if err != nil { return cache.NewResult(nil, err), nil } - if val == nil || reflect.ValueOf(val).IsNil() { + if val == nil || xreflect.IsNil(val) { if err = c.opts.client.Set(ctx, key, c.opts.nilValue, c.opts.nilExpiration).Err(); err != nil { return cache.NewResult(nil, err), nil } return cache.NewResult(nil, errors.ErrNil), nil } + expiration := time.Duration(xrand.Int64(int64(c.opts.minExpiration), int64(c.opts.maxExpiration))) + if err = c.opts.client.Set(ctx, key, xconv.String(val), expiration).Err(); err != nil { return cache.NewResult(nil, err), nil } diff --git a/cache/redis/options.go b/cache/redis/options.go index f4248be4..f800e51f 100644 --- a/cache/redis/options.go +++ b/cache/redis/options.go @@ -13,6 +13,8 @@ const ( defaultPrefix = "cache" defaultNilValue = "cache@nil" defaultNilExpiration = "10s" + defaultMinExpiration = "1h" + defaultMaxExpiration = "24h" ) const ( @@ -24,6 +26,8 @@ const ( defaultPasswordKey = "etc.cache.redis.password" defaultNilValueKey = "etc.cache.redis.nilValue" defaultNilExpirationKey = "etc.cache.redis.nilExpiration" + defaultMinExpirationKey = "etc.cache.redis.minExpiration" + defaultMaxExpirationKey = "etc.cache.redis.maxExpiration" ) type Option func(o *options) @@ -62,6 +66,12 @@ type options struct { // 空值过期时间,默认为10s nilExpiration time.Duration + + // 最小过期时间,默认为1h + minExpiration time.Duration + + // 最大过期时间,默认为24h + maxExpiration time.Duration } func defaultOptions() *options { @@ -74,6 +84,8 @@ func defaultOptions() *options { password: etc.Get(defaultPasswordKey).String(), nilValue: etc.Get(defaultNilValueKey, defaultNilValue).String(), nilExpiration: etc.Get(defaultNilExpirationKey, defaultNilExpiration).Duration(), + minExpiration: etc.Get(defaultMinExpirationKey, defaultMinExpiration).Duration(), + maxExpiration: etc.Get(defaultMaxExpirationKey, defaultMaxExpiration).Duration(), } } @@ -121,3 +133,13 @@ func WithNilValue(nilValue string) Option { func WithNilExpiration(nilExpiration time.Duration) Option { return func(o *options) { o.nilExpiration = nilExpiration } } + +// WithMinExpiration 设置最小过期时间 +func WithMinExpiration(minExpiration time.Duration) Option { + return func(o *options) { o.minExpiration = minExpiration } +} + +// WithMaxExpiration 设置最大过期时间 +func WithMaxExpiration(maxExpiration time.Duration) Option { + return func(o *options) { o.maxExpiration = maxExpiration } +} diff --git a/testdata/etc/etc.toml b/testdata/etc/etc.toml index 686c8e84..9e028bf3 100644 --- a/testdata/etc/etc.toml +++ b/testdata/etc/etc.toml @@ -364,7 +364,10 @@ timezone = "Local" nilValue = "cache@nil" # 空值过期时间,默认为10s nilExpiration = "10s" - + # 最小过期时间,默认为1h + minExpiration = "1h" + # 最大过期时间,默认为24h + maxExpiration = "24h" [crypto] # RSA设置 [crypto.rsa] diff --git a/utils/xreflect/reflect.go b/utils/xreflect/reflect.go index 79457983..c9c0e1b4 100644 --- a/utils/xreflect/reflect.go +++ b/utils/xreflect/reflect.go @@ -1,6 +1,8 @@ package xreflect -import "reflect" +import ( + "reflect" +) func Value(i any) (reflect.Kind, reflect.Value) { var ( @@ -15,3 +17,16 @@ func Value(i any) (reflect.Kind, reflect.Value) { return rk, rv } + +// IsNil 检测值是否为nil +func IsNil(v any) bool { + rv := reflect.ValueOf(v) + rk := rv.Kind() + + switch rk { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice: + return rv.IsNil() + default: + return false + } +} diff --git a/utils/xreflect/reflect_test.go b/utils/xreflect/reflect_test.go new file mode 100644 index 00000000..f1c112d1 --- /dev/null +++ b/utils/xreflect/reflect_test.go @@ -0,0 +1,15 @@ +package xreflect_test + +import ( + "github.com/dobyte/due/v2/utils/xreflect" + "testing" +) + +func TestIsNil(t *testing.T) { + var b1 bool + var b2 *bool + t.Log(xreflect.IsNil(b1)) + t.Log(xreflect.IsNil(&b1)) + t.Log(xreflect.IsNil(b2)) + t.Log(xreflect.IsNil(&b2)) +}