diff --git a/cache/redis/store.go b/cache/redis/store.go index 67aff79b..7f62b827 100644 --- a/cache/redis/store.go +++ b/cache/redis/store.go @@ -2,6 +2,7 @@ package redis import ( "context" + "errors" "time" "github.com/redis/go-redis/v9" @@ -10,7 +11,7 @@ import ( "github.com/go-kratos-ecosystem/components/v2/codec" "github.com/go-kratos-ecosystem/components/v2/codec/json" "github.com/go-kratos-ecosystem/components/v2/locker" - redisLocker "github.com/go-kratos-ecosystem/components/v2/locker/redis" + redislocker "github.com/go-kratos-ecosystem/components/v2/locker/redis" ) type Store struct { @@ -72,6 +73,9 @@ func (s *Store) Has(ctx context.Context, key string) (bool, error) { func (s *Store) Get(ctx context.Context, key string, dest any) error { r := s.redis.Get(ctx, s.opts.prefix+key) if r.Err() != nil { + if errors.Is(r.Err(), redis.Nil) { + return cache.ErrNotFound + } return r.Err() } @@ -161,8 +165,8 @@ func (s *Store) Add(ctx context.Context, key string, value any, ttl time.Duratio } func (s *Store) Lock(key string, ttl time.Duration) locker.Locker { - return redisLocker.NewLocker(s.redis, - redisLocker.WithName(s.opts.prefix+key), - redisLocker.WithTTL(ttl), + return redislocker.NewLocker(s.redis, + redislocker.WithName(s.opts.prefix+key), + redislocker.WithTTL(ttl), ) } diff --git a/cache/redis/store_test.go b/cache/redis/store_test.go index 76bd737f..856abb4f 100644 --- a/cache/redis/store_test.go +++ b/cache/redis/store_test.go @@ -11,19 +11,25 @@ import ( "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" + "github.com/go-kratos-ecosystem/components/v2/cache" "github.com/go-kratos-ecosystem/components/v2/codec/json" "github.com/go-kratos-ecosystem/components/v2/locker" ) -func createRedis() redis.UniversalClient { - return redis.NewClient(&redis.Options{ +var ctx = context.Background() + +func createRedis(t *testing.T) redis.UniversalClient { + client := redis.NewClient(&redis.Options{ Addr: ":6379", }) + t.Cleanup(func() { + client.FlushAll(ctx) + }) + return client } func TestRedis_Base(t *testing.T) { - store := New(createRedis(), Prefix("cache:redis"), Codec(json.Codec)) - ctx := context.Background() + store := New(createRedis(t), Prefix("cache:redis"), Codec(json.Codec)) ok1, err := store.Put(ctx, "test", "test", time.Second) assert.Nil(t, err) @@ -45,8 +51,7 @@ func TestRedis_Base(t *testing.T) { } func TestRedis_IncrAndDecr(t *testing.T) { - store := New(createRedis(), Prefix("cache:redis")) - ctx := context.Background() + store := New(createRedis(t), Prefix("cache:redis")) _, err := store.Forget(ctx, "test:inc") assert.Nil(t, err) @@ -62,12 +67,25 @@ func TestRedis_IncrAndDecr(t *testing.T) { v3, err := store.Decrement(ctx, "test:inc", 1) assert.Nil(t, err) assert.Equal(t, 10, v3) + + // put another type + ok1, err := store.Put(ctx, "test:inc:type", "test", time.Second*3) + assert.Nil(t, err) + assert.True(t, ok1) + + v4, err := store.Increment(ctx, "test:inc:type", 1) + t.Log(err) + assert.Error(t, err) + assert.Zero(t, v4) + + v5, err := store.Decrement(ctx, "test:inc:type", 1) + assert.Error(t, err) + assert.Zero(t, v5) } func TestRedis_Forever(t *testing.T) { - client := createRedis() - store := New(createRedis(), Prefix("cache:redis")) - ctx := context.Background() + client := createRedis(t) + store := New(createRedis(t), Prefix("cache:redis")) ok1, err := store.Forever(ctx, "test:forever", "test") assert.Nil(t, err) @@ -80,8 +98,7 @@ func TestRedis_Forever(t *testing.T) { } func TestRedis_Flush(t *testing.T) { - store := New(createRedis(), Prefix("cache:redis")) - ctx := context.Background() + store := New(createRedis(t), Prefix("cache:redis")) ok1, err := store.Put(ctx, "test:flush", "test", time.Second) assert.Nil(t, err) @@ -92,13 +109,12 @@ func TestRedis_Flush(t *testing.T) { assert.True(t, ok2) ok3, err := store.Has(ctx, "test:flush") - assert.Nil(t, err) + assert.NoError(t, err) assert.False(t, ok3) } func TestRedis_Add(t *testing.T) { - store := New(createRedis(), Prefix("cache:redis")) - ctx := context.Background() + store := New(createRedis(t), Prefix("cache:redis")) ok1, err := store.Add(ctx, "test:add", "test", time.Second) assert.Nil(t, err) @@ -115,7 +131,7 @@ func TestRedis_Add(t *testing.T) { } func TestRedis_Lock(t *testing.T) { - r := New(createRedis()) + r := New(createRedis(t)) var wg sync.WaitGroup var s int64 @@ -136,3 +152,18 @@ func TestRedis_Lock(t *testing.T) { wg.Wait() assert.True(t, s > 0) } + +func TestRedis_ErrNotFound(t *testing.T) { + store := New(createRedis(t), Prefix("cache:redis:notfound")) + + // Has + ok1, err := store.Has(ctx, "test:notfound:has") + assert.Nil(t, err) + assert.False(t, ok1) + + // Get + var v string + err = store.Get(ctx, "test:notfound:get", &v) + assert.True(t, errors.Is(err, cache.ErrNotFound)) + assert.Empty(t, v) +} diff --git a/cache/repository.go b/cache/repository.go index 01f2c69f..e6c8cb7c 100644 --- a/cache/repository.go +++ b/cache/repository.go @@ -3,8 +3,6 @@ package cache import ( "context" "time" - - "github.com/go-kratos-ecosystem/components/v2/helpers" ) type Repository interface { @@ -14,7 +12,6 @@ type Repository interface { Missing(ctx context.Context, key string) (bool, error) Delete(ctx context.Context, key string) (bool, error) Set(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) - Remember(ctx context.Context, key string, dest any, value func() any, ttl time.Duration) error } type repository struct { @@ -63,24 +60,3 @@ func (r *repository) Delete(ctx context.Context, key string) (bool, error) { func (r *repository) Set(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) { return r.Put(ctx, key, value, ttl) } - -func (r *repository) Remember( - ctx context.Context, - key string, dest any, - value func() any, - ttl time.Duration, -) error { - if missing, err := r.Missing(ctx, key); err != nil { - return err - } else if missing { - v := value() - - if _, err := r.Set(ctx, key, v, ttl); err != nil { - return err - } - - return helpers.Scan(v, dest) - } - - return r.Get(ctx, key, dest) -} diff --git a/cache/repository_test.go b/cache/repository_test.go index 35a93242..93e39717 100644 --- a/cache/repository_test.go +++ b/cache/repository_test.go @@ -79,9 +79,4 @@ func TestRepository(t *testing.T) { set, err := repo.Set(ctx, "test", "test", 0) assert.Nil(t, err) assert.True(t, set) - - // Remember - assert.Nil(t, repo.Remember(ctx, "test", nil, func() any { - return "test" - }, 0)) } diff --git a/cache/store.go b/cache/store.go index 8fa86ddf..a602c282 100644 --- a/cache/store.go +++ b/cache/store.go @@ -2,22 +2,43 @@ package cache import ( "context" + "errors" "time" "github.com/go-kratos-ecosystem/components/v2/locker" ) +var ( + ErrNotFound = errors.New("cache: the key is not found") + ErrNotInteger = errors.New("cache: the key is not an integer") +) + type Store interface { Locker + // Has returns true if the key exists in the cache. + // If the key does not exist, the return value will be false, and the return error will be nil. + // If the key exists, the return value will be true, and the return error will be nil. + // otherwise, the return error will be the store error. Has(ctx context.Context, key string) (bool, error) + // Get retrieves the value from the cache. + // If the key does not exist, the dest will be unchanged, and the return error will be ErrNotFound. + // If the key exists, the value will be unmarshaled to dest, and the return error will be nil. + // otherwise, the return error will be the store error. Get(ctx context.Context, key string, dest any) error + // Put stores the value into the cache with an expiration time. + // If put success, the return value will be true, and the return error will be nil. + // otherwise, the return value will be false, and the return error will be the store error. Put(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) + // Increment increments the value in the cache. + // If the key does not exist, the before default value is 0. Increment(ctx context.Context, key string, value int) (int, error) + // Decrement decrements the value in the cache. + // If the key does not exist, the before default value is 0. Decrement(ctx context.Context, key string, value int) (int, error) Forever(ctx context.Context, key string, value any) (bool, error) @@ -30,6 +51,10 @@ type Store interface { } type Addable interface { + // Add stores the value into the cache with an expiration time if the key does not exist. + // If the key exists, the return value will be false, and the return error will be nil. + // If the key does not exist, the return value will be true, and the return error will be nil. + // otherwise, the return error will be the store error. Add(ctx context.Context, key string, value any, ttl time.Duration) (bool, error) }