Skip to content

grpc-api: fix client serializer-cache collision across message types - #3568

Merged
bryce-anderson merged 2 commits into
apple:mainfrom
bryce-anderson:bl_anderson/repro-serializer-cache
Jul 14, 2026
Merged

grpc-api: fix client serializer-cache collision across message types#3568
bryce-anderson merged 2 commits into
apple:mainfrom
bryce-anderson:bl_anderson/repro-serializer-cache

Conversation

@bryce-anderson

@bryce-anderson bryce-anderson commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Motivation

DefaultGrpcClientCallFactory cached request serializers in two static maps keyed
only by the request BufferEncoder (the compression codec). The cached
GrpcSerializer/GrpcStreamingSerializer embeds a message-type-specific request
serializer, so the first client to send a compressed request with a given codec
won that codec's cache entry for the entire JVM. Any other client that later
reused the same codec (e.g. gzip) for a different request message type received
the wrong-typed cached serializer and failed with a ClassCastException during
request serialization, before the request was sent. Only the compressed-request
path was affected; uncompressed requests short-circuit to the identity serializer.

Modifications

  • Drop the static serializerMap and streamingSerializerMap. The compressed
    serializer is method-specific and must not be shared across methods keyed only
    by the compressor, so build it per call instead. The uncompressed path still
    returns the shared identity serializer and allocates nothing; only the
    (uncommon) compressed path now allocates a small short-lived serializer, whose
    cost is negligible next to the compression pass it wraps.
  • Add a collision regression test in GrpcLargeMessageTest: two clients that share
    the gzip request compressor for different message types (Greeter/HelloRequest
    and Tester/TestRequest); both compressed round-trips must succeed.

Result

Multiple gRPC clients in the same JVM can share a request compression codec for
different message types without colliding. No public API or behavior change.

 #### Motivation

DefaultGrpcClientCallFactory cached request serializers in two static maps keyed
only by the request BufferEncoder (the compression codec). The cached
GrpcSerializer/GrpcStreamingSerializer embeds a message-type-specific request
serializer, so the first client to send a compressed request with a given codec
won that codec's cache entry for the entire JVM. Any other client that later
reused the same codec (e.g. gzip) for a different request message type received
the wrong-typed cached serializer and failed with a ClassCastException during
request serialization, before the request was sent. Only the compressed-request
path was affected; uncompressed requests short-circuit to the identity serializer.

 #### Modifications

- Drop the static serializerMap and streamingSerializerMap. The compressed
  serializer is method-specific and must not be shared across methods keyed only
  by the compressor, so build it per call instead. The uncompressed path still
  returns the shared identity serializer and allocates nothing; only the
  (uncommon) compressed path now allocates a small short-lived serializer, whose
  cost is negligible next to the compression pass it wraps.
- Add a collision regression test in GrpcLargeMessageTest: two clients that share
  the gzip request compressor for different message types (Greeter/HelloRequest
  and Tester/TestRequest); both compressed round-trips must succeed.

 #### Result

Multiple gRPC clients in the same JVM can share a request compression codec for
different message types without colliding. No public API or behavior change.
@bryce-anderson bryce-anderson changed the title Bl anderson/repro serializer cache grpc-api: fix client serializer-cache collision across message types Jul 14, 2026

@idelpivnitskiy idelpivnitskiy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

@bryce-anderson
bryce-anderson merged commit 11dbb79 into apple:main Jul 14, 2026
19 checks passed
@bryce-anderson
bryce-anderson deleted the bl_anderson/repro-serializer-cache branch July 14, 2026 14:26
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.

3 participants