1
0
mirror of https://github.com/redis/go-redis.git synced 2025-11-05 14:10:37 +03:00

Add support for XReadGroup CLAIM argument (#3578)

* Add support for XReadGroup CLAIM argument

* modify tutorial tests

---------

Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com>
This commit is contained in:
ofekshenawa
2025-11-03 18:58:14 +02:00
committed by GitHub
parent ce53464448
commit 71bb3cae4f
4 changed files with 286 additions and 19 deletions

View File

@@ -6749,6 +6749,242 @@ var _ = Describe("Commands", func() {
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(2)))
})
It("should XReadGroup with CLAIM argument", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
time.Sleep(100 * time.Millisecond)
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group",
Consumer: "consumer2",
Streams: []string{"stream", ">"},
Claim: 50 * time.Millisecond,
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(HaveLen(1))
Expect(res[0].Stream).To(Equal("stream"))
messages := res[0].Messages
Expect(len(messages)).To(BeNumerically(">=", 1))
for _, msg := range messages {
if msg.MillisElapsedFromDelivery > 0 {
Expect(msg.MillisElapsedFromDelivery).To(BeNumerically(">=", 50))
Expect(msg.DeliveredCount).To(BeNumerically(">=", 1))
}
}
})
It("should XReadGroup with CLAIM and COUNT", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
time.Sleep(100 * time.Millisecond)
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group",
Consumer: "consumer3",
Streams: []string{"stream", ">"},
Claim: 50 * time.Millisecond,
Count: 2,
}).Result()
Expect(err).NotTo(HaveOccurred())
if len(res) > 0 && len(res[0].Messages) > 0 {
Expect(len(res[0].Messages)).To(BeNumerically("<=", 2))
}
})
It("should XReadGroup with CLAIM and NOACK", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
time.Sleep(100 * time.Millisecond)
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group",
Consumer: "consumer4",
Streams: []string{"stream", ">"},
Claim: 50 * time.Millisecond,
NoAck: true,
}).Result()
Expect(err).NotTo(HaveOccurred())
if len(res) > 0 {
Expect(res[0].Stream).To(Equal("stream"))
}
})
It("should XReadGroup CLAIM empties PEL after acknowledgment", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
time.Sleep(100 * time.Millisecond)
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group",
Consumer: "consumer5",
Streams: []string{"stream", ">"},
Claim: 50 * time.Millisecond,
}).Result()
Expect(err).NotTo(HaveOccurred())
if len(res) > 0 && len(res[0].Messages) > 0 {
ids := make([]string, len(res[0].Messages))
for i, msg := range res[0].Messages {
ids[i] = msg.ID
}
n, err := client.XAck(ctx, "stream", "group", ids...).Result()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(BeNumerically(">=", 1))
pending, err := client.XPending(ctx, "stream", "group").Result()
Expect(err).NotTo(HaveOccurred())
Expect(pending.Count).To(BeNumerically("<", 3))
}
})
It("should XReadGroup backward compatibility without CLAIM", func() {
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group",
Consumer: "consumer_compat",
Streams: []string{"stream", "0"},
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(HaveLen(1))
Expect(res[0].Stream).To(Equal("stream"))
for _, msg := range res[0].Messages {
Expect(msg.MillisElapsedFromDelivery).To(Equal(int64(0)))
Expect(msg.DeliveredCount).To(Equal(int64(0)))
}
})
It("should XReadGroup CLAIM with multiple streams", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
id, err := client.XAdd(ctx, &redis.XAddArgs{
Stream: "stream2",
ID: "1-0",
Values: map[string]interface{}{"field1": "value1"},
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(id).To(Equal("1-0"))
id, err = client.XAdd(ctx, &redis.XAddArgs{
Stream: "stream2",
ID: "2-0",
Values: map[string]interface{}{"field2": "value2"},
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(id).To(Equal("2-0"))
err = client.XGroupCreate(ctx, "stream2", "group2", "0").Err()
Expect(err).NotTo(HaveOccurred())
_, err = client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group2",
Consumer: "consumer1",
Streams: []string{"stream2", ">"},
}).Result()
Expect(err).NotTo(HaveOccurred())
time.Sleep(100 * time.Millisecond)
res, err := client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: "group2",
Consumer: "consumer2",
Streams: []string{"stream2", ">"},
Claim: 50 * time.Millisecond,
}).Result()
Expect(err).NotTo(HaveOccurred())
if len(res) > 0 {
Expect(res[0].Stream).To(Equal("stream2"))
if len(res[0].Messages) > 0 {
for _, msg := range res[0].Messages {
if msg.MillisElapsedFromDelivery > 0 {
Expect(msg.DeliveredCount).To(BeNumerically(">=", 1))
}
}
}
}
})
It("should XReadGroup CLAIM work consistently on RESP2 and RESP3", func() {
SkipBeforeRedisVersion(8.3, "XREADGROUP CLAIM requires Redis 8.3+")
streamName := "stream-resp-test"
err := client.XAdd(ctx, &redis.XAddArgs{
Stream: streamName,
Values: map[string]interface{}{"field1": "value1"},
}).Err()
Expect(err).NotTo(HaveOccurred())
err = client.XAdd(ctx, &redis.XAddArgs{
Stream: streamName,
Values: map[string]interface{}{"field2": "value2"},
}).Err()
Expect(err).NotTo(HaveOccurred())
groupName := "resp-test-group"
err = client.XGroupCreate(ctx, streamName, groupName, "0").Err()
Expect(err).NotTo(HaveOccurred())
_, err = client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: groupName,
Consumer: "consumer1",
Streams: []string{streamName, ">"},
}).Result()
Expect(err).NotTo(HaveOccurred())
time.Sleep(100 * time.Millisecond)
// Test with RESP2 (protocol 2)
resp2Client := redis.NewClient(&redis.Options{
Addr: redisAddr,
Protocol: 2,
})
defer resp2Client.Close()
resp2Result, err := resp2Client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: groupName,
Consumer: "consumer2",
Streams: []string{streamName, "0"},
Claim: 50 * time.Millisecond,
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resp2Result).To(HaveLen(1))
// Test with RESP3 (protocol 3)
resp3Client := redis.NewClient(&redis.Options{
Addr: redisAddr,
Protocol: 3,
})
defer resp3Client.Close()
resp3Result, err := resp3Client.XReadGroup(ctx, &redis.XReadGroupArgs{
Group: groupName,
Consumer: "consumer3",
Streams: []string{streamName, "0"},
Claim: 50 * time.Millisecond,
}).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resp3Result).To(HaveLen(1))
Expect(len(resp2Result[0].Messages)).To(Equal(len(resp3Result[0].Messages)))
for i := range resp2Result[0].Messages {
msg2 := resp2Result[0].Messages[i]
msg3 := resp3Result[0].Messages[i]
Expect(msg2.ID).To(Equal(msg3.ID))
if msg2.MillisElapsedFromDelivery > 0 {
Expect(msg3.MillisElapsedFromDelivery).To(BeNumerically(">", 0))
Expect(msg2.DeliveredCount).To(Equal(msg3.DeliveredCount))
}
}
})
})
Describe("xinfo", func() {