Phase 10: conformance, interop (JMS+Proton), netns partition, tuned bench + link-keying fix - #26
Merged
Merged
Conversation
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).
- 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)
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.
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 exactamqp:*symbols / flow credit-ceiling / settlement) over a sharedtests/harness/(pure-facademod.rs); folds in and deletesadversarial.rs.2. Interop — three independent client stacks → our broker: Qpid JMS (Java,
f186745, CIinterop-jms), Qpid Proton (Python/C,33faef0, CIinterop-proton), plus the existing Rustfe2o3-amqpleg.3. 🐛 Core fix — link keying (
33faef0): the proton leg flushed out a real bug.ramqp-corekeyed 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: core0.2.4→0.2.5, ramqp0.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. CIpartition.5. Tuned-incumbent + durability-parity bench (
62ae118,257373b): vs tuned RabbitMQ + tuned Artemis + fsync-to-fsync quorum, all over loopback TCP. Provisional (WSL2):Closes the Phase 6 "in-memory vs fsync" caveat: even at durability parity our quorum p50 is ~7× below RabbitMQ's.
Also:
e7bffc4gatesdead_letter_orderedtostore-redb(pre-existing default-feature warning).Gates
cargo clippy --all-targets --all-features -- -D warningsclean;cargo test --all-featuresgreen (23 binaries).ramqp-brokerstayspublish = false.Deferred / remaining
apache/qpid-broker-jimage (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.