Skip to content

feat(transcode): propagate W3C trace-context and request deadlines to gRPC#32

Merged
polaz merged 5 commits into
mainfrom
feat/#31-context-propagation
Jun 20, 2026
Merged

feat(transcode): propagate W3C trace-context and request deadlines to gRPC#32
polaz merged 5 commits into
mainfrom
feat/#31-context-propagation

Conversation

@polaz

@polaz polaz commented Jun 19, 2026

Copy link
Copy Markdown
Member

Summary

Request context now crosses the REST→gRPC boundary, so distributed traces stay connected and client deadlines reach the upstream. Both apply to the unary and server-streaming transcoding paths.

What's added

  • Trace-context (W3C) — an incoming traceparent (and tracestate) is forwarded verbatim into gRPC metadata. When no traceparent is present, a fresh one is synthesized (random 16-byte trace-id + 8-byte span-id, -01 sampled) so even a non-instrumented client produces a single joinable trace across the boundary.
  • Deadline — a gRPC-format grpc-timeout header (<int><unit>, units H/M/S/m/u/n) is parsed and set as the upstream call timeout via tonic. Malformed or overflowing values are ignored and the channel default applies.

Design

http_headers_to_grpc_metadata now also injects trace-context; apply_request_deadline returns the applied Duration (testable wiring). Parsing and trace-id generation are pure, unit-tested helpers. Timeout math uses checked arithmetic (overflow → ignored, not saturated).

Testing

12 metadata tests: traceparent forward vs synthesize (valid W3C shape + uniqueness), tracestate forwarding, grpc-timeout parsing per unit + malformed/overflow rejection, deadline applied/no-op. cargo nextest run --features redis: 100 passed. clippy (all-features) + fmt clean.

Docs

README: trace-context + deadline propagation moved from Roadmap to Features.

Closes #31

Carry request context across the REST→gRPC boundary so traces stay
connected and client deadlines reach the upstream:

- Trace-context: forward an incoming traceparent (and tracestate) into
  gRPC metadata, or synthesize a fresh one (random 16-byte trace-id +
  8-byte span-id, sampled) when absent, so a non-instrumented client
  still produces a single joinable trace.
- Deadline: parse a gRPC-format grpc-timeout header (<int><unit>, units
  H/M/S/m/u/n) and set it as the upstream call timeout; malformed or
  overflowing values are ignored and the channel default applies.

Both apply to the unary and server-streaming transcoding paths.

Closes #31
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b5e35f62-7121-455f-a257-c08896c79a24

📥 Commits

Reviewing files that changed from the base of the PR and between 63126a1 and 70573be.

📒 Files selected for processing (1)
  • src/transcode/metadata.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Enhanced REST↔gRPC context propagation: forwards valid W3C traceparent/tracestate and synthesizes a fresh traceparent when missing or invalid.
    • Propagates client deadlines by converting HTTP grpc-timeout into gRPC request timeouts for transcoding (including streaming).
  • Documentation
    • Updated the Features and Roadmap sections to reflect enforced context propagation behavior.
  • Tests
    • Expanded coverage for valid/invalid trace-context forwarding and comprehensive grpc-timeout parsing and rejection cases.

Walkthrough

Adds context propagation across the REST↔gRPC boundary: W3C traceparent/tracestate headers are forwarded or synthesized into gRPC metadata, and the grpc-timeout header is parsed into a tonic::Request deadline. Both propagation paths are wired into the unary and server-streaming transcoding handlers. README is updated accordingly.

Changes

Context Propagation: W3C Trace-Context and Deadline

Layer / File(s) Summary
W3C trace-context forwarding and synthesis
src/transcode/metadata.rs
Module documentation describes trace propagation behavior. Helper functions validate traceparent format against W3C spec and all-zero restrictions, generate new trace IDs using RNG with lowercase hex encoding, and insert ASCII-only metadata. http_headers_to_grpc_metadata calls inject_trace_context to forward existing traceparent/tracestate or synthesize fresh W3C-compliant trace IDs. Tests validate forwarding, synthesis, per-call uniqueness, and rejection of invalid traceparent formats.
grpc-timeout deadline parsing and application
src/transcode/metadata.rs
Exported apply_request_deadline reads the grpc-timeout header and delegates to parse_grpc_timeout to convert <int><unit> format to Duration with support for units H/M/S/m/u/n, arithmetic overflow guarding, and 8-digit limit enforcement. Result is applied to the tonic::Request timeout. Tests cover all time units, malformed input rejection, 8-digit limit enforcement, zero-duration rejection, and absent-header behavior.
Handler wiring and documentation
src/transcode/mod.rs, README.md
streaming_handler and transcode_handler each call apply_request_deadline on the prepared tonic::Request after metadata assignment. README Features section gains a context propagation bullet documenting trace-context forwarding/synthesis and deadline propagation, and the Roadmap no longer lists context propagation as planned.

Sequence Diagram(s)

sequenceDiagram
    participant Client as HTTP Client
    participant Handler as streaming_handler / transcode_handler
    participant Meta as metadata module
    participant gRPC as gRPC upstream

    Client->>Handler: HTTP request (traceparent?, grpc-timeout?)
    Handler->>Meta: http_headers_to_grpc_metadata(headers)
    Note over Meta: Forward or synthesize traceparent/tracestate
    Meta-->>Handler: MetadataMap with trace-context
    Handler->>Meta: apply_request_deadline(&mut request, headers)
    Note over Meta: Parse grpc-timeout → Duration
    Meta-->>Handler: Option<Duration> applied to request timeout
    Handler->>gRPC: tonic::Request with metadata + deadline
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: W3C trace-context propagation and request deadline propagation to gRPC across the REST↔gRPC boundary.
Description check ✅ Passed The description comprehensively relates to the changeset, covering trace-context forwarding/synthesis, deadline parsing, implementation details, testing, and documentation updates.
Linked Issues check ✅ Passed The PR fully satisfies all objectives from issue #31: W3C traceparent/tracestate forwarding with synthesis, grpc-timeout parsing for both unary and server-streaming paths, 12+ unit tests, and README documentation updates.
Out of Scope Changes check ✅ Passed All changes align with the linked issue scope: metadata handling, deadline parsing, transcoding integration, tests, and README. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#31-context-propagation

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/transcode/metadata.rs`:
- Around line 241-259: The test case in the
grpc_timeout_rejects_malformed_and_overflow function has a misleading comment.
The value "99999999999999999999H" is too large to parse as u64, so it fails at
the parsing step rather than exercising the checked_mul overflow guard. Replace
this test case with a value that successfully parses as u64 but overflows when
multiplied by 3600 (the seconds multiplier for hours), such as a value slightly
above u64::MAX divided by 3600, to properly test the overflow protection
mechanism.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6f818547-b43a-4244-ae0d-4d4796e7251f

📥 Commits

Reviewing files that changed from the base of the PR and between b4986d9 and 6847bb8.

📒 Files selected for processing (3)
  • README.md
  • src/transcode/metadata.rs
  • src/transcode/mod.rs

Comment thread src/transcode/metadata.rs
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR propagates W3C trace-context and client deadlines across the REST→gRPC boundary for both the unary and server-streaming transcoding paths. All three issues flagged in the previous review round (zero-duration deadlines, the 8-digit gRPC spec limit, and all-zeros / malformed traceparents) have been addressed with matching tests.

  • Trace-context: inject_trace_context validates incoming traceparent per W3C §3.2.2 (structure, non-zero IDs, ff version rejection, future-version tolerance) before forwarding; otherwise synthesizes a fresh one using getrandom. tracestate only travels with a successfully validated traceparent.
  • Deadline: parse_grpc_timeout covers all six gRPC timeout units, enforces the 8-digit spec cap, rejects zero durations, and apply_request_deadline is wired into both handler paths.
  • Tests: 12 new unit tests covering forwarding, synthesis, uniqueness, per-unit parsing, malformed/zero/oversized rejection, and the applied-vs-noop deadline paths.

Confidence Score: 5/5

Safe to merge. The new trace-context and deadline paths are well-guarded: invalid traceparents are rejected and replaced, zero/oversized deadlines are ignored, and both handler paths receive the same treatment.

The implementation correctly handles all the previously-identified edge cases, and the logic for header parsing, validation, and synthesis is straightforward and thoroughly tested. The one remaining edge case (tracestate leaking when tracestate is manually added to forwarded_headers alongside an invalid traceparent) requires a non-default configuration and produces incorrect trace metadata rather than any functional breakage.

src/transcode/metadata.rs — the tracestate/forwarded_headers interaction noted above is the only item worth a second look before merging.

Important Files Changed

Filename Overview
src/transcode/metadata.rs Core of the PR: adds W3C traceparent forwarding/synthesis and grpc-timeout parsing. All three previously-flagged issues (zero-duration, 8-digit limit, all-zeros traceparent) are correctly fixed with tests. Logic is sound; one minor edge case around tracestate leaking via forwarded_headers when the incoming traceparent is invalid.
src/transcode/mod.rs Wires apply_request_deadline into both the unary (transcode_handler) and server-streaming (streaming_handler) paths — one-liner addition in each, correct placement after metadata is built.
README.md Moves context-propagation from the Roadmap section to Features, accurately reflecting the new capability.

Reviews (3): Last reviewed commit: "fix(transcode): accept future W3C tracep..." | Re-trigger Greptile

Comment thread src/transcode/metadata.rs
Comment thread src/transcode/metadata.rs
Comment thread src/transcode/metadata.rs
polaz added 2 commits June 20, 2026 05:43
- A zero grpc-timeout ("0S") parses to a zero deadline, which makes the
  upstream call expire immediately with DEADLINE_EXCEEDED.
- grpc-timeout accepts more than the spec's 8-digit limit, letting a
  client set an absurd multi-millennia timeout.
- An all-zeros traceparent (invalid per W3C 3.2.2) is forwarded verbatim
  instead of being replaced with a fresh, valid one.

All three fail on current code; fixes follow.

Part of #31
- An incoming traceparent is forwarded only when well-formed per W3C
  3.2.2 (correct structure, non-zero trace-id and parent-id); otherwise a
  fresh one is synthesized, so an all-zeros or malformed value from a
  buggy client can't break trace continuity upstream. tracestate now only
  travels alongside a forwarded (valid) traceparent.
- grpc-timeout is capped at the spec's 8 digits and a zero duration is
  rejected (falls back to the channel default) instead of setting an
  already-expired deadline that fails every call with DEADLINE_EXCEEDED.
  With the digit cap the unit multiplications provably cannot overflow,
  so they no longer need checked arithmetic.

Part of #31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/transcode/metadata.rs`:
- Around line 66-75: The is_valid_traceparent function currently rejects
traceparent headers with any version other than "00" by using version == "00" in
the validation logic, which violates the W3C Trace Context specification. Update
the version validation to accept any valid 2-character hexadecimal version
except "ff" instead of requiring an exact match to "00", while keeping all other
validation checks for trace_id, parent_id, and flags unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: be4f9a83-c080-4d71-90f1-3c50b63a279a

📥 Commits

Reviewing files that changed from the base of the PR and between 6847bb8 and 63126a1.

📒 Files selected for processing (1)
  • src/transcode/metadata.rs

Comment thread src/transcode/metadata.rs Outdated
polaz added 2 commits June 20, 2026 06:35
The validator only accepts version "00", so a W3C-valid versioned
traceparent (e.g. "01-...") is dropped and replaced with a synthesized
one, breaking trace continuity for clients using future versions.

Fails on current code; fix follows.

Part of #31
The validator only accepted version "00", so a W3C-valid versioned
traceparent was dropped and resynthesized, breaking trace continuity for
clients using newer versions. It now accepts any 2-hex version except the
reserved "ff", keeps the baseline "00" at exactly four fields (future
versions may append more), and retains the trace-id / parent-id integrity
and non-zero checks.

Part of #31
@polaz
polaz merged commit be70bc4 into main Jun 20, 2026
3 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.

feat(transcode): propagate W3C trace-context and request deadlines to gRPC

1 participant