Skip to content

grpc-js: Backport performance improvement patches to 1.14.x branch - #3088

Merged
murgatroid99 merged 3 commits into
grpc:@grpc/grpc-js@1.14.xfrom
murgatroid99:grpc-js_perf_improvement_backport
Sep 16, 2026
Merged

murgatroid99 merged 3 commits into
grpc:@grpc/grpc-js@1.14.xfrom
murgatroid99:grpc-js_perf_improvement_backport

Conversation

@murgatroid99

Copy link
Copy Markdown
Member

Backport #3082 and #3084 to 1.14.x.

Previously, each layer of an RPC (ResolvingCall, RetryingCall,
LoadBalancingCall, and Http2SubchannelCall) allocated its own separate
call number via getNextCallNumber(). This made correlating logs across
layers difficult and consumed multiple IDs per logical RPC.

Additionally, dynamic trace arguments such as string concatenations,
JSON.stringify(), and deadline formatting were evaluated eagerly at call
sites even when tracing was disabled, adding unnecessary garbage
collection pressure on the fast path.

This change:
1. Passes the initial callNumber from ResolvingCall down through
   RetryingCall, LoadBalancingCall, and Http2SubchannelCall so that all
   layers of an attempt share the same call number. Subsequent retries
   and hedged attempts allocate a new call number per attempt.
2. Short-circuits isTracerEnabled() when no tracers are active and guards
   dynamic trace argument evaluation behind traceEnabled checks so that
   disabled tracers incur zero string or formatting allocations.
When a call completes, destroyHttp2Stream() invokes http2Stream.end() if
the server ended the call. However, for unary and server-streaming calls
(as well as client streams where writing already finished), halfClose()
has already ended the stream.

In Node.js, calling .end() on an already finished stream without a
callback causes Node's stream internals to construct an
ERR_STREAM_ALREADY_FINISHED Error with a full V8 stack trace, only to
immediately discard it. Under high throughput, this introduces significant
CPU overhead and garbage collection pressure.

This change checks writableEnded before calling http2Stream.end() in
destroyHttp2Stream() and halfClose(), ensuring .end() is only called when
the client side of the stream has not yet been closed.
Add explicit type cast for the intercepted HTTP/2 stream and type arguments
for makeClientStreamRequest.
@murgatroid99
murgatroid99 merged commit 748ae86 into grpc:@grpc/grpc-js@1.14.x Sep 16, 2026
4 of 5 checks passed
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.

2 participants