From 1b6c355c69d696868c31998df494dcbc8ecb9024 Mon Sep 17 00:00:00 2001 From: Rueian Date: Wed, 28 Aug 2024 07:04:53 +0800 Subject: [PATCH] feat: add HExpire, HPExpire, HTTL, and HPTTL to rueidiscompat (#618) Signed-off-by: Rueian --- docker-compose.yml | 12 +-- rueidiscompat/adapter.go | 148 +++++++++++++++++++++++--- rueidiscompat/adapter_test.go | 183 +++++++++++++++++++++++++++++++-- rueidiscompat/command.go | 7 ++ rueidiscompat/pipeline.go | 78 ++++++++++++++ rueidiscompat/pipeline_test.go | 62 ++++++++++- 6 files changed, 460 insertions(+), 30 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 429293bc..3287dde2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,11 +2,11 @@ version: "3.8" services: redis: - image: redis:7.2-rc-alpine + image: redis:7.4-alpine ports: - "6379:6379" redislock: - image: redis:7.2-rc-alpine + image: redis:7.4-alpine ports: - "6376:6379" redis5: @@ -30,7 +30,7 @@ services: ports: - "6377:6379" compat: - image: redis/redis-stack:7.2.0-v3 + image: redis/redis-stack:7.4.0-v0 ports: - "6378:6379" compat5: @@ -38,7 +38,7 @@ services: ports: - "6356:6379" sentinel: - image: redis:7.2-rc-alpine + image: redis:7.4-alpine entrypoint: - /bin/sh - -c @@ -62,7 +62,7 @@ services: - "6385:6385" - "26355:26379" cluster: - image: redis:7.2-rc-alpine + image: redis:7.4-alpine entrypoint: - /bin/sh - -c @@ -107,7 +107,7 @@ services: - "7008:7008" - "7009:7009" clusteradapter: - image: redis:7.2-rc-alpine + image: redis:7.4-alpine entrypoint: - /bin/sh - -c diff --git a/rueidiscompat/adapter.go b/rueidiscompat/adapter.go index 36d4a258..d6ee60bd 100644 --- a/rueidiscompat/adapter.go +++ b/rueidiscompat/adapter.go @@ -162,19 +162,19 @@ type CoreCmdable interface { HVals(ctx context.Context, key string) *StringSliceCmd HRandField(ctx context.Context, key string, count int64) *StringSliceCmd HRandFieldWithValues(ctx context.Context, key string, count int64) *KeyValueSliceCmd - // TODO HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd - // TODO HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd - // TODO HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd - // TODO HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd - // TODO HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd - // TODO HExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd - // TODO HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd - // TODO HPExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd - // TODO HPersist(ctx context.Context, key string, fields ...string) *IntSliceCmd - // TODO HExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd - // TODO HPExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd - // TODO HTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd - // TODO HPTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd + HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd + HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd + HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd + HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd + HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd + HExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd + HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd + HPExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd + HPersist(ctx context.Context, key string, fields ...string) *IntSliceCmd + HExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd + HPExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd + HTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd + HPTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd BLMPop(ctx context.Context, timeout time.Duration, direction string, count int64, keys ...string) *KeyValuesCmd @@ -1408,6 +1408,128 @@ func (c *Compat) HRandFieldWithValues(ctx context.Context, key string, count int return newKeyValueSliceCmd(c.client.Do(ctx, c.client.B().Hrandfield().Key(key).Count(count).Withvalues().Build())) } +func (c *Compat) HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + var cmd rueidis.Completed + if expirationArgs.NX { + cmd = c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Nx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.XX { + cmd = c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Xx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.GT { + cmd = c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Gt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.LT { + cmd = c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Lt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else { + cmd = c.client.B().Hexpire().Key(key).Seconds(formatSec(expiration)).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + var cmd rueidis.Completed + if expirationArgs.NX { + cmd = c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Nx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.XX { + cmd = c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Xx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.GT { + cmd = c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Gt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.LT { + cmd = c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Lt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else { + cmd = c.client.B().Hpexpire().Key(key).Milliseconds(formatMs(expiration)).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + var cmd rueidis.Completed + if expirationArgs.NX { + cmd = c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Nx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.XX { + cmd = c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Xx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.GT { + cmd = c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Gt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.LT { + cmd = c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Lt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else { + cmd = c.client.B().Hexpireat().Key(key).UnixTimeSeconds(tm.Unix()).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + var cmd rueidis.Completed + if expirationArgs.NX { + cmd = c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Nx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.XX { + cmd = c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Xx().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.GT { + cmd = c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Gt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else if expirationArgs.LT { + cmd = c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Lt().Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } else { + cmd = c.client.B().Hpexpireat().Key(key).UnixTimeMilliseconds(tm.UnixMilli()).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + } + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPersist(ctx context.Context, key string, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hpersist().Key(key).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hexpiretime().Key(key).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hpexpiretime().Key(key).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd { + cmd := c.client.B().Httl().Key(key).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + +func (c *Compat) HPTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd { + cmd := c.client.B().Hpttl().Key(key).Fields().Numfields(int64(len(fields))).Field(fields...).Build() + resp := c.client.Do(ctx, cmd) + return newIntSliceCmd(resp) +} + func (c *Compat) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd { cmd := c.client.B().Blpop().Key(keys...).Timeout(float64(formatSec(timeout))).Build() resp := c.client.Do(ctx, cmd) diff --git a/rueidiscompat/adapter_test.go b/rueidiscompat/adapter_test.go index d11bf281..ea0f1ac2 100644 --- a/rueidiscompat/adapter_test.go +++ b/rueidiscompat/adapter_test.go @@ -2383,6 +2383,166 @@ func testAdapter(resp3 bool) { Equal([]KeyValue{{Key: "key2", Value: "hello2"}}), )) }) + + It("should HExpire", Label("hash-expiration", "NonRedisEnterprise"), func() { + res, err := adapter.HExpire(ctx, "no_such_key", 10*time.Second, "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(res).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err = adapter.HExpire(ctx, "myhash", 10*time.Second, "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, 1, -2})) + }) + + It("should HPExpire", Label("hash-expiration", "NonRedisEnterprise"), func() { + res, err := adapter.HPExpire(ctx, "no_such_key", 10*time.Second, "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(res).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err = adapter.HPExpire(ctx, "myhash", 10*time.Second, "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, 1, -2})) + }) + + It("should HExpireAt", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HExpireAt(ctx, "no_such_key", time.Now().Add(10*time.Second), "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HExpireAt(ctx, "myhash", time.Now().Add(10*time.Second), "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, 1, -2})) + }) + + It("should HPExpireAt", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HPExpireAt(ctx, "no_such_key", time.Now().Add(10*time.Second), "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HPExpireAt(ctx, "myhash", time.Now().Add(10*time.Second), "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, 1, -2})) + }) + + It("should HPersist", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HPersist(ctx, "no_such_key", "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HPersist(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{-1, -1, -2})) + + res, err = adapter.HExpire(ctx, "myhash", 10*time.Second, "key1", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -2})) + + res, err = adapter.HPersist(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -1, -2})) + }) + + It("should HExpireTime", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HExpireTime(ctx, "no_such_key", "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HExpire(ctx, "myhash", 10*time.Second, "key1", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -2})) + + res, err = adapter.HExpireTime(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res[0]).To(BeNumerically("~", time.Now().Add(10*time.Second).Unix(), 1)) + }) + + It("should HPExpireTime", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HPExpireTime(ctx, "no_such_key", "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + expireAt := time.Now().Add(10 * time.Second) + res, err := adapter.HPExpireAt(ctx, "myhash", expireAt, "key1", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -2})) + + res, err = adapter.HPExpireTime(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(BeEquivalentTo([]int64{expireAt.UnixMilli(), -1, -2})) + }) + + It("should HTTL", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HTTL(ctx, "no_such_key", "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HExpire(ctx, "myhash", 10*time.Second, "key1", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -2})) + + res, err = adapter.HTTL(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{10, -1, -2})) + }) + + It("should HPTTL", Label("hash-expiration", "NonRedisEnterprise"), func() { + resEmpty, err := adapter.HPTTL(ctx, "no_such_key", "field1", "field2", "field3").Result() + Expect(err).To(BeNil()) + Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2})) + + for i := 0; i < 100; i++ { + sadd := adapter.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello") + Expect(sadd.Err()).NotTo(HaveOccurred()) + } + + res, err := adapter.HExpire(ctx, "myhash", 10*time.Second, "key1", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res).To(Equal([]int64{1, -2})) + + res, err = adapter.HPTTL(ctx, "myhash", "key1", "key2", "key200").Result() + Expect(err).NotTo(HaveOccurred()) + Expect(res[0]).To(BeNumerically("~", 10*time.Second.Milliseconds(), 1)) + }) } }) @@ -5976,12 +6136,15 @@ func testAdapter(resp3 bool) { res, err := adapter.ZRangeWithScores(ctx, "result", 0, -1).Result() Expect(err).NotTo(HaveOccurred()) + for i, z := range res { + res[i].Score = float64(int(z.Score)) + } Expect(res).To(ContainElement(Z{ - Score: 190.44242984775784, + Score: 190., Member: "Palermo", })) Expect(res).To(ContainElement(Z{ - Score: 56.4412578701582, + Score: 56., Member: "Catania", })) }) @@ -6311,13 +6474,16 @@ func testAdapter(resp3 bool) { v, err := adapter.ZRangeWithScores(ctx, "key2", 0, -1).Result() Expect(err).NotTo(HaveOccurred()) + for i, z := range v { + v[i].Score = float64(int(z.Score)) + } Expect(v).To(Equal([]Z{ { - Score: 56.441257870158204, + Score: 56., Member: "Catania", }, { - Score: 190.44242984775784, + Score: 190., Member: "Palermo", }, })) @@ -8932,6 +9098,7 @@ func testAdapterCache(resp3 bool) { BeforeEach(func() { Expect(adapter.FlushDB(ctx).Err()).NotTo(HaveOccurred()) adapter.TFunctionDelete(ctx, "lib1") + adapter.TFunctionDelete(ctx, "lib2") }) // Copied from go-redis // https://github.com/redis/go-redis/blob/f994ff1cd96299a5c8029ae3403af7b17ef06e8a/gears_commands_test.go @@ -8944,7 +9111,6 @@ func testAdapterCache(resp3 bool) { Expect(err).NotTo(HaveOccurred()) Expect(resultAdd).To(BeEquivalentTo("OK")) opt.Replace = false - adapter.TFunctionDelete(ctx, libCodeWithConfig("lib2")).Result() resultAdd, err = adapter.TFunctionLoadArgs(ctx, libCodeWithConfig("lib2"), opt).Result() Expect(err).NotTo(HaveOccurred()) Expect(resultAdd).To(BeEquivalentTo("OK")) @@ -10835,8 +11001,7 @@ func testAdapterCache(resp3 bool) { cmd3 := adapter.JSONGet(ctx, "insert2") Expect(cmd3.Err()).NotTo(HaveOccurred()) - Expect(cmd3.Val()).To(Or( - Equal(`[[100,200,300,1,2,200]]`))) + Expect(cmd3.Val()).To(Equal(`[100,200,300,1,2,200]`)) }) It("should JSONArrLen", Label("json.arrlen", "json"), func() { @@ -10944,11 +11109,11 @@ func testAdapterCache(resp3 bool) { res, err = adapter.JSONGetWithArgs(ctx, "get3", &JSONGetArgs{Indent: "-"}).Result() Expect(err).NotTo(HaveOccurred()) - Expect(res).To(Equal(`[-{--"a":1,--"b":2-}]`)) + Expect(res).To(Equal(`{-"a":1,-"b":2}`)) res, err = adapter.JSONGetWithArgs(ctx, "get3", &JSONGetArgs{Indent: "-", Newline: `~`, Space: `!`}).Result() Expect(err).NotTo(HaveOccurred()) - Expect(res).To(Equal(`[~-{~--"a":!1,~--"b":!2~-}~]`)) + Expect(res).To(Equal(`{~-"a":!1,~-"b":!2~}`)) }) It("should JSONMerge", Label("json.merge", "json"), func() { diff --git a/rueidiscompat/command.go b/rueidiscompat/command.go index 2f343b7d..699f8706 100644 --- a/rueidiscompat/command.go +++ b/rueidiscompat/command.go @@ -2132,6 +2132,13 @@ func newCommandsInfoCmd(res rueidis.RedisResult) *CommandsInfoCmd { return cmd } +type HExpireArgs struct { + NX bool + XX bool + GT bool + LT bool +} + type Sort struct { By string Order string diff --git a/rueidiscompat/pipeline.go b/rueidiscompat/pipeline.go index e5f2db83..b985be56 100644 --- a/rueidiscompat/pipeline.go +++ b/rueidiscompat/pipeline.go @@ -661,6 +661,84 @@ func (c *Pipeline) HRandFieldWithValues(ctx context.Context, key string, count i return ret } +func (c *Pipeline) HExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd { + ret := c.comp.HExpire(ctx, key, expiration, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + ret := c.comp.HExpireWithArgs(ctx, key, expiration, expirationArgs, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPExpire(ctx context.Context, key string, expiration time.Duration, fields ...string) *IntSliceCmd { + ret := c.comp.HPExpire(ctx, key, expiration, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPExpireWithArgs(ctx context.Context, key string, expiration time.Duration, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + ret := c.comp.HPExpireWithArgs(ctx, key, expiration, expirationArgs, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd { + ret := c.comp.HExpireAt(ctx, key, tm, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + ret := c.comp.HExpireAtWithArgs(ctx, key, tm, expirationArgs, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPExpireAt(ctx context.Context, key string, tm time.Time, fields ...string) *IntSliceCmd { + ret := c.comp.HPExpireAt(ctx, key, tm, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPExpireAtWithArgs(ctx context.Context, key string, tm time.Time, expirationArgs HExpireArgs, fields ...string) *IntSliceCmd { + ret := c.comp.HPExpireAtWithArgs(ctx, key, tm, expirationArgs, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPersist(ctx context.Context, key string, fields ...string) *IntSliceCmd { + ret := c.comp.HPersist(ctx, key, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd { + ret := c.comp.HExpireTime(ctx, key, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPExpireTime(ctx context.Context, key string, fields ...string) *IntSliceCmd { + ret := c.comp.HPExpireTime(ctx, key, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd { + ret := c.comp.HTTL(ctx, key, fields...) + c.rets = append(c.rets, ret) + return ret +} + +func (c *Pipeline) HPTTL(ctx context.Context, key string, fields ...string) *IntSliceCmd { + ret := c.comp.HPTTL(ctx, key, fields...) + c.rets = append(c.rets, ret) + return ret +} + func (c *Pipeline) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *StringSliceCmd { ret := c.comp.BLPop(ctx, timeout, keys...) c.rets = append(c.rets, ret) diff --git a/rueidiscompat/pipeline_test.go b/rueidiscompat/pipeline_test.go index 26d3c4db..eb64d780 100644 --- a/rueidiscompat/pipeline_test.go +++ b/rueidiscompat/pipeline_test.go @@ -217,6 +217,35 @@ func TestPipeliner(t *testing.T) { p.HVals(ctx, "1") p.HRandField(ctx, "1", 1) p.HRandFieldWithValues(ctx, "1", 1) + p.HExpire(ctx, "1", time.Second, "2", "3") + p.HExpireWithArgs(ctx, "1", time.Second, HExpireArgs{NX: true}, "2", "3") + p.HExpireWithArgs(ctx, "1", time.Second, HExpireArgs{XX: true}, "2", "3") + p.HExpireWithArgs(ctx, "1", time.Second, HExpireArgs{GT: true}, "2", "3") + p.HExpireWithArgs(ctx, "1", time.Second, HExpireArgs{LT: true}, "2", "3") + p.HExpireWithArgs(ctx, "1", time.Second, HExpireArgs{}, "2", "3") + p.HPExpire(ctx, "1", time.Second, "2", "3") + p.HPExpireWithArgs(ctx, "1", time.Second, HExpireArgs{NX: true}, "2", "3") + p.HPExpireWithArgs(ctx, "1", time.Second, HExpireArgs{XX: true}, "2", "3") + p.HPExpireWithArgs(ctx, "1", time.Second, HExpireArgs{GT: true}, "2", "3") + p.HPExpireWithArgs(ctx, "1", time.Second, HExpireArgs{LT: true}, "2", "3") + p.HPExpireWithArgs(ctx, "1", time.Second, HExpireArgs{}, "2", "3") + p.HExpireAt(ctx, "1", time.UnixMilli(1724164643), "2", "3") + p.HExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{NX: true}, "2", "3") + p.HExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{XX: true}, "2", "3") + p.HExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{GT: true}, "2", "3") + p.HExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{LT: true}, "2", "3") + p.HExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{}, "2", "3") + p.HPExpireAt(ctx, "1", time.UnixMilli(1724164643), "2", "3") + p.HPExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{NX: true}, "2", "3") + p.HPExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{XX: true}, "2", "3") + p.HPExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{GT: true}, "2", "3") + p.HPExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{LT: true}, "2", "3") + p.HPExpireAtWithArgs(ctx, "1", time.UnixMilli(1724164643), HExpireArgs{}, "2", "3") + p.HPersist(ctx, "1", "2", "3") + p.HExpireTime(ctx, "1", "2", "3") + p.HPExpireTime(ctx, "1", "2", "3") + p.HTTL(ctx, "1", "2", "3") + p.HPTTL(ctx, "1", "2", "3") p.BLPop(ctx, time.Second, "1", "2") p.BLMPop(ctx, time.Second, "1", 1, "1", "2") p.BRPop(ctx, time.Second, "1", "2") @@ -569,7 +598,7 @@ func TestPipeliner(t *testing.T) { p.JSONToggle(ctx, "1", "1") p.JSONType(ctx, "1", "1") - if n := len(p.rets); n != 445 { + if n := len(p.rets); n != 474 { t.Fatalf("unexpected pipeline calls: %v", n) } for i, cmd := range p.rets { @@ -577,7 +606,7 @@ func TestPipeliner(t *testing.T) { t.Fatalf("unexpected pipeline placeholder err(%d): %v", i, err) } } - if n := len(p.comp.client.(*proxy).cmds); n != 445 { + if n := len(p.comp.client.(*proxy).cmds); n != 474 { t.Fatalf("unexpected pipeline commands: %v", n) } var pipeline [][]string @@ -703,6 +732,35 @@ var golden = `[ ["HVALS","1"], ["HRANDFIELD","1","1"], ["HRANDFIELD","1","1","WITHVALUES"], + ["HEXPIRE","1","1","FIELDS","2","2","3"], + ["HEXPIRE","1","1","NX","FIELDS","2","2","3"], + ["HEXPIRE","1","1","XX","FIELDS","2","2","3"], + ["HEXPIRE","1","1","GT","FIELDS","2","2","3"], + ["HEXPIRE","1","1","LT","FIELDS","2","2","3"], + ["HEXPIRE","1","1","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","NX","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","XX","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","GT","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","LT","FIELDS","2","2","3"], + ["HPEXPIRE","1","1000","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","NX","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","XX","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","GT","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","LT","FIELDS","2","2","3"], + ["HEXPIREAT","1","1724164","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","NX","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","XX","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","GT","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","LT","FIELDS","2","2","3"], + ["HPEXPIREAT","1","1724164643","FIELDS","2","2","3"], + ["HPERSIST","1","FIELDS","2","2","3"], + ["HEXPIRETIME","1","FIELDS","2","2","3"], + ["HPEXPIRETIME","1","FIELDS","2","2","3"], + ["HTTL","1","FIELDS","2","2","3"], + ["HPTTL","1","FIELDS","2","2","3"], ["BLPOP","1","2","1"], ["BLMPOP","1","2","1","2","1","COUNT","1"], ["BRPOP","1","2","1"],