Skip to content

Phase 10: conformance, interop (JMS+Proton), netns partition, tuned bench + link-keying fix - #26

Merged
mack42 merged 11 commits into
mainfrom
feat/phase10-conformance
Jul 17, 2026
Merged

Phase 10: conformance, interop (JMS+Proton), netns partition, tuned bench + link-keying fix#26
mack42 merged 11 commits into
mainfrom
feat/phase10-conformance

Conversation

@mack42

@mack42 mack42 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Advances broker.md Phase 10 — now substantially complete. All verified locally; committed leg-by-leg.

Legs

1. Unified conformance harness (c1e99cf) — one wire-level matrix (framing / error-conditions asserting exact amqp:* symbols / flow credit-ceiling / settlement) over a shared tests/harness/ (pure-facade mod.rs); folds in and deletes adversarial.rs.

2. Interop — three independent client stacks → our broker: Qpid JMS (Java, f186745, CI interop-jms), Qpid Proton (Python/C, 33faef0, CI interop-proton), plus the existing Rust fe2o3-amqp leg.

3. 🐛 Core fix — link keying (33faef0): the proton leg flushed out a real bug. ramqp-core keyed session links by name alone, but AMQP 1.0 §2.6.1 identifies a link by name + role — proton names a sender and receiver to one address identically, so the second attach was misrouted and dropped. Fixed to key by (name, role); regression-tested. Versions: core 0.2.4→0.2.5, ramqp 0.8.1→0.8.2, broker →0.8.35, pins synced, CHANGELOG updated.

4. Process-level netns partition test (b5472f5, 7c651ed): 3 quorum nodes in Linux namespaces, iptables split-brain → majority available (incl. an 8×5 concurrent load sweep through the partition), minority refuses (no silent accept), no committed-message loss on heal. CI partition.

5. Tuned-incumbent + durability-parity bench (62ae118, 257373b): vs tuned RabbitMQ + tuned Artemis + fsync-to-fsync quorum, all over loopback TCP. Provisional (WSL2):

leg p50 µs p99 p99.9
ramqp-broker transient 93.7 263.0 340.4
RabbitMQ classic (tuned) 249.1 467.4 650.6
Artemis (tuned, NIO) 292.1 704.2 1132.5
ramqp-broker quorum (store-redb, fsync) 304.3 665.3 866.9
RabbitMQ quorum (fsync) 2260.5 3834.4 7303.2

Closes the Phase 6 "in-memory vs fsync" caveat: even at durability parity our quorum p50 is ~7× below RabbitMQ's.

Also: e7bffc4 gates dead_letter_ordered to store-redb (pre-existing default-feature warning).

Gates

cargo clippy --all-targets --all-features -- -D warnings clean; cargo test --all-features green (23 binaries). ramqp-broker stays publish = false.

Deferred / remaining

  • our-client → Qpid Broker-J: deferred — blocked by the apache/qpid-broker-j image (no env-var substitution for admin creds, Plain-only auth, REST 405 on queue create), not by rAMQP; already covered by the RabbitMQ + Artemis interop jobs.
  • Bare-metal bench numbers (this box is WSL2/provisional) + a latency-under-N-connections bench sweep.

mack42 added 9 commits July 13, 2026 13:30
Single wire-level conformance matrix (tests/conformance.rs) over a shared
loopback + raw-frame harness (tests/harness/), covering four axes:
- framing: header/open exchange, directional max-frame-size
- error-conditions: each violation asserts the EXACT amqp:* condition symbol
  (duplicate open, frame on unmapped channel, slow-loris handshake)
- flow: broker never exceeds granted link-credit (manual-credit client)
- settlement: terminal accepted removes the message

Folds in the raw-socket cases from tests/adversarial.rs (now deleted) and
tightens their error.is_some() checks to exact-symbol assertions. Behavioral
queue semantics stay in produce_consume/quorum_queue. mod.rs kept to a pure
facade; helpers live in named sibling files.

Updates broker.md Phase 10 conformance checkbox to done.
An independent Java AMQP 1.0 stack (Apache Qpid JMS 2.x, Jakarta Messaging)
round-trips a message through ramqp-broker — a second implementation, unrelated
to fe2o3-amqp, proving the broker's wire behavior isn't tailored to our own
client.

- tests/interop/jms/JmsInterop.java: minimal produce+consume+verify client
- tests/jms_interop.rs: #[ignore]d orchestration test — starts a loopback
  broker in-process, spawns the Java client, asserts INTEROP_OK
- tests/interop/jms/run.sh: fetch qpid-jms, compile, run the ignored test
  (cached under target/interop; qpid-jms version pinned, default 2.10.0)
- tests/interop/README.md: the interop leg matrix + how to run
- ci.yml: interop-jms job (setup-java 21 + run.sh)

Verified locally end-to-end (download -> compile -> pass). Updates broker.md
Phase 10 interop checkbox; proton + our-client->Qpid-broker legs still pending.
Its only caller lives in durable.rs (store-redb). Gating the method to the same
feature makes default-feature builds warning-clean (was: dead_code warning).
Jepsen-style split-brain against real broker processes across Linux network
namespaces, complementing the in-process fault injection in tests/cluster.rs.

- tests/partition/run.sh: builds brokerd + probe, self-elevates via sudo, sets
  up a bridge + 3 netns (one quorum node each), iptables-partitions the minority,
  and asserts: majority stays available, minority refuses (never silently
  accepts), no committed-message loss on heal. Tears down via EXIT trap.
- examples/partition_probe.rs: ramqp-client workload probe
  (expect-accept / expect-refused / consume) reporting via exit code.
- tests/partition/README.md + ci.yml partition job.

Verified locally: majority accepted, minority refused cleanly, 8/8 committed
messages survived. Updates broker.md Phase 10 fault-injection checkbox.
…se 10)

Re-runs the closed-loop latency comparison against a TUNED RabbitMQ (not stock
defaults) and adds the durability-parity quorum leg — our store-redb fsync
quorum vs RabbitMQ's fsync quorum — closing the Phase 6 'in-memory vs fsync'
caveat. All legs run over loopback TCP against a broker process (fairer than the
original in-process-vs-docker table).

- bench-compare/tuned/rabbitmq.conf: latency-tuned RabbitMQ 4.x config
- bench-compare/tuned/run.sh: one-command runner (stands up tuned RabbitMQ on
  non-default ports, our transient + durable-quorum brokerd, runs all legs,
  emits a Markdown table). results.md is generated (gitignored).
- bench-compare/README.md: provisional results + reproduce steps
- broker.md: Phase 10 bench checkbox

Provisional numbers (WSL2, indicative only): transient p50 ~2.8x below tuned
RabbitMQ classic; store-redb quorum p50 ~8x below RabbitMQ fsync quorum. Real
figures come from bare metal via the same script.
AMQP 1.0 §2.6.1 identifies a link by container-id + name + role, so a peer may
open a sender and a receiver sharing a link name on one session. ramqp-core
keyed links by name alone (link_handles: HashMap<String,_>), so the second
same-named attach was misrouted via knows_link() and silently dropped — the
attach never got a reply. Apache Qpid Proton hits this: its default link names
derive from the address, identical for both directions.

- ramqp-core: link_handles keyed by (name, Role); Role gains Hash + opposite();
  knows_link(name, remote_role) matches by name AND role; accept_peer_attach
  dedup and forget_link updated. Regression test
  same_name_sender_and_receiver_both_attach.
- ramqp-broker: pass attach.role to knows_link.
- Proton interop leg: tests/interop/proton/{proton_interop.py,run.sh} (host
  python3-qpid-proton, or docker fallback) + CI job interop-proton. Verified
  locally: proton default naming now round-trips (was: timeout).
- Versions: ramqp-core 0.2.4->0.2.5, ramqp 0.8.1->0.8.2, ramqp-broker
  0.8.33->0.8.34, pins synced; CHANGELOG + broker.md + interop README.
…e 10)

Adds a 'load' mode to partition_probe (N concurrent producers x M publishes)
and drives an 8x5 concurrent load through the majority DURING the network
partition, asserting all 40 are accepted and all committed messages survive the
heal (now 48). Extends the netns split-brain test from a bounded workload to
sustained concurrent load.
Stands up a tuned Artemis (NIO journal, autotune off) alongside tuned RabbitMQ
in bench-compare/tuned/run.sh and adds it as a latency row. Provisional (WSL2):
transient p50 ~3x below tuned Artemis. Updates the bench README table + broker.md.
Marks the interop checkbox done (fe2o3 + Qpid JMS + Qpid Proton = three
independent stacks), refreshes the status line, and documents the deferred
our-client->Qpid-Broker-J leg (blocked by apache/qpid-broker-j image config, not
rAMQP; already covered by the RabbitMQ + Artemis interop jobs).
@mack42 mack42 changed the title Phase 10: conformance harness, Qpid JMS interop, netns partition tests, tuned bench Phase 10: conformance, interop (JMS+Proton), netns partition, tuned bench + link-keying fix Jul 14, 2026
- ramqp-broker: drop publish=false, version 0.8.35 -> 0.9.0 (deliberate
  first-release number; the old patch stream was per-commit churn)
- config types (BrokerConfig, QueuePolicy, OverflowBehavior,
  ClusterMemberConfig) are #[non_exhaustive]; external construction sites
  (integration tests, bench-compare) rewritten to default-then-mutate
- exclude tests/ from the package (they need the path-only ramqp dev-dep,
  which packaging strips, so they could never compile from the .crate)
- docs.rs metadata (all-features), refreshed lib.rs status (was Phase 4),
  broker README: pre-1.0 banner, absolute links, cargo-install run steps
- release.yml publishes core -> ramqp -> broker, tolerant of already-
  published versions; RELEASING.md + root README + CHANGELOG updated
- bench-compare: fix two pre-existing clippy lints surfaced by workspace
  -D warnings (manual_is_multiple_of, while-let loop)
@mack42 mack42 self-assigned this Jul 17, 2026
@mack42
mack42 merged commit df5a0a1 into main Jul 17, 2026
11 checks passed
@mack42
mack42 deleted the feat/phase10-conformance branch July 17, 2026 20:20
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.

1 participant