Repository navigation
Add trace spans to the Rust io-context crate - #8034
Draft
bartlomieju wants to merge 4 commits into
Draft
bartlomieju wants to merge 4 commits into
bartlomieju wants to merge 4 commits into
Conversation
Rust APIs need it for failures that only a bug causes, such as an IoContext that is no longer current. Throwing the error gives JavaScript a generic "internal error; reference = <id>" Error and logs the message with that ID. A new test checks the redaction.
IoContext::await_io() waits for a Rust future as part of the request, then runs a callback with its output in the IoContext. It goes through IoContext::awaitIoAndRun(), a new public variant of awaitIo() for callers that settle no jsg::Promise. Both now share the I/O half of awaitIoImpl(), split out as resumeAfterIo(), so the callback runs like awaitIo()'s does: holding a pending event, in the critical section and async context frame current when the wait began, and recorded for async tracing. If the request ends first, the future and callback are dropped without the callback running. IoContext::add_object() gives the IoContext ownership of a value, as C++ addObject() does. It returns IoOwned<T>, from which the value can only be taken out, rather than used in place as IoOwn<T> is.
IoContext::make_trace_span() and make_user_trace_span() start spans as C++ makeTraceSpan() and makeUserTraceSpan() do, returning a SpanBuilder or TraceContext that holds the C++ object and ends the span when dropped. SpanBuilder::set_tag(), SpanBuilder::new_child() and TraceContext::set_tag() mirror their C++ counterparts. Span names and tag keys are &'static CStr, written as c"..." literals. C++ wraps them without copying, as it does its own string literals. A tag value can be a bool, an integer, a float, a literal, or a borrowed string, which is copied only if the span is observed. A user trace span started before await_io() is adopted by it for async tracing, as for awaitIo(), and a test checks this.
The memory_cache_fallback span was tagged fallback_success=true before the fallback's result was validated. If validation then threw, as for a NaN expiration or a value that cannot be serialized, the catch handler overwrote the tag with false. The final tags were right, but overwriting a tag is treated as a programming error, logged at ERROR in predictable mode. The tag is now set once the result has been accepted. A test records the spans of two rejected results and checks their tags, failing on the overwrite's log without the fix.
|
The generated output of Full Type Diff |
bartlomieju
force-pushed
the
biwanczuk/rust-io-context-await-io
branch
from
October 28, 2026 21:58
05d31f8 to
42efd1c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #8030; only the last two commits are new here.
This gives Rust APIs the trace spans that porting
MemoryCache.read()to Rust needs, so the Rust port records the same spans and tags as
the C++ implementation.
API
IoContext::make_trace_span()andmake_user_trace_span()callC++
makeTraceSpan()andmakeUserTraceSpan(), so spans arechildren of the span current in the async context frame.
SpanBuilderandTraceContexthold the C++ objects and end thespans when dropped. Like the C++ types, they stay on the
IoContext's thread: neither isSendorSync.SpanBuilder::set_tag(),SpanBuilder::new_child()andTraceContext::set_tag()mirror their C++ counterparts.Strings
C++ expects span names and tag keys to be string literals, which it
does not copy. The Rust API takes them as
&'static CStr(c"..."literals), which cross the bridge with their NUL, so C++ wraps them
in a
kj::ConstStringwith a null disposer, as it does_kjcliterals. A tag value can be a bool, an integer, a float, a literal
(not copied either), or a borrowed string, which is copied only if
the span is observed.
Async tracing
As in C++, a user trace span started just before
await_io()is anoperation that the wait is reported under, instead of a bridge of its
own. This works because
await_io()sharesawaitIo()'simplementation (#8030).
Tests
io-context-test.c++records spans through a testSpanObserver:tags of each type, and a tag set twice keeps its last value
SpanBuilderis droppedawait_io()reports its wait under the operation of a user tracespan started before it
C++ MemoryCache:
fallback_successoverwriteA separate commit fixes a span tag bug in the C++
MemoryCache.read(), which the Rust port has to reproduce tag for tag. Thememory_cache_fallbackspan was taggedfallback_success=truebefore the fallback's result was validated; when validation then threw (a NaN expiration, or a value that cannot be serialized), the catch handler overwrote the tag withfalse. The final tags were right, buttrace.c++treats overwriting a tag as a programming error and logs it at ERROR in predictable mode. The tag is now set once the result is accepted. A new test inmemory-cache-test.c++records the spans of two rejected results and checks their tags; without the fix it fails on the overwrite's log.