Skip to content

redis: return to the caller when the request deadline expires - #1286

Open
nacx wants to merge 1 commit into
envoyproxy:mainfrom
nacx:pr/f-request-deadline
Open

nacx wants to merge 1 commit into
envoyproxy:mainfrom
nacx:pr/f-request-deadline

Conversation

@nacx

@nacx nacx commented Oct 22, 2026

Copy link
Copy Markdown
Member

radix v4 does not return when a command's context is done: the caller keeps waiting until the connection reads the reply or fails, and it does so while holding the pool's read lock. So although the gRPC deadline set by Envoy (20ms by default in Envoy Gateway) is passed all the way down to PipeDo, a call to an unresponsive Redis blocks far beyond it, piling up goroutines and in-flight requests.

Run each Redis call on its own goroutine and return as soon as the call completes or the context is done, whichever comes first. The call finishes in the background, bounded by the I/O timeout. Since it may outlive PipeDo, replies are decoded into private receivers and copied into the caller's only when PipeDo succeeds, so a call the caller gave up on never writes into its memory. PipeDo documents that receivers are only written on success.

Calls whose context is already done are not sent to Redis: radix treats that case as a connection error and discards a healthy connection.

Add REDIS_MAX_INFLIGHT / REDIS_PERSECOND_MAX_INFLIGHT (default 0, unbounded) to cap the commands in flight per client, including those still running after their caller gave up, so a slow Redis cannot pile up unbounded work.

radix v4 does not return when a command's context is done: the caller
keeps waiting until the connection reads the reply or fails, and it
does so while holding the pool's read lock. So although the gRPC
deadline set by Envoy (20ms by default in Envoy Gateway) is passed all
the way down to PipeDo, a call to an unresponsive Redis blocks far
beyond it, piling up goroutines and in-flight requests.

Run each Redis call on its own goroutine and return as soon as the
call completes or the context is done, whichever comes first. The call
finishes in the background, bounded by the I/O timeout. Since it may
outlive PipeDo, replies are decoded into private receivers and copied
into the caller's only when PipeDo succeeds, so a call the caller gave
up on never writes into its memory. PipeDo documents that receivers
are only written on success.

Calls whose context is already done are not sent to Redis: radix
treats that case as a connection error and discards a healthy
connection.

Add REDIS_MAX_INFLIGHT / REDIS_PERSECOND_MAX_INFLIGHT (default 0,
unbounded) to cap the commands in flight per client, including those
still running after their caller gave up, so a slow Redis cannot pile
up unbounded work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Ignasi Barrera <ignasi@tetrate.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant