Skip to content

feat(telemetry): request-scoped spans on ingest, query, mcp, sweep (RFC 0038 slice 2) - #615

Merged
jensholdgaard merged 4 commits into
mainfrom
rfc-0038-request-spans
Jul 24, 2026
Merged

feat(telemetry): request-scoped spans on ingest, query, mcp, sweep (RFC 0038 slice 2)#615
jensholdgaard merged 4 commits into
mainfrom
rfc-0038-request-spans

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jul 24, 2026

Copy link
Copy Markdown
Owner

RFC 0038 slice 2 — request-scoped spans

Builds on the slice-1 tracer foundation (#614). This PR adds the spans at all four entry points pinned in RFC 0038 §3.5:

Span Kind Where
POST /v1/query server querier::handle_query
ourios.mcp.{query_logs,list_templates,template_drift} internal the three tool delegates (children of rmcp's serve_inner)
ourios.ingest.batch server IngestPipeline::ingest_bound
ourios.wal.commit internal child of the batch span, over the group-commit await
ourios.compaction.sweep internal run_sweep_with_promoted

All are skip_all — no request/headers/body payload on the span.

Hot-path discipline (hazard: tracing overhead)

The batch span is opened once per OTLP Export, never per record. The miner / encode-pool / record-sink inner loops stay span-free (RFC 0038 §3.2). The two spans a batch produces (ingest.batch + one wal.commit child) are O(1) in the record count, which the new test asserts directly: a 1-record batch and a 64-record batch emit the same span count (2). ourios.wal.commit is a child span, not an event, because the group-commit fsync is the one genuinely I/O-bound, durationful step.

The spans are opened inside the spawned callees (ingest_bound, the sweep body), not at the tokio::spawn / spawn_blocking sites — span context does not cross those boundaries (RFC 0038 §3.3).

Semconv cleanliness

tracing-opentelemetry injects synthetic span attributes (busy_ns/idle_ns, target, code.module.name) that fail weaver registry live-check. The telemetry layer now disables tracked-inactivity, location, and target; thread.{id,name} (valid semconv) stay. Verified against local weaver v0.23.0 (CI pin): zero violation-level advisories.

MCP span placement

#[tracing::instrument] directly on an rmcp #[tool] method drops the doc-comment tool description the macro derives (would break RFC0027.7 / RFC0032.5). Each #[tool] method stays a thin delegate — doc + attribute intact — and calls a private *_traced helper that carries the span.

Tests

rfc0038_2_ingest_batch_span.rs (consolidated it-harness, scoped WithSubscriber over an InMemorySpanExporter — no process-global tracer):

  • rfc0038_2_ingest_batch_emits_one_span_with_a_wal_commit_child — one ingest.batch (SERVER) span, one wal.commit (INTERNAL) child correctly parented, no further sub-spans, and the otel.kind contract on both (the ingest arm of RFC0038.1).
  • rfc0038_2_span_count_is_constant_in_record_count — 1 vs 64 records → identical span count (RFC0038.2, O(1)).

What lands in later slices

The spans above are all present in this PR. What remains is test coverage and config, not the spans themselves:

  • Slice 3: span-emission/correlation tests for the query, MCP, and sweep spans (the remaining arms of RFC0038.1) + the spawn-boundary correlation test (RFC0038.3).
  • Slice 4: config-file telemetry.traces.* + sampler precedence/validation (RFC0038.4).

🤖 Generated with Claude Code

…ep (RFC 0038)

Slice 2 of RFC 0038, on the slice-1 tracer foundation (#614). Opens the
request-scope spans pinned in §3.5 at the four entry points:

- POST /v1/query (SERVER) on the querier handler
- ourios.mcp.{query_logs,list_templates,template_drift} (INTERNAL),
  children of rmcp's serve_inner span
- ourios.ingest.batch (SERVER) per OTLP Export, with an
  ourios.wal.commit INTERNAL child over the group-commit await
- ourios.compaction.sweep (INTERNAL)

The batch span is opened once per Export, never per record: the miner,
encode-pool, and record-sink inner loops stay span-free (§3.2). The
spans open inside the spawned callees (ingest_bound, the sweep body),
not at the tokio::spawn / spawn_blocking sites, because span context
does not cross those boundaries (§3.3). wal.commit is a child span, not
an event, because the group-commit fsync is the one durationful,
I/O-bound step. All spans skip_all — no payload on the span.

rfc0038_2_ingest_batch_span.rs (scoped WithSubscriber over an
InMemorySpanExporter, no process-global tracer) asserts RFC0038.2: a
1-record and a 64-record batch emit the same span count (2 = batch +
wal.commit child, correctly parented, no further sub-spans) — O(1) in
record count, the ingest arm of RFC0038.1.

RFC0038.3 (spawn-boundary correlation) and the query/MCP/sweep arms of
RFC0038.1 follow in slice 3; the config-file telemetry.traces.* +
sampler precedence (RFC0038.4) in slice 4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Signed-off-by: Jens Holdgaard Pedersen <Jens@holdgaard.org>
@jensholdgaard
jensholdgaard requested a review from Copilot July 24, 2026 06:40
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1212d28d-8d79-4e43-9be9-5a22447c0d4a

📥 Commits

Reviewing files that changed from the base of the PR and between aa46359 and 327332a.

📒 Files selected for processing (1)
  • crates/ourios-ingester/tests/it/rfc0038_2_ingest_batch_span.rs
📝 Walkthrough

Walkthrough

Adds RFC 0038 tracing instrumentation for ingest batches, WAL commits, compaction sweeps, query requests, and MCP tools. Extends ingester test dependencies, configures trace span attributes, and adds integration tests validating ingest span parentage and constant span cardinality.

Changes

Telemetry instrumentation

Layer / File(s) Summary
Ingest batch and WAL spans
crates/ourios-ingester/Cargo.toml, crates/ourios-ingester/src/receiver/pipeline.rs, crates/ourios-ingester/tests/it/*
The ingest path emits batch and WAL commit spans, with OpenTelemetry test wiring and integration tests validating parentage and constant span counts.
Query and MCP spans
crates/ourios-server/src/querier.rs, crates/ourios-server/src/mcp.rs
The query handler and three MCP tools receive named tracing spans with appropriate OpenTelemetry kinds.
Compaction sweep span
crates/ourios-ingester/src/compactor.rs
Each promoted compaction sweep is wrapped in a named internal tracing span.
Telemetry span attributes
crates/ourios-telemetry/src/lib.rs
The OpenTelemetry tracing layer disables automatic inactivity, location, and target attributes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OTLPClient
  participant ingest_bound
  participant WALCommit
  participant SpanExporter
  OTLPClient->>ingest_bound: Send OTLP batch
  ingest_bound->>WALCommit: Commit payload
  WALCommit-->>ingest_bound: Return commit outcome
  ingest_bound->>SpanExporter: Export batch and WAL spans
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the telemetry span changes in this PR.
Description check ✅ Passed The description is detailed and covers scope, rationale, and tests, though it does not mirror the template headings exactly.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rfc-0038-request-spans

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Implements RFC 0038 slice 2 by adding request-scoped tracing spans at the main service entry points (query HTTP handler, MCP tools, ingest pipeline batch/commit boundary, and compaction sweep), with an integration test enforcing O(1) span count per OTLP export to protect ingest hot-path overhead.

Changes:

  • Add #[tracing::instrument(skip_all, …)] request spans for query (POST /v1/query), MCP tools, ingest batches, and compaction sweeps.
  • Add a ourios.wal.commit child span around the group-commit await in IngestPipeline::ingest_bound.
  • Add an integration test asserting ingest span structure and constant span count (1 vs 64 records) using an in-memory span exporter and scoped subscriber.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
crates/ourios-server/src/querier.rs Adds a request-scoped server span for the POST /v1/query handler.
crates/ourios-server/src/mcp.rs Adds per-tool internal spans for MCP tool methods to correlate tool calls under rmcp’s request span.
crates/ourios-ingester/src/receiver/pipeline.rs Adds an ourios.ingest.batch span and an ourios.wal.commit child span around the group-commit await.
crates/ourios-ingester/src/compactor.rs Adds a coarse ourios.compaction.sweep span around a sweep invocation.
crates/ourios-ingester/tests/it/rfc0038_2_ingest_batch_span.rs New integration tests asserting span structure and O(1) span count per export batch.
crates/ourios-ingester/tests/it/main.rs Registers the new RFC0038.2 integration test module in the consolidated harness.
crates/ourios-ingester/Cargo.toml Adds dev-deps/features needed for in-memory trace export + scoped subscriber in tests.
Cargo.lock Locks new dev-dependency entries used by the ingest span tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ourios-server/src/querier.rs
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1232 2 1230 0
View the top 2 failed test(s) by shortest run time
ourios-server::it::rfc0027_mcp::rfc0027_7_output_discipline
Stack Traces | 0.028s run time
thread 'rfc0027_mcp::rfc0027_7_output_discipline' (17006) panicked at .../tests/it/rfc0027_mcp.rs:633:9:
"list_templates" carries the treat-as-data warning: 
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ourios-server::it::rfc0032_query_schema::rfc0032_5_tool_description_placement
Stack Traces | 0.035s run time
thread 'rfc0032_query_schema::rfc0032_5_tool_description_placement' (17377) panicked at .../tests/it/rfc0032_query_schema.rs:399:14:
description
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@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 `@crates/ourios-ingester/tests/it/rfc0038_2_ingest_batch_span.rs`:
- Around line 61-110: Extend the tests around
rfc0038_2_ingest_batch_emits_one_span_with_a_wal_commit_child and
rfc0038_2_span_count_is_constant_in_record_count to validate each emitted span’s
OTLP kind via span_context.trace_flags and cover the swept tracing spans defined
under the ingester source. Add assertions for expected span names, parent-child
relationships, and constant span counts where applicable, ensuring the new
regression coverage exercises the full span Kind contract rather than only IDs
and names.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 188042dc-40d8-44ed-8d07-18998fea9153

📥 Commits

Reviewing files that changed from the base of the PR and between d431653 and b3d1ff3.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • crates/ourios-ingester/Cargo.toml
  • crates/ourios-ingester/src/compactor.rs
  • crates/ourios-ingester/src/receiver/pipeline.rs
  • crates/ourios-ingester/tests/it/main.rs
  • crates/ourios-ingester/tests/it/rfc0038_2_ingest_batch_span.rs
  • crates/ourios-server/src/mcp.rs
  • crates/ourios-server/src/querier.rs

Comment thread crates/ourios-ingester/tests/it/rfc0038_2_ingest_batch_span.rs
… cleanliness

The RFC 0038 request spans (slice 2) made ourios-server emit spans to the
CI weaver live-check for the first time. tracing-opentelemetry injects
four attributes that fail `weaver registry live-check`: busy_ns/idle_ns
(tracked inactivity) and target carry no semconv namespace, and
with_location emits code.module.name, which collides with the upstream
code namespace. All four are instrumentation-source metadata, not domain
telemetry — our spans carry their identity in the span name plus the
attributes we add deliberately.

Disable tracked_inactivity, location, and target on the layer.
thread.{id,name} are valid semconv (no advice) and stay. Verified with a
local `weaver registry live-check` (v0.23.0, matching CI): the query span
now carries only thread.{id,name}, zero violation-level advisories (was
4), server + weaver both exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Signed-off-by: Jens Holdgaard Pedersen <Jens@holdgaard.org>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

Putting #[tracing::instrument] directly on the rmcp #[tool] methods
dropped the doc-comment tool description the macro derives, breaking
RFC0027.7 (output discipline) and RFC0032.5 (description placement) in
CI. Keep each #[tool] method a thin delegate — doc comment and #[tool]
attribute untouched, so description derivation is restored — and move the
span onto a private *_traced helper it calls. The span is still one per
tool call, a child of rmcp's serve_inner. Both tests pass again locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Signed-off-by: Jens Holdgaard Pedersen <Jens@holdgaard.org>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

…t spans

Per review: the ingest span tests checked names/parentage/count but not
the §3.5 kind contract. Assert ourios.ingest.batch is a SERVER span and
its ourios.wal.commit child is INTERNAL — verifying the otel.kind fields
survive export as SpanData.span_kind, not just the ids and names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WQY9wfrfRggqSpMLH8Xj3Y
Signed-off-by: Jens Holdgaard Pedersen <Jens@holdgaard.org>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 9 changed files in this pull request and generated no new comments.

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