Repository: zeromicro/go-zero
Pull request #1325 (2021-12-14 07:24:36)

commit  missing method for redis
commit  missing method `decr ` `decrby` `lindex`

kevwan: Thanks for your PR!

Please add unit tests for redis commands. And put the functions in alphabetical order.

Thanks.
CrazyZard: i've already done a code-review 
thanks
CrazyZard: goods news

Patch:
diff --git a/core/stores/kv/store.go b/core/stores/kv/store.go
index aa0a499a..c43dcb9f 100644
--- a/core/stores/kv/store.go
+++ b/core/stores/kv/store.go
@@ -16,6 +16,8 @@ var ErrNoRedisNode = errors.New("no redis node")
 type (
 	// Store interface represents a KV store.
 	Store interface {
+		Decr(key string) (int64, error)
+		Decrby(key string, increment int64) (int64, error)
 		Del(keys ...string) (int, error)
 		Eval(script, key string, args ...interface{}) (interface{}, error)
 		Exists(key string) (bool, error)
@@ -37,6 +39,7 @@ type (
 		Incr(key string) (int64, error)
 		Incrby(key string, increment int64) (int64, error)
 		Llen(key string) (int, error)
+		Lindex(key string, index int64) (string, error)
 		Lpop(key string) (string, error)
 		Lpush(key string, values ...interface{}) (int, error)
 		Lrange(key string, start, stop int) ([]string, error)
@@ -102,6 +105,24 @@ func NewStore(c KvConf) Store {
 	}
 }
 
+func (cs clusterStore) Decr(key string) (int64, error) {
+	node, err := cs.getRedis(key)
+	if err != nil {
+		return 0, err
+	}
+
+	return node.Decr(key)
+}
+
+func (cs clusterStore) Decrby(key string, increment int64) (int64, error) {
+	node, err := cs.getRedis(key)
+	if err != nil {
+		return 0, err
+	}
+
+	return node.Decrby(key, increment)
+}
+
 func (cs clusterStore) Del(keys ...string) (int, error) {
 	var val int
 	var be errorx.BatchError
@@ -303,6 +324,15 @@ func (cs clusterStore) Llen(key string) (int, error) {
 	return node.Llen(key)
 }
 
+func (cs clusterStore) Lindex(key string, index int64) (string, error) {
+	node, err := cs.getRedis(key)
+	if err != nil {
+		return "", err
+	}
+
+	return node.Lindex(key, index)
+}
+
 func (cs clusterStore) Lpop(key string) (string, error) {
 	node, err := cs.getRedis(key)
 	if err != nil {
diff --git a/core/stores/redis/redis.go b/core/stores/redis/redis.go
index 3fa001c4..d6fad308 100644
--- a/core/stores/redis/redis.go
+++ b/core/stores/redis/redis.go
@@ -238,6 +238,36 @@ func (s *Redis) BlpopEx(redisNode RedisNode, key string) (string, bool, error) {
 	return vals[1], true, nil
 }
 
+// Decr is the implementation of redis decr command.
+func (s *Redis) Decr(key string) (val int64, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.Decr(key).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
+// Decrby is the implementation of redis decrby command.
+func (s *Redis) Decrby(key string, increment int64) (val int64, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.DecrBy(key, increment).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
 // Del deletes keys.
 func (s *Redis) Del(keys ...string) (val int, err error) {
 	err = s.brk.DoWithAcceptable(func() error {
@@ -765,6 +795,21 @@ func (s *Redis) Llen(key string) (val int, err error) {
 	return
 }
 
+// Lindex is the implementation of redis lindex command.
+func (s *Redis) Lindex(key string, index int64) (val string, err error) {
+	err = s.brk.DoWithAcceptable(func() error {
+		conn, err := getRedis(s)
+		if err != nil {
+			return err
+		}
+
+		val, err = conn.LIndex(key, index).Result()
+		return err
+	}, acceptable)
+
+	return
+}
+
 // Lpop is the implementation of redis lpop command.
 func (s *Redis) Lpop(key string) (val string, err error) {
 	err = s.brk.DoWithAcceptable(func() error {


Test patch:
diff --git a/core/stores/kv/store_test.go b/core/stores/kv/store_test.go
index c26a4231..ee7cd047 100644
--- a/core/stores/kv/store_test.go
+++ b/core/stores/kv/store_test.go
@@ -17,6 +17,36 @@ var (
 	s2, _ = miniredis.Run()
 )
 
+func TestRedis_Decr(t *testing.T) {
+	store := clusterStore{dispatcher: hash.NewConsistentHash()}
+	_, err := store.Decr("a")
+	assert.NotNil(t, err)
+
+	runOnCluster(t, func(client Store) {
+		val, err := client.Decr("a")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-1), val)
+		val, err = client.Decr("a")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-2), val)
+	})
+}
+
+func TestRedis_DecrBy(t *testing.T) {
+	store := clusterStore{dispatcher: hash.NewConsistentHash()}
+	_, err := store.Incrby("a", 2)
+	assert.NotNil(t, err)
+
+	runOnCluster(t, func(client Store) {
+		val, err := client.Decrby("a", 2)
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-2), val)
+		val, err = client.Decrby("a", 3)
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-5), val)
+	})
+}
+
 func TestRedis_Exists(t *testing.T) {
 	store := clusterStore{dispatcher: hash.NewConsistentHash()}
 	_, err := store.Exists("foo")
@@ -234,6 +264,8 @@ func TestRedis_List(t *testing.T) {
 	assert.NotNil(t, err)
 	_, err = store.Lrem("key", 0, "val")
 	assert.NotNil(t, err)
+	_, err = store.Lindex("key", 0)
+	assert.NotNil(t, err)
 
 	runOnCluster(t, func(client Store) {
 		val, err := client.Lpush("key", "value1", "value2")
@@ -245,6 +277,9 @@ func TestRedis_List(t *testing.T) {
 		val, err = client.Llen("key")
 		assert.Nil(t, err)
 		assert.Equal(t, 4, val)
+		value, err := client.Lindex("key", 0)
+		assert.Nil(t, err)
+		assert.Equal(t, "value2", value)
 		vals, err := client.Lrange("key", 0, 10)
 		assert.Nil(t, err)
 		assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals)
diff --git a/core/stores/redis/redis_test.go b/core/stores/redis/redis_test.go
index 1e9251e1..49aee638 100644
--- a/core/stores/redis/redis_test.go
+++ b/core/stores/redis/redis_test.go
@@ -14,6 +14,32 @@ import (
 	"github.com/tal-tech/go-zero/core/stringx"
 )
 
+func TestRedis_Decr(t *testing.T) {
+	runOnRedis(t, func(client *Redis) {
+		_, err := New(client.Addr, badType()).Decr("a")
+		assert.NotNil(t, err)
+		val, err := client.Decr("a")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-1), val)
+		val, err = client.Decr("a")
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-2), val)
+	})
+}
+
+func TestRedis_DecrBy(t *testing.T) {
+	runOnRedis(t, func(client *Redis) {
+		_, err := New(client.Addr, badType()).Decrby("a", 2)
+		assert.NotNil(t, err)
+		val, err := client.Decrby("a", 2)
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-2), val)
+		val, err = client.Decrby("a", 3)
+		assert.Nil(t, err)
+		assert.Equal(t, int64(-5), val)
+	})
+}
+
 func TestRedis_Exists(t *testing.T) {
 	runOnRedis(t, func(client *Redis) {
 		_, err := New(client.Addr, badType()).Exists("a")
@@ -295,6 +321,11 @@ func TestRedis_List(t *testing.T) {
 		val, err = client.Llen("key")
 		assert.Nil(t, err)
 		assert.Equal(t, 4, val)
+		_, err = New(client.Addr, badType()).Lindex("key", 1)
+		assert.NotNil(t, err)
+		value, err := client.Lindex("key", 0)
+		assert.Nil(t, err)
+		assert.Equal(t, "value2", value)
 		vals, err := client.Lrange("key", 0, 10)
 		assert.Nil(t, err)
 		assert.EqualValues(t, []string{"value2", "value1", "value3", "value4"}, vals)