Skip to content

Commit ce36baf

Browse files
committed
redis.Client#XReadUntilResult(): Respect context errors
Explicitly check for context errors because go-redis v9 does not respect `context.Canceled` or `context.DeadlineExceeded` unless `Options.ContextTimeoutEnabled` is set [^1] [^2], which we do not enable. If the context is canceled or times out during `XRead()` and there is no data to read, `XRead()` will **still** block until the block timeout is reached and return `redis.Nil` instead of the context error. Without this check, the function would return `redis.Nil`, potentially leading to unexpected errors for consumers. With this change, we can also remove the check for no context error when checking if the error returned by `XRead()` is retryable. [^1]: redis/go-redis#2556 [^2]: redis/go-redis#2682
1 parent db81704 commit ce36baf

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

redis/client.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,19 @@ func (c *Client) XReadUntilResult(ctx context.Context, a *redis.XReadArgs) ([]re
243243

244244
for {
245245
cmd := c.XRead(ctx, a)
246+
// Explicitly check for context errors because go-redis v9 does not respect context.Canceled or
247+
// context.DeadlineExceeded unless Options.ContextTimeoutEnabled is set [^1] [^2], which we do not enable.
248+
// If the context is canceled or times out during XRead and there is no data to read,
249+
// XRead will **still** block until the block timeout is reached and
250+
// return redis.Nil instead of the context error. Without this check,
251+
// the function would return redis.Nil, potentially leading to unexpected errors for consumers.
252+
//
253+
// [^1]: https://github.com/redis/go-redis/issues/2556
254+
// [^2]: https://github.com/redis/go-redis/issues/2682
255+
if ctx.Err() != nil {
256+
return nil, ctx.Err()
257+
}
258+
246259
streams, err := cmd.Result()
247260
if err != nil {
248261
// We need to retry the XREAD commands in the following situations:
@@ -253,11 +266,10 @@ func (c *Client) XReadUntilResult(ctx context.Context, a *redis.XReadArgs) ([]re
253266
// important to set a block timeout greater than zero for the XREAD commands, see the "a.Block" above.
254267
// However, setting a block timeout means that Go Redis will not retry any errors internally and will
255268
// instead return an I/O timeout error when exceeding the timeout. Thus, we need to handle this here and
256-
// retry it again. On the other hand, an I/O timeout could also mean a context.DeadlineExceeded error,
257-
// which is not retryable, so we have to check for context termination by ourselves via ctx.Err().
269+
// retry it again.
258270
//
259271
// [^1]: https://github.com/redis/go-redis/issues/2131
260-
if (errors.Is(err, redis.Nil) || retry.Retryable(err)) && ctx.Err() == nil {
272+
if errors.Is(err, redis.Nil) || retry.Retryable(err) {
261273
continue
262274
}
263275

0 commit comments

Comments
 (0)