Download the PHP package thesis/grpc-retry without Composer
On this page you can find all versions of the php package thesis/grpc-retry. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download thesis/grpc-retry
More information about thesis/grpc-retry
Files in thesis/grpc-retry
Package grpc-retry
Short Description Configurable retry interceptors (unary, stream) for the thesis/grpc-client, with backoff and per-status retry policies.
License MIT
Informations about the package grpc-retry
thesis/grpc-retry
Retry middleware for thesis/grpc-client: unary and streaming interceptors that transparently re-issue a failed call while its gRPC status is retryable and the attempt budget is not exhausted, pausing between attempts according to a configurable backoff.
Contents
- Installation
- Usage
- Configuration
- Backoff
- How it works
- Streaming caveats
- Relation to gRFC A6
Installation
Usage
Retry\Interceptor implements both client interceptor contracts, so a single instance can be
registered on both chains: the unary chain retries request → response calls, the stream chain
retries streams. Register it on whichever chains you need.
Configuration
Retry\Config is an immutable dto:
| Option | Default | Meaning |
|---|---|---|
maxAttempts |
3 |
Total number of attempts. 3 means the initial call plus two retries. |
retryableCodes |
[Code::UNAVAILABLE] |
A retry happens only when the failure carries one of these statuses. |
backoff |
Backoff\Exponential |
How long to wait before each retry. |
maxBufferedMessages |
256 |
How many client messages a stream may buffer for a possible replay. |
Transport-level failures (a refused or dropped connection) are surfaced by the client as
InvokeError with Code::UNAVAILABLE, so the default policy already covers them. Add more codes
(e.g. Code::RESOURCE_EXHAUSTED, Code::DEADLINE_EXCEEDED) only for calls where retrying them is
safe.
Backoff
Backoff decides how long to pause before the n-th retry:
Backoff\Exponential
Exponentially growing delay, capped at max, with random jitter to avoid a thundering herd of
clients retrying in lockstep:
Jitter is drawn from an injectable Random\Randomizer, so tests can pass a seeded engine for a
deterministic delay:
Backoff\Fixed
The same delay before every retry:
Custom
Implement Backoff for any other schedule (decorrelated jitter, a fixed table, no delay at all).
How it works
Both interceptors are endpoint-agnostic: failover is handled by the transport. Every re-issue reuses
the call's PickContext, so the load balancer skips the endpoint that just failed whenever it has an
alternative. Waits between attempts honour the call's Cancellation: a cancelled call or an expired
deadline aborts the wait instead of sleeping it out.
- Unary (
interceptUnary) re-invokes the call in a loop: on anInvokeErrorwhose status is retryable it waits forbackoff->delay(...)and tries again, until the call succeeds or the attempt budget is exhausted, at which point the last error is rethrown. - Stream (
interceptStream) wraps the stream in a decorator that buffers the client's messages and the half-close, and re-opens the stream on a retryable failure, replaying the buffer onto the fresh stream.
Streaming caveats
A stream can only be retried while no server message has been observed yet. As soon as receive()
or iteration yields a value, the call is committed: any later failure is surfaced to the caller
unchanged, because replaying it could duplicate already-processed responses. Until then, every sent
message is buffered in memory so it can be replayed. That buffer is bounded by maxBufferedMessages:
a client- or bidirectional stream that sends more than that before the first response simply stops
being retryable (a partial replay would be wrong) and releases the buffer, so memory stays bounded.
Because retries replay the request, only enable them for calls that are safe to execute more than once. Retrying is inherently safe for reads; for writes it requires idempotency (e.g. an idempotency key or a conditional update).
Relation to gRFC A6
The buffer/replay/commit model follows gRFC A6 "client retries":
outgoing messages are buffered so a fresh attempt can replay them, and the call commits — retries
stop and the buffer is freed — once it can no longer be safely replayed. The backoff formula
(min(base * factor^(n-1), max) * random(0.8, 1.2)) and the retryableStatusCodes check come from
A6 as well.
It is a deliberate subset, adapted to what a client interceptor can observe:
- Commit point. A6 commits when the client receives Response-Headers; this library commits on the first received message (or on a buffer overflow), because the interceptor layer sees messages, not the header event. A retry can therefore still happen in the narrow window after headers but before the first message.
- Buffer bound. A6 leaves the unit unspecified; this library bounds the buffer by message count
(
maxBufferedMessages), since it holds unserialized messages. - Not implemented: retry throttling (token bucket), server pushback (
grpc-retry-pushback-ms), and transparent retries.
All versions of grpc-retry with dependencies
amphp/amp Version ^3.1
thesis/googleapis-rpc-types Version ^0.2
thesis/grpc-client Version ^0.2
thesis/grpc-protocol Version ^0.2