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:
32
command.go
32
command.go
@@ -1585,6 +1585,12 @@ func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
|
|||||||
type XMessage struct {
|
type XMessage struct {
|
||||||
ID string
|
ID string
|
||||||
Values map[string]interface{}
|
Values map[string]interface{}
|
||||||
|
// MillisElapsedFromDelivery is the number of milliseconds since the entry was last delivered.
|
||||||
|
// Only populated when using XREADGROUP with CLAIM argument for claimed entries.
|
||||||
|
MillisElapsedFromDelivery int64
|
||||||
|
// DeliveredCount is the number of times the entry was delivered.
|
||||||
|
// Only populated when using XREADGROUP with CLAIM argument for claimed entries.
|
||||||
|
DeliveredCount int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type XMessageSliceCmd struct {
|
type XMessageSliceCmd struct {
|
||||||
@@ -1641,10 +1647,16 @@ func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readXMessage(rd *proto.Reader) (XMessage, error) {
|
func readXMessage(rd *proto.Reader) (XMessage, error) {
|
||||||
if err := rd.ReadFixedArrayLen(2); err != nil {
|
// Read array length can be 2 or 4 (with CLAIM metadata)
|
||||||
|
n, err := rd.ReadArrayLen()
|
||||||
|
if err != nil {
|
||||||
return XMessage{}, err
|
return XMessage{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n != 2 && n != 4 {
|
||||||
|
return XMessage{}, fmt.Errorf("redis: got %d elements in the XMessage array, expected 2 or 4", n)
|
||||||
|
}
|
||||||
|
|
||||||
id, err := rd.ReadString()
|
id, err := rd.ReadString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return XMessage{}, err
|
return XMessage{}, err
|
||||||
@@ -1657,10 +1669,24 @@ func readXMessage(rd *proto.Reader) (XMessage, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return XMessage{
|
msg := XMessage{
|
||||||
ID: id,
|
ID: id,
|
||||||
Values: v,
|
Values: v,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
|
if n == 4 {
|
||||||
|
msg.MillisElapsedFromDelivery, err = rd.ReadInt()
|
||||||
|
if err != nil {
|
||||||
|
return XMessage{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.DeliveredCount, err = rd.ReadInt()
|
||||||
|
if err != nil {
|
||||||
|
return XMessage{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func stringInterfaceMapParser(rd *proto.Reader) (map[string]interface{}, error) {
|
func stringInterfaceMapParser(rd *proto.Reader) (map[string]interface{}, error) {
|
||||||
|
|||||||
236
commands_test.go
236
commands_test.go
@@ -6749,6 +6749,242 @@ var _ = Describe("Commands", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(n).To(Equal(int64(2)))
|
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() {
|
Describe("xinfo", func() {
|
||||||
|
|||||||
@@ -216,8 +216,8 @@ func ExampleClient_racefrance1() {
|
|||||||
// REMOVE_END
|
// REMOVE_END
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}]
|
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]
|
||||||
// [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]}]}]
|
// [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0}]}]
|
||||||
// 4
|
// 4
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,13 +467,13 @@ func ExampleClient_racefrance2() {
|
|||||||
// STEP_END
|
// STEP_END
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}]
|
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0} {1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
|
||||||
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]}]
|
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]
|
||||||
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}]
|
// [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]
|
||||||
// [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7]} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}]
|
// [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
|
||||||
// []
|
// []
|
||||||
// [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9]}]
|
// [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
|
||||||
// [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2]} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8]}]}]
|
// [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleClient_xgroupcreate() {
|
func ExampleClient_xgroupcreate() {
|
||||||
@@ -999,18 +999,18 @@ func ExampleClient_raceitaly() {
|
|||||||
// REMOVE_END
|
// REMOVE_END
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [{race:italy [{1692632639151-0 map[rider:Castilla]}]}]
|
// [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
|
||||||
// 1
|
// 1
|
||||||
// [{race:italy []}]
|
// [{race:italy []}]
|
||||||
// [{race:italy [{1692632647899-0 map[rider:Royce]} {1692632662819-0 map[rider:Sam-Bodden]}]}]
|
// [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]
|
||||||
// &{2 1692632647899-0 1692632662819-0 map[Bob:2]}
|
// &{2 1692632647899-0 1692632662819-0 map[Bob:2]}
|
||||||
// [{1692632647899-0 map[rider:Royce]}]
|
// [{1692632647899-0 map[rider:Royce] 0 0}]
|
||||||
// [{1692632647899-0 map[rider:Royce]}]
|
// [{1692632647899-0 map[rider:Royce] 0 0}]
|
||||||
// [{1692632647899-0 map[rider:Royce]}]
|
// [{1692632647899-0 map[rider:Royce] 0 0}]
|
||||||
// 1692632662819-0
|
// 1692632662819-0
|
||||||
// []
|
// []
|
||||||
// 0-0
|
// 0-0
|
||||||
// &{5 1 2 1 1692632678249-0 0-0 5 {1692632639151-0 map[rider:Castilla]} {1692632678249-0 map[rider:Norem]} 1692632639151-0}
|
// &{5 1 2 1 1692632678249-0 0-0 5 {1692632639151-0 map[rider:Castilla] 0 0} {1692632678249-0 map[rider:Norem] 0 0} 1692632639151-0}
|
||||||
// [{italy_riders 3 2 1692632662819-0 3 2}]
|
// [{italy_riders 3 2 1692632662819-0 3 2}]
|
||||||
// 2
|
// 2
|
||||||
// 0
|
// 0
|
||||||
@@ -1085,7 +1085,7 @@ func ExampleClient_xdel() {
|
|||||||
// STEP_END
|
// STEP_END
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// [{1692633198206-0 map[rider:Wood]} {1692633208557-0 map[rider:Henshaw]}]
|
// [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]
|
||||||
// 1
|
// 1
|
||||||
// [{1692633198206-0 map[rider:Wood]}]
|
// [{1692633198206-0 map[rider:Wood] 0 0}]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ type XReadGroupArgs struct {
|
|||||||
Count int64
|
Count int64
|
||||||
Block time.Duration
|
Block time.Duration
|
||||||
NoAck bool
|
NoAck bool
|
||||||
|
Claim time.Duration // Claim idle pending entries older than this duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c cmdable) XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd {
|
func (c cmdable) XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSliceCmd {
|
||||||
@@ -282,6 +283,10 @@ func (c cmdable) XReadGroup(ctx context.Context, a *XReadGroupArgs) *XStreamSlic
|
|||||||
args = append(args, "noack")
|
args = append(args, "noack")
|
||||||
keyPos++
|
keyPos++
|
||||||
}
|
}
|
||||||
|
if a.Claim > 0 {
|
||||||
|
args = append(args, "claim", int64(a.Claim/time.Millisecond))
|
||||||
|
keyPos += 2
|
||||||
|
}
|
||||||
args = append(args, "streams")
|
args = append(args, "streams")
|
||||||
keyPos++
|
keyPos++
|
||||||
for _, s := range a.Streams {
|
for _, s := range a.Streams {
|
||||||
|
|||||||
Reference in New Issue
Block a user