Port Aeron adapter to wingfoil-next - #601
Open
0-jake-0 wants to merge 2 commits into
Open
Conversation
Ports the classic `wingfoil::adapters::aeron` module onto the Op model, behind the `aeron` (rusteron-client, C++ FFI) or `aeron-rs` (pure Rust) backend features, with `aeron-driver` embedding a media driver in-process. Both polling modes are ported: - `AeronMode::Spin` rides a busy-spin `custom_node` polling Aeron inside the cycle on the graph thread. - `AeronMode::Threaded` rides `source_at_start` — a background poll thread over the channel layer, with classic's exponential idle back-off. The typed parser with `FragmentHeader` access, the `fragment_limit` per-poll cap, the `Spin`->`Threaded` downgrade for backends that lock on poll, both status side-channels, the `ChannelUri` builders, `ClaimBuffer`'s zero-copy commit/abort contract, and the `TransportError` / `AeronStatus` types are all carried over. Like classic, the feature uses no `async`/tokio. Deviations (documented in the module's `# Deviations from classic` block and the deviation register): the sources take a `&GraphBuilder` + `RunMode`, return `Result`, and reject `RunMode::HistoricalFrom` at wiring (the publisher keeps classic's real-time check at graph start); the status side-channel is a plain stream rather than classic's `AeronStatusStream` node — status is multiplexed with data over one internal envelope and split with `map_filter`, so spin mode now carries it in-band too; the sink is the `AeronSinkOps` extension trait returning `Stream<()>`; and the mock backends are public test support, since next's tests live outside the lib. Tests: `tests/aeron_adapter.rs` ports the classic node-level unit tests as 20 mock-backed cases needing no media driver; `tests/aeron_integration.rs` ports the classic media-driver suite behind `aeron-integration-test`, wired into CI via `aeron-next-integration.yml` (which installs the cmake >= 3.30 / clang / uuid / libbsd toolchain rusteron needs). Both classic examples are ported to `examples/aeron/`. With this, every classic I/O adapter has a wingfoil-next twin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKhf2tiM8nwtSBzC1St3ga
The main `Build, Test, & Lint` job runs `cargo test -p wingfoil-next --all-features --lib --tests`, which enables `aeron-integration-test` and so runs this suite *without* `--test-threads=1` (the dedicated workflow passes it; the main job cannot). These tests are inherently non-parallel: the media driver writes its CNC file to a fixed `AERON_DIR` under a bind-mounted `/dev/shm`, and the Aeron client is pointed at it through a process-global environment variable that `start_media_driver` — and `no_driver_connection_fails`, which points it at an empty directory — rewrite. Two tests in flight at once clobber each other's driver directory and env var. Enforce the constraint in code rather than leaving it to the caller: a process-wide `serial_guard()` taken at the top of every test. The mutex is poison-tolerant so one failing test does not cascade into "poisoned" failures for the rest. This is the same class of defect as the Fluvio suite fix; the remedy differs because Aeron's per-test driver is fine once serialised, whereas Fluvio's fixed host ports mean only one cluster can exist at all. Docker is unavailable in the dev sandbox, so this was verified by compilation and clippy only; the suite itself runs in CI. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKhf2tiM8nwtSBzC1St3ga
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.
Ports the classic
wingfoil::adapters::aeronmodule onto the Op model — the Aeron IPC/UDP low-latency message transport — behind theaeron(rusteron-client, C++ FFI) oraeron-rs(pure Rust) backend features, withaeron-driverembedding a media driver in-process. Last entry in Phase 4 item 8.With this, every classic I/O adapter has a wingfoil-next twin. (The other three of this batch: #598 fluvio, #599 iceoryx2, #600 web.)
Surface
Sources (free builder fns on
&GraphBuilder, realtime-only):aeron_sub_fragment(&g, run_mode, subscriber, parser, opts)— the typed-parser surface withFragmentHeaderaccess (position,session_id,stream_id) and theOk(Some)/Ok(None)/Errcontract (a malformed fragment is logged and skipped, never aborting the cycle).aeron_sub_fragment_with_status(...)— the parallel-additive sibling returning(data, status).Both polling modes are ported:
Spin(primary)custom_nodepolling Aeron inside the cycle on the graph threadThreaded(secondary)source_at_start→ a background poll thread over thechannellayer, with classic's exponential idle back-offSink:
AeronSinkOps::{aeron_pub, aeron_pub_with_status}onStream<Burst<T>>, overregister_op1, with classic's exact status ordering (Closedterminal and checked first;BackPressurerecordsBackPressuredand drops the rest of the burst; a successful offer recordsConnected; an empty burst falls back tois_connected).Also carried over: the
fragment_limitper-poll cap, theSpin→Threadeddowngrade for backends that lock on poll, theChannelUribuilders,ClaimBuffer's zero-copy commit/abort contract, and theTransportError/AeronStatustypes. Like classic, the feature uses noasync/tokio.Deviations from classic
Canonical list is in the module's
# Deviations from classicblock; summarised:&GraphBuilder+RunMode, returnResult, and rejectRunMode::HistoricalFromat wiring — a live Aeron subscription has no historical timeline, and the threaded mode's channel receiver would block-collect the never-closing poll thread and deadlock atstart(register B2, ratified). Classic's spin subscriber silently ran against the fast-forwarded historical clock. The publisher keeps classic's behaviour exactly: its real-time check fires at graphstart().AeronStatusStream(aMutableNodethe producer drove viaclear()/record()and wired as an active downstream) has no next twin — next multiplexes status with data over one internal envelope and splits it withmap_filter, thezmq_subshape. Observable behaviour (transition-only emission, derivation order, in-band ordering in threaded mode) is identical; notably the spin mode now carries status in-band too.Stream<()>, not classic'sAeronPubreturningRc<dyn Node>.tests/and compile against the public library, soMockSubscriber/MockPublishercan't stay#[cfg(test)]-gated inside the crate.aeron_sub_fragmentnever derives status — classic'sOption<Rc<RefCell<AeronStatusStream>>>becomes atrack_statusflag. Same behaviour, no allocation.Tests
tests/aeron_adapter.rs(aeron) — parity port of the classic node-level unit tests, 20 mock-backed cases needing no media driver: burst shaping (one poll ⇒ one atomic burst), the parserNone/Errpaths, the synthesised default header, all four status-derivation cases for the subscriber (connected / disconnected / terminal close / steady-state no-re-emission) in both modes, the wiring guards, and the four publisher status cases plus the empty-burst fallback. All green.tests/aeron_integration.rs(aeron-integration-test) — parity port of the classic media-driver suite: no-driver connect failure, the threetry_claimcases (zero-copy round trip, typed back-pressure, drop-aborts), thefragment_limitcap, spin/threaded/collapse round trips, real fragment positions, both status side-channels over a live driver, the no-dedup publisher, bothChannelUrishapes, and theaeron-rsbackend round trip (which is what exercises theSpin→Threadeddowngrade end to end).Docker isn't available in the dev sandbox, so the driver-backed suite was not executed locally; it runs in CI.
Other changes
examples/aeron/{main.rs,status_circuit_breaker.rs,README.md}— both classic examples ported. The circuit breaker is notable: where classic needed a hand-writtenMutableNodereading both streams, next expresses it with the ordinary fluent vocabulary (foldlatches the transition; a two-inputcustom_nodegates data on it as a passive upstream)..github/workflows/aeron-next-integration.yml+ registration inintegration-tests.yml. The workflow installs the toolchain rusteron's build script needs — cmake ≥3.30 (Ubuntu 24.04 ships 3.28), clang, uuid-dev, libbsd-dev.next/docs/port-plan.mdPhase 4 item 8 andnext/docs/deviation-register.md(B2 scope) updated.The classic Criterion benches (
aeron_publication_latency,aeron_subscription_throughput,aeron_transceiver,aeron_allocation_tracking) are not ported — next's bench suite is a separate work item, as for every adapter so far.Checks
cargo fmt --all -- --check,cargo lint,cargo lint-all(the full workspace all-features gate — it builds here because the Aeron native toolchain was installed for this port, unlike the earlier adapters in this batch),cargo clippy -p wingfoil-next --all-features --all-targets -- -D warnings, andcargo test -p wingfoil-next --features aeronall pass.🤖 Generated with Claude Code
https://claude.ai/code/session_01FKhf2tiM8nwtSBzC1St3ga
Generated by Claude Code