From 89cbc1a7bd95a24e404a927fbd46888f7f608cc5 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Tue, 10 Jun 2025 11:13:18 +0300 Subject: [PATCH 1/5] Add support for new bitop operations --- bitmap_commands.go | 20 +++++++++++++ commands_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/bitmap_commands.go b/bitmap_commands.go index a21558289..55a1c3a8f 100644 --- a/bitmap_commands.go +++ b/bitmap_commands.go @@ -12,6 +12,10 @@ type BitMapCmdable interface { BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpDiff(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpDiff1(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpAndOr(ctx context.Context, destKey string, keys ...string) *IntCmd + BitOpOne(ctx context.Context, destKey string, keys ...string) *IntCmd BitOpNot(ctx context.Context, destKey string, key string) *IntCmd BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd BitPosSpan(ctx context.Context, key string, bit int8, start, end int64, span string) *IntCmd @@ -90,6 +94,22 @@ func (c cmdable) BitOpXor(ctx context.Context, destKey string, keys ...string) * return c.bitOp(ctx, "xor", destKey, keys...) } +func (c cmdable) BitOpDiff(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "diff", destKey, keys...) +} + +func (c cmdable) BitOpDiff1(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "diff1", destKey, keys...) +} + +func (c cmdable) BitOpAndOr(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "andor", destKey, keys...) +} + +func (c cmdable) BitOpOne(ctx context.Context, destKey string, keys ...string) *IntCmd { + return c.bitOp(ctx, "one", destKey, keys...) +} + func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd { return c.bitOp(ctx, "not", destKey, key) } diff --git a/commands_test.go b/commands_test.go index 72b206943..a37fee633 100644 --- a/commands_test.go +++ b/commands_test.go @@ -1469,6 +1469,78 @@ var _ = Describe("Commands", func() { Expect(get.Val()).To(Equal("\xf0")) }) + It("should BitOpDiff", Label("NonRedisEnterprise"), func() { + set := client.Set(ctx, "key1", "\xff", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + set = client.Set(ctx, "key2", "\x0f", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + bitOpDiff := client.BitOpDiff(ctx, "dest", "key1", "key2") + Expect(bitOpDiff.Err()).NotTo(HaveOccurred()) + Expect(bitOpDiff.Val()).To(Equal(int64(1))) + + get := client.Get(ctx, "dest") + Expect(get.Err()).NotTo(HaveOccurred()) + Expect(get.Val()).To(Equal("\xf0")) + }) + + It("should BitOpDiff1", Label("NonRedisEnterprise"), func() { + set := client.Set(ctx, "key1", "\xff", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + set = client.Set(ctx, "key2", "\x0f", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + bitOpDiff1 := client.BitOpDiff1(ctx, "dest", "key1", "key2") + Expect(bitOpDiff1.Err()).NotTo(HaveOccurred()) + Expect(bitOpDiff1.Val()).To(Equal(int64(1))) + + get := client.Get(ctx, "dest") + Expect(get.Err()).NotTo(HaveOccurred()) + Expect(get.Val()).To(Equal("\xf0")) + }) + + It("should BitOpAndOr", Label("NonRedisEnterprise"), func() { + set := client.Set(ctx, "key1", "\xff", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + set = client.Set(ctx, "key2", "\x0f", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + bitOpAndOr := client.BitOpAndOr(ctx, "dest", "key1", "key2") + Expect(bitOpAndOr.Err()).NotTo(HaveOccurred()) + Expect(bitOpAndOr.Val()).To(Equal(int64(1))) + + get := client.Get(ctx, "dest") + Expect(get.Err()).NotTo(HaveOccurred()) + Expect(get.Val()).To(Equal("\xf0")) + }) + + It("should BitOpOne", Label("NonRedisEnterprise"), func() { + set := client.Set(ctx, "key1", "\xff", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + set = client.Set(ctx, "key2", "\x0f", 0) + Expect(set.Err()).NotTo(HaveOccurred()) + Expect(set.Val()).To(Equal("OK")) + + bitOpOne := client.BitOpOne(ctx, "dest", "key1", "key2") + Expect(bitOpOne.Err()).NotTo(HaveOccurred()) + Expect(bitOpOne.Val()).To(Equal(int64(1))) + + get := client.Get(ctx, "dest") + Expect(get.Err()).NotTo(HaveOccurred()) + Expect(get.Val()).To(Equal("\xf0")) + }) + It("should BitOpNot", Label("NonRedisEnterprise"), func() { set := client.Set(ctx, "key1", "\x00", 0) Expect(set.Err()).NotTo(HaveOccurred()) From c6b7b3fb458317235ca8f1bf20b2eea5bae0faa9 Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Tue, 17 Jun 2025 11:36:24 +0300 Subject: [PATCH 2/5] chore(ci): Add 8.2 pre build for CI --- .github/actions/run-tests/action.yml | 1 + .github/workflows/build.yml | 3 +++ commands_test.go | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index e9bb7e797..bba991972 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -25,6 +25,7 @@ runs: # Mapping of redis version to redis testing containers declare -A redis_version_mapping=( + ["8.2.x"]="8.2-M01-pre" ["8.0.x"]="8.0.2" ["7.4.x"]="rs-7.4.0-v5" ["7.2.x"]="rs-7.2.0-v17" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c8b3d1b68..dd097a9fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: fail-fast: false matrix: redis-version: + - "8.2.x" # Redis CE 8.2 - "8.0.x" # Redis CE 8.0 - "7.4.x" # Redis stack 7.4 go-version: @@ -43,6 +44,7 @@ jobs: # Mapping of redis version to redis testing containers declare -A redis_version_mapping=( + ["8.2.x"]="8.2-M01-pre" ["8.0.x"]="8.0.2" ["7.4.x"]="rs-7.4.0-v5" ) @@ -72,6 +74,7 @@ jobs: fail-fast: false matrix: redis-version: + - "8.2.x" # Redis CE 8.2 - "8.0.x" # Redis CE 8.0 - "7.4.x" # Redis stack 7.4 - "7.2.x" # Redis stack 7.2 diff --git a/commands_test.go b/commands_test.go index a37fee633..32c3829dd 100644 --- a/commands_test.go +++ b/commands_test.go @@ -1470,6 +1470,7 @@ var _ = Describe("Commands", func() { }) It("should BitOpDiff", Label("NonRedisEnterprise"), func() { + SkipBeforeRedisVersion(8.2, "BITOP DIFF is available since Redis 8.2") set := client.Set(ctx, "key1", "\xff", 0) Expect(set.Err()).NotTo(HaveOccurred()) Expect(set.Val()).To(Equal("OK")) @@ -1488,6 +1489,7 @@ var _ = Describe("Commands", func() { }) It("should BitOpDiff1", Label("NonRedisEnterprise"), func() { + SkipBeforeRedisVersion(8.2, "BITOP DIFF is available since Redis 8.2") set := client.Set(ctx, "key1", "\xff", 0) Expect(set.Err()).NotTo(HaveOccurred()) Expect(set.Val()).To(Equal("OK")) @@ -1506,6 +1508,7 @@ var _ = Describe("Commands", func() { }) It("should BitOpAndOr", Label("NonRedisEnterprise"), func() { + SkipBeforeRedisVersion(8.2, "BITOP ANDOR is available since Redis 8.2") set := client.Set(ctx, "key1", "\xff", 0) Expect(set.Err()).NotTo(HaveOccurred()) Expect(set.Val()).To(Equal("OK")) @@ -1524,6 +1527,7 @@ var _ = Describe("Commands", func() { }) It("should BitOpOne", Label("NonRedisEnterprise"), func() { + SkipBeforeRedisVersion(8.2, "BITOP ONE is available since Redis 8.2") set := client.Set(ctx, "key1", "\xff", 0) Expect(set.Err()).NotTo(HaveOccurred()) Expect(set.Val()).To(Equal("OK")) From a1d8deb6f42666f885e22e0217c9ecb6c1950ae3 Mon Sep 17 00:00:00 2001 From: Nedyalko Dyakov Date: Tue, 17 Jun 2025 13:20:24 +0300 Subject: [PATCH 3/5] feat(info): add new client info keys --- command.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/command.go b/command.go index 652e241be..9d9d2848c 100644 --- a/command.go +++ b/command.go @@ -5138,6 +5138,9 @@ type ClientInfo struct { OutputListLength int // oll, output list length (replies are queued in this list when the buffer is full) OutputMemory int // omem, output buffer memory usage TotalMemory int // tot-mem, total memory consumed by this client in its various buffers + TotalNetIn int // tot-net-in, total network input + TotalNetOut int // tot-net-out, total network output + TotalCmds int // tot-cmds, total number of commands processed IoThread int // io-thread id Events string // file descriptor events (see below) LastCmd string // cmd, last command played @@ -5303,6 +5306,12 @@ func parseClientInfo(txt string) (info *ClientInfo, err error) { info.OutputMemory, err = strconv.Atoi(val) case "tot-mem": info.TotalMemory, err = strconv.Atoi(val) + case "tot-net-in": + info.TotalNetIn, err = strconv.Atoi(val) + case "tot-net-out": + info.TotalNetOut, err = strconv.Atoi(val) + case "tot-cmds": + info.TotalCmds, err = strconv.Atoi(val) case "events": info.Events = val case "cmd": From 89b6476bc1debac9af5e2a2d64a5fbb4a07e7ca6 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Tue, 17 Jun 2025 14:39:35 +0300 Subject: [PATCH 4/5] fixed tests --- commands_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commands_test.go b/commands_test.go index 32c3829dd..19548e134 100644 --- a/commands_test.go +++ b/commands_test.go @@ -1504,7 +1504,7 @@ var _ = Describe("Commands", func() { get := client.Get(ctx, "dest") Expect(get.Err()).NotTo(HaveOccurred()) - Expect(get.Val()).To(Equal("\xf0")) + Expect(get.Val()).To(Equal("\x00")) }) It("should BitOpAndOr", Label("NonRedisEnterprise"), func() { @@ -1523,7 +1523,7 @@ var _ = Describe("Commands", func() { get := client.Get(ctx, "dest") Expect(get.Err()).NotTo(HaveOccurred()) - Expect(get.Val()).To(Equal("\xf0")) + Expect(get.Val()).To(Equal("\x0f")) }) It("should BitOpOne", Label("NonRedisEnterprise"), func() { From 7fbc42adadd72cd546d9d5cac934210edf2e6ee9 Mon Sep 17 00:00:00 2001 From: Hristo Temelski Date: Thu, 19 Jun 2025 13:20:55 +0300 Subject: [PATCH 5/5] added godocs for bitop commands --- bitmap_commands.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/bitmap_commands.go b/bitmap_commands.go index 55a1c3a8f..4dbc862a1 100644 --- a/bitmap_commands.go +++ b/bitmap_commands.go @@ -82,38 +82,50 @@ func (c cmdable) bitOp(ctx context.Context, op, destKey string, keys ...string) return cmd } +// BitOpAnd creates a new bitmap in which users are members of all given bitmaps func (c cmdable) BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "and", destKey, keys...) } +// BitOpOr creates a new bitmap in which users are member of at least one given bitmap func (c cmdable) BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "or", destKey, keys...) } +// BitOpXor creates a new bitmap in which users are the result of XORing all given bitmaps func (c cmdable) BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "xor", destKey, keys...) } +// BitOpNot creates a new bitmap in which users are not members of a given bitmap +func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd { + return c.bitOp(ctx, "not", destKey, key) +} + +// BitOpDiff creates a new bitmap in which users are members of bitmap X but not of any of bitmaps Y1, Y2, … +// Introduced with Redis 8.2 func (c cmdable) BitOpDiff(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "diff", destKey, keys...) } +// BitOpDiff1 creates a new bitmap in which users are members of one or more of bitmaps Y1, Y2, … but not members of bitmap X +// Introduced with Redis 8.2 func (c cmdable) BitOpDiff1(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "diff1", destKey, keys...) } +// BitOpAndOr creates a new bitmap in which users are members of bitmap X and also members of one or more of bitmaps Y1, Y2, … +// Introduced with Redis 8.2 func (c cmdable) BitOpAndOr(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "andor", destKey, keys...) } +// BitOpOne creates a new bitmap in which users are members of exactly one of the given bitmaps +// Introduced with Redis 8.2 func (c cmdable) BitOpOne(ctx context.Context, destKey string, keys ...string) *IntCmd { return c.bitOp(ctx, "one", destKey, keys...) } -func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd { - return c.bitOp(ctx, "not", destKey, key) -} - // BitPos is an API before Redis version 7.0, cmd: bitpos key bit start end // if you need the `byte | bit` parameter, please use `BitPosSpan`. func (c cmdable) BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd {