Skip to content

xDS: Support retries in RawMessageClientInterceptor and conditionally add it to the filter chain - #13042

Open
kannanjgithub wants to merge 2 commits into
grpc:masterfrom
kannanjgithub:ext_proc_support_retries
Open

xDS: Support retries in RawMessageClientInterceptor and conditionally add it to the filter chain#13042
kannanjgithub wants to merge 2 commits into
grpc:masterfrom
kannanjgithub:ext_proc_support_retries

Conversation

@kannanjgithub

@kannanjgithub kannanjgithub commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

1. Supporting retries

Previously, RawMessageClientInterceptor.sendMessage() forwarded a single-use
InputStream directly to rawCall.sendMessage(). When retries were triggered by
RetriableStream, attempt 1 drained the InputStream to EOF. Subsequent retry
attempts would then serialize the already-drained stream, sending an empty (0-byte)
request payload.

Fix this by:

  1. Buffering the request stream into an immutable ByteString (using Drainable
    when supported) in RawMessageClientInterceptor.sendMessage() and wrapping it
    in a KnownLengthInputStream.
  2. Exposing getByteString() on KnownLengthInputStream.
  3. Having RAW_MARSHALLER.stream() return a fresh KnownLengthInputStream wrapping
    the underlying ByteString on each attempt, allowing retries to read the payload
    from byte 0.

2. Conditional addition of RawMessageClientInterceptor

Also we need to only install RawMessageClientInterceptor when a filter requires payload access to avoid the performance hit for non ext_proc cases. As suggested in the grfc discussion, introduce Filter.requiresPayloadAccess(config, overrideConfig) to allow filters to declare whether they need access to message payloads. Update ExternalProcessorFilter to implement this method, returning false when body send mode is NONE for both request and response. Update XdsNameResolver to only install RawMessageClientInterceptor when at least one configured filter requires payload access. Generalize ExternalProcessorClientInterceptor to support generic request/response types when payload interception is not needed.

Generalizing ExternalProcessorClientInterceptor to Support Pass-through Types

ExternalProcessorClientInterceptor previously hardcoded ClientCall<InputStream, InputStream> and SimpleForwardingClientCallListener<InputStream>, assuming RawMessageClientInterceptor was always present earlier in the chain to convert application messages to InputStream.

Now that RawMessageClientInterceptor is omitted when body send mode is NONE, ExternalProcessorClientInterceptor directly handles the application's request and response types (ReqT, RespT). DataPlaneClientCall and DataPlaneListener were generalized to <ReqT, RespT> to avoid compiler-generated synthetic bridge casts to InputStream, which would otherwise result in a runtime ClassCastException when sending or receiving application messages.

Fixes #13010

…payload access

Introduce Filter.requiresPayloadAccess(config, overrideConfig) to allow filters
to declare whether they need access to message payloads. Update
ExternalProcessorFilter to implement this method, returning false when body
send mode is NONE for both request and response. Update XdsNameResolver to only
install RawMessageClientInterceptor when at least one configured filter requires
payload access. Generalize ExternalProcessorClientInterceptor to support
generic request/response types when payload interception is not needed.

TAG=agy
CONV=56c87717-243d-4f12-af9e-c532e509892a
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.

Retries on xds:/// channels (or with RawMessageClientInterceptor) send empty 0-byte payload on retry

1 participant