Skip to content

feat(server): serve the OTLP receiver role — flip RFC0003.16 green - #141

Merged
jensholdgaard merged 4 commits into
mainfrom
feat/rfc0003-16-served
Jun 6, 2026
Merged

feat(server): serve the OTLP receiver role — flip RFC0003.16 green#141
jensholdgaard merged 4 commits into
mainfrom
feat/rfc0003-16-served

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jun 6, 2026

Copy link
Copy Markdown
Owner

What

Implements RFC0003.16 — wires the OTLP receiver as a config-toggled ourios-server role (the §9 process-model resolution), and flips the red-gate stub to a green real-socket integration test. This is the last piece of the receiver: it now serves.

receiver::serve binds gRPC (tonic) + HTTP (axum) over one shared IngestPipeline backed by a single Wal (RFC 0008 §3.1's single-writer rule), reusing the receiver crate's http::router + grpc::LogsReceiver. Both listeners run on the binary's tokio runtime alongside the compactor; one watch channel fans graceful shutdown to both.

Config (env)

OURIOS_RECEIVER_ENABLED, OURIOS_RECEIVER_GRPC_ADDR (default 0.0.0.0:4317), OURIOS_RECEIVER_HTTP_ADDR (default 0.0.0.0:4318), OURIOS_WAL_ROOT.

Graceful shutdown

The server now drains on SIGINT or SIGTERM (k8s / nerdctl stop send SIGTERM — a real production need, tokio's already-enabled signal feature, no new dep). A failed final telemetry flush is logged, not fatal, so a clean shutdown exits 0 even when the metrics collector is unreachable.

Test (RFC0003.16)

A real-socket integration test spawns the binary on 127.0.0.1:0 (both transports), reads the reported ports, exports a batch over a real tonic gRPC client + a hand-rolled HTTP POST (no HTTP-client dep), SIGTERMs the process via kill -TERM (no nix, no unsafe — keeps #![deny(unsafe_code)]), waits for a clean exit, then replays the WAL to assert both OtlpBatch frames are durable (no acked batch lost). No dedup asserted (at-least-once). Stable across repeated runs.

Also adds the 405-on-non-POST confirming test (axum's MethodRouter default) per the OTLP-conformance review.

Verification

  • cargo test --all-features ✓ — RFC0003.16 green (×3 stable); 405 test green; workspace green. · cargo clippy --all-targets --all-features -- -D warnings ✓ · cargo fmt --all --check

Next

Once merged: advance RFC 0003 specified → green again (RFC0003.16 now passing) via a doc PR, and update the memory note. The receiver is then a complete, runnable server role — the §9 question fully resolved + implemented.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added OpenTelemetry (OTLP) receiver capability, enabling the server to receive and process telemetry logs over both gRPC and HTTP transports with data durability.
    • Added support for configurable receiver binding addresses via environment variables.
  • Bug Fixes

    • Improved HTTP request validation to properly return 405 METHOD_NOT_ALLOWED responses for unsupported HTTP methods.
  • Chores

    • Enhanced graceful shutdown handling to support SIGTERM termination signals alongside existing SIGINT/Ctrl-C support.

Wires the receiver as a config-toggled ourios-server role (the RFC 0003 §9
process-model resolution). `receiver::serve` binds gRPC (tonic) + HTTP
(axum) over ONE shared IngestPipeline backed by a single Wal (RFC 0008
§3.1), reusing the receiver crate's http::router + grpc::LogsReceiver. Both
listeners run on the binary's tokio runtime alongside the compactor; a
single watch channel fans graceful shutdown to both.

Config (env): OURIOS_RECEIVER_ENABLED, OURIOS_RECEIVER_GRPC_ADDR (default
0.0.0.0:4317), OURIOS_RECEIVER_HTTP_ADDR (default 0.0.0.0:4318),
OURIOS_WAL_ROOT. The server now shuts down on SIGINT *or* SIGTERM (k8s /
nerdctl stop send SIGTERM); a failed final telemetry flush is logged, not
fatal, so a clean shutdown exits 0 even when the metrics collector is
unreachable.

Flips RFC0003.16: a real-socket integration test spawns the binary on
127.0.0.1:0 (both transports), reads the reported ports, exports a batch
over a real tonic gRPC client + a hand-rolled HTTP POST, SIGTERMs the
process (via `kill -TERM` — no nix dep, no unsafe), waits for a clean
exit, then replays the WAL to assert both OtlpBatch frames are durable (no
acked batch lost). No dedup asserted. Stable across repeated runs.

Also adds the 405-on-non-POST confirming test (axum's MethodRouter
default) per the OTLP-conformance review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard requested a review from Copilot June 6, 2026 22:50
@jensholdgaard

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

More reviews will be available in 36 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

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.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: df864c20-b8e6-4b9d-9f94-c0e9e3d2e0fe

📥 Commits

Reviewing files that changed from the base of the PR and between 82bd04a and 67ec98d.

📒 Files selected for processing (2)
  • crates/ourios-server/src/main.rs
  • crates/ourios-server/tests/rfc0003_16_served_binary.rs
📝 Walkthrough

Walkthrough

This PR implements a complete OTLP receiver for the ourios-server binary, enabling it to ingest OpenTelemetry logs over both gRPC and HTTP transports. The receiver shares a single WAL and ingest pipeline across both protocols, coordinates graceful shutdown via a watch channel, and is exercised by an RFC0003.16 end-to-end integration test that validates durability, transport correctness, and clean shutdown behavior.

Changes

Served-binary OTLP receiver end-to-end

Layer / File(s) Summary
Dependencies for OTLP receiver hosting
crates/ourios-server/Cargo.toml
Adds path dependencies for WAL/miner/core and OTLP transport crates (tonic for gRPC, axum for HTTP, opentelemetry-proto with tonic log service generation), expands tokio features to include net and sync, and introduces dev-dependencies for integration testing (tempfile, prost, tonic channel, tokio io/time/process).
Receiver module – gRPC and HTTP transport serving
crates/ourios-server/src/receiver.rs
New module defines ReceiverConfig (bind addresses + WAL config) and ReceiverHandle (resolved addresses, shutdown sender, join handles). serve opens the WAL, constructs a shared IngestPipeline, binds gRPC listener first (to resolve ephemeral port before serving), binds HTTP listener, creates a shared watch shutdown channel, spawns tonic gRPC server and axum HTTP server each monitoring the watch receiver, and returns the handle with resolved addresses once both sockets are bound. ReceiverHandle::shutdown broadcasts via watch and awaits both join handles.
Server wiring – configuration and main loop integration
crates/ourios-server/src/main.rs
ServerConfig extended with optional receiver field. New ReceiverParams struct and environment-parsing helpers (parse_addr, build_receiver_config, wal_config) parse RECEIVER_GRPC_ADDR/RECEIVER_HTTP_ADDR/RECEIVER_WAL_ROOT. Default OTLP bind address constants added. main conditionally starts receiver via receiver::serve when enabled, prints resolved gRPC/HTTP addresses to stdout, installs SIGTERM handler alongside existing Ctrl-C/SIGINT, and coordinates graceful shutdown by draining receiver before flushing telemetry (changed to best-effort).
End-to-end integration test – RFC0003.16 served-binary scenario
crates/ourios-server/tests/rfc0003_16_served_binary.rs
Replaces ignored stub with active async integration test. Spawns ourios-server binary with receiver enabled on ephemeral 127.0.0.1:0 ports, captures and parses stdout to extract resolved gRPC and HTTP addresses, exports one log batch via real tonic gRPC client and one via raw TCP HTTP POST (asserts HTTP 200 status), sends SIGTERM and waits for clean shutdown, replays WAL, and asserts exactly two OtlpBatch frames whose decoded bodies match the "grpc batch" and "http batch" strings. Includes test helpers for building OTLP export requests, posting raw HTTP, replaying frames from WAL, and extracting log bodies from decoded export requests.
HTTP transport error test – 405 on non-POST
crates/ourios-ingester/tests/http_transport_errors.rs
Adds non_post_to_logs_path_is_405 test that sends a GET request to /v1/logs, asserts the HTTP response is 405 METHOD_NOT_ALLOWED, and verifies no OtlpBatch frames are appended for rejected requests.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit hops in, two paths in sight,
gRPC and HTTP, both burning bright,
One shared pipeline, one graceful way—
SIGTERM to rest, logs safe to stay. 🐰✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically describes the main change: implementing RFC0003.16 by serving the OTLP receiver role in ourios-server and converting a test stub to a passing test.
Description check ✅ Passed The description thoroughly covers all required template sections: a comprehensive Summary explaining the implementation, Related section linking RFC0003.16, and a complete Checklist with all items marked as completed.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/rfc0003-16-served

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 and usage tips.

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 RFC0003.16 by adding an OTLP receiver “role” to ourios-server: the binary can now optionally bind OTLP/gRPC + OTLP/HTTP over a shared ingest pipeline backed by a single WAL, and the prior red-gate stub is replaced with a real-socket served-binary integration test that validates WAL-before-ack durability across both transports and graceful shutdown.

Changes:

  • Added receiver::serve to bind and run OTLP/gRPC (tonic) + OTLP/HTTP (axum) listeners over a shared IngestPipeline.
  • Extended ourios-server config/env parsing and runtime to optionally start the receiver role and shutdown on SIGINT/SIGTERM, with best-effort telemetry flush.
  • Converted RFC0003.16 to a real-socket integration test and added an OTLP/HTTP 405-on-non-POST conformance test.

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/ourios-server/tests/rfc0003_16_served_binary.rs Replaces ignored stub with real-socket spawn/export/SIGTERM/shutdown + WAL replay assertions.
crates/ourios-server/src/receiver.rs New receiver role module: binds gRPC+HTTP and serves them with shared pipeline + watch-driven shutdown.
crates/ourios-server/src/main.rs Adds receiver role toggles + address reporting + SIGTERM handling + shutdown ordering and best-effort telemetry flush.
crates/ourios-server/Cargo.toml Adds required deps (axum/tonic/opentelemetry-proto/WAL) and test-only deps for the new integration test.
crates/ourios-ingester/tests/http_transport_errors.rs Adds explicit 405 test for non-POST requests to /v1/logs.
Cargo.lock Updates lockfile for newly added dependencies.

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

Comment thread crates/ourios-server/src/main.rs Outdated
Comment on lines 197 to 199
let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
.map_err(|e| format!("install SIGTERM handler: {e}"))?;
let shutdown = tokio::select! {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed — SIGTERM is now gated behind cfg(unix) in a terminate_signal helper; the binary compiles on non-Unix again with SIGINT/ctrl_c as the cross-platform path, and a SIGTERM-handler install failure is logged (non-fatal), leaving SIGINT in charge.

Comment on lines +122 to +123
#[tokio::test]
async fn rfc0003_16_served_binary_binds_round_trips_and_shuts_down() {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed — the test file is now #![cfg(unix)] (it drives shutdown via kill -TERM, and the server's SIGTERM handling is Unix-only).

Comment on lines +182 to +187
let pid = child.id().expect("server pid");
std::process::Command::new("kill")
.arg("-TERM")
.arg(pid.to_string())
.status()
.expect("send SIGTERM");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed — the kill now uses tokio::process::Command and awaits the status, asserting kill_status.success() (no blocking the runtime; a failed kill is diagnosed).

@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.

🧹 Nitpick comments (1)
crates/ourios-server/src/main.rs (1)

85-104: ⚡ Quick win

Consider adding unit tests for build_receiver_config.

This function has non-trivial validation logic (enabled-value parsing, wal_root requirement when enabled). Adding tests similar to the existing build_config tests would improve confidence and catch regressions.

Example test cases:

  • Disabled when enabled_raw is None, "0", or "false"
  • Enabled with valid addresses and wal_root
  • Rejects missing wal_root when enabled
  • Rejects empty wal_root when enabled
  • Uses default addresses when unset
🤖 Prompt for 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.

In `@crates/ourios-server/src/main.rs` around lines 85 - 104, Add unit tests for
build_receiver_config to cover its enabled-value parsing, address defaults, and
wal_root validation: write tests that call build_receiver_config with various
enabled_raw values (None, "0", "false" should return Ok(None); "1"/"true"/"yes"
should proceed), with grpc_raw/http_raw unset to ensure parse_addr uses
DEFAULT_GRPC_ADDR/DEFAULT_HTTP_ADDR, with a valid wal_root PathBuf to return
Ok(Some(ReceiverParams{...})), and failing cases where enabled is truthy but
wal_root is None or an empty path to assert an Err with the expected message;
reference build_receiver_config, ReceiverParams, and parse_addr in the tests.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@crates/ourios-server/src/main.rs`:
- Around line 85-104: Add unit tests for build_receiver_config to cover its
enabled-value parsing, address defaults, and wal_root validation: write tests
that call build_receiver_config with various enabled_raw values (None, "0",
"false" should return Ok(None); "1"/"true"/"yes" should proceed), with
grpc_raw/http_raw unset to ensure parse_addr uses
DEFAULT_GRPC_ADDR/DEFAULT_HTTP_ADDR, with a valid wal_root PathBuf to return
Ok(Some(ReceiverParams{...})), and failing cases where enabled is truthy but
wal_root is None or an empty path to assert an Err with the expected message;
reference build_receiver_config, ReceiverParams, and parse_addr in the tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 34bab1df-85b2-4ab5-8f5b-365de79f6932

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa4b5a and 82bd04a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • crates/ourios-ingester/tests/http_transport_errors.rs
  • crates/ourios-server/Cargo.toml
  • crates/ourios-server/src/main.rs
  • crates/ourios-server/src/receiver.rs
  • crates/ourios-server/tests/rfc0003_16_served_binary.rs

Review fixes:
- Gate SIGTERM behind cfg(unix) via a `terminate_signal` helper — the
  binary compiles on non-Unix again (SIGINT/ctrl_c stays the
  cross-platform path; SIGTERM is the Unix extra), and a handler-install
  failure is logged, not fatal.
- Mark the served-binary integration test `#![cfg(unix)]` (it drives
  shutdown with `kill -TERM`).
- Send the kill via `tokio::process::Command` and await + assert its
  status (no blocking the runtime; failures are diagnosed).
- Add unit tests for `build_receiver_config` (disabled-by-default, default
  + custom addresses, WAL-root-required-when-enabled, malformed address).

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

Copy link
Copy Markdown
Owner Author

@coderabbitai added the suggested build_receiver_config unit tests (disabled-by-default, default + custom addresses, WAL-root-required-when-enabled, malformed-address) in 0f30520.

@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@jensholdgaard Great — those four test cases cover exactly the edge cases that matter (disabled, default addrs, custom addrs, wal-root-required, malformed addr). Thanks for following through on that!


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

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 5 out of 6 changed files in this pull request and generated 1 comment.

Comment on lines +138 to +141
.env("OURIOS_WAL_ROOT", &wal_root)
.stdout(Stdio::piped())
.spawn()
.expect("spawn ourios-server");

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed — set .kill_on_drop(true) on the spawned server Command, so an early return (timeout or assertion panic) reaps the child rather than leaking it.

Review fix: if the integration test returns early (address-read timeout or
an assertion panic) the spawned ourios-server could be left running. Set
kill_on_drop(true) so the child is reaped when its handle drops.

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

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 5 out of 6 changed files in this pull request and generated 1 comment.

Comment thread crates/ourios-server/src/main.rs Outdated
Comment on lines 244 to 245
// A signal-handler setup failure is fatal: cancelling the compactor and
// exiting 0 would leave the server silently doing no work.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed — the comment now says the SIGINT (ctrl_c) handler failure is fatal (the shutdown? propagates the ctrl_c result), and notes a SIGTERM-handler failure is non-fatal per terminate_signal.

Review nit: SIGTERM-handler failures are non-fatal now (terminate_signal),
so the comment on the final shutdown? — which propagates the ctrl_c
result — now says SIGINT specifically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jensholdgaard
jensholdgaard merged commit 614adf6 into main Jun 6, 2026
10 checks passed
@jensholdgaard
jensholdgaard deleted the feat/rfc0003-16-served branch June 6, 2026 23:19
jensholdgaard added a commit that referenced this pull request Jun 6, 2026
…142)

All 16 §5 acceptance criteria (RFC0003.1-.16) are live + passing, including
the served binary (#141). Per docs/rfcs/README.md, green = all acceptance
criteria pass (mechanical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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