Skip to content

Propagate inbound W3C baggage through gRPC server handlers - #12721

Draft
sy-lee wants to merge 2 commits into
DataDog:masterfrom
sy-lee:suet/armeria-grpc-baggage-context
Draft

sy-lee wants to merge 2 commits into
DataDog:masterfrom
sy-lee:suet/armeria-grpc-baggage-context

Conversation

@sy-lee

@sy-lee sy-lee commented Oct 1, 2026

Copy link
Copy Markdown

What Does This Do

Makes the full extracted context, including W3C baggage, current for the whole gRPC server call in both grpc-1.5 and armeria-grpc-0.84. Also makes the grpc-1.5 client inject the full current context.

  • Server (grpc-1.5, armeria-grpc-0.84): TracingServerInterceptor extracts with Propagators.defaultPropagator().extract(root(), headers, GETTER) in place of the deprecated AgentPropagation.extractContextAndGetSpanContext. It starts the grpc.server span from the extracted span context as before, then runs startCall, ServerCall.close and every listener callback (onMessage, onHalfClose, onCancel, onComplete, onReady) under parentContext.with(span).attach() instead of the span-only activateSpan(span). onMessage attaches context.with(msgSpan), so the grpc.message span stays the active span. Scopes are still closed by the same try-with-resources blocks.
  • Client (grpc-1.5): StartContextPropagationAdvice injects currentContext() instead of only the active span. It keeps the existing "no active span, no injection" guard. This matches what the armeria-grpc client already does.

Motivation

Solves #12654.

Today both server instrumentations extract inbound baggage (it shows up as baggage.* tags on the server span) but make only the span current. As a result:

  • Baggage is missing from the current context in the service implementation (empty Baggage.current() through the OTel bridge).
  • Calls made from the handler don't re-inject baggage.

HTTP server instrumentations have used full-context extraction since #8820 (HttpServerDecorator.extract / startSpan returns parentContext.with(span)), so this follows that pattern.

The grpc-1.5 client change is needed for the "outbound calls carry baggage" half of the issue when the downstream call is itself a grpc-java call. Without it, the server fix alone makes baggage current in the handler, but ClientCallImplInstrumentation injects only activeSpan(), so the baggage header is still dropped.

Additional Notes

Tests: each module's GrpcTest / ArmeriaGrpcTest gets a new case. A client sends baggage: user.id=abc123,jtbd=checkout. The test asserts that the handler sees those items in Baggage.fromContext(Context.current()), and that a downstream gRPC call made from the handler (to an inbound-ignored method, whose raw headers are captured by a ServerInterceptor) carries the same baggage header.

Local results (JDK 21, macOS arm64):

armeria-grpc-0.84  test 61/61, forkedTest 50/50, latestDepTest 61/61, latestDepForkedTest 50/50
grpc-1.5           test 121/121, forkedTest 71/71, latestDepTest 121/121

With the server change reverted, the new test fails in both modules at the handler assertion (Baggage is null). With the grpc-1.5 client change reverted, the grpc-1.5 test fails at the downstream-header assertion.

Behaviour note: Context.attach() replaces the current context, whereas activateSpan(span) merges the span into whatever context is already active on the thread. This is the same trade-off the HTTP server instrumentations already make. The extracted context is now the root for the call.

Other callers of the deprecated helper listed in the issue (sofarpc, messaging consumers, API shims) are out of scope here.

Suggested labels for maintainers (as a fork contributor I can't set labels): tag: ai generated, inst: grpc, type: bug fix, comp: context propagation.

Contributor Checklist

Jira ticket: [PROJ-IDENT]

🤖 Generated with Claude Code

sy-lee and others added 2 commits October 1, 2026 15:53
… handler

TracingServerInterceptor in grpc-1.5 and armeria-grpc-0.84 used the
deprecated AgentPropagation.extractContextAndGetSpanContext, which drops
every extracted element except the span context, and then ran startCall,
close and the listener callbacks under a span-only activateSpan(span).
Inbound W3C baggage was tagged on the server span but was not current in
the service implementation, so Baggage was missing from Context.current()
and outbound calls did not re-inject it.

Extract the full context with the default propagator and attach
parentContext.with(span), mirroring HttpServerDecorator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StartContextPropagationAdvice injected only the active span, so W3C
baggage in the current context never reached outbound grpc-java calls.
Inject currentContext() instead, as the armeria-grpc client already does,
keeping the existing guard that skips injection without an active span.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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