diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24f9437..5fb2345 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -167,3 +167,50 @@ jobs: status=$? kill "$broker" 2>/dev/null || true exit $status + + # External-toolchain interop: an independent, third-party AMQP 1.0 client + # stack (Apache Qpid JMS, pure Java) exercising ramqp-broker — the JMS leg of + # broker.md Phase 10, alongside the Rust fe2o3-amqp leg in bench-compare. The + # runner fetches qpid-jms, compiles the client, and runs the #[ignore]d + # jms_interop test (which starts a loopback broker in-process). + interop-jms: + name: Interop (Qpid JMS -> ramqp-broker) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Qpid JMS interop + run: ramqp-broker/tests/interop/jms/run.sh + + # External-toolchain interop: Apache Qpid Proton (Python, the C-based AMQP 1.0 + # engine) exercising ramqp-broker — a third independent stack after fe2o3-amqp + # (Rust) and Qpid JMS (Java). Installs proton on the runner; run.sh takes the + # host path. + interop-proton: + name: Interop (Qpid Proton -> ramqp-broker) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Install python3-qpid-proton + run: sudo apt-get update -qq && sudo apt-get install -y -qq python3-qpid-proton + - name: Qpid Proton interop + run: ramqp-broker/tests/interop/proton/run.sh + + # Process-level split-brain: a 3-node quorum cluster across Linux network + # namespaces, iptables-partitioned into majority/minority, asserting the + # majority stays available, the minority refuses (never silently accepts), + # and no committed message is lost on heal. Needs root (netns + iptables); + # the script self-elevates via sudo. + partition: + name: Partition (netns split-brain) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - name: Network-partition test + run: ramqp-broker/tests/partition/run.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd9a734..ee8397e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,12 @@ name: Release # (Settings -> Secrets and variables -> Actions). Create the token at # https://crates.io/settings/tokens with the "publish-update" scope. # -# Publish ORDER matters (see RELEASING.md): `ramqp` depends on `ramqp-core` -# by version, so core must land on crates.io first. `ramqp-broker` is not -# published yet (pre-alpha); `bench-compare` is publish = false. +# Publish ORDER matters (see RELEASING.md): `ramqp` and `ramqp-broker` both +# depend on `ramqp-core` by version, so core must land on crates.io first. +# `bench-compare` is publish = false. +# +# NOTE: the token needs the "publish-new" scope for a crate's FIRST-ever +# publish (publish-update alone fails on a new crate name). on: push: @@ -55,7 +58,29 @@ jobs: done echo "index never showed ramqp-core $core_version"; exit 1 - name: Publish ramqp - run: cargo publish -p ramqp --token ${{ secrets.CARGO_REGISTRY_TOKEN }} + # Skips cleanly if this version of ramqp is already published + # (e.g. a broker-only release retagging the same client version). + run: | + if cargo publish -p ramqp --token ${{ secrets.CARGO_REGISTRY_TOKEN }} 2>err.log; then + echo "ramqp published" + elif grep -q "already exists" err.log; then + echo "ramqp version already on crates.io; continuing" + else + cat err.log; exit 1 + fi + - name: Publish ramqp-broker + # Same tolerance: a client-only release may retag the same broker + # version. The broker's registry dependency is only ramqp-core (the + # `ramqp` dev-dependency is path-only and stripped at packaging), so + # the core index wait above is the only ordering it needs. + run: | + if cargo publish -p ramqp-broker --token ${{ secrets.CARGO_REGISTRY_TOKEN }} 2>err.log; then + echo "ramqp-broker published" + elif grep -q "already exists" err.log; then + echo "ramqp-broker version already on crates.io; continuing" + else + cat err.log; exit 1 + fi release: name: Create GitHub Release diff --git a/.gitignore b/.gitignore index e21a85e..47b0a01 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ ramqp-core/fuzz/coverage/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +# Generated by bench-compare/tuned/run.sh (provisional numbers live in the README) +bench-compare/tuned/results.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc2e3b..c24680e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,29 @@ All notable changes to ramqp will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.2] - unreleased + +### Added +- **`ramqp-broker` publishes to crates.io, first release 0.9.0.** The broker + (transient + Raft-replicated quorum + redb-durable queues, clustering with + leader routing, policies, transactions, management endpoint) ships as a + library crate and the `ramqp-brokerd` daemon (`cargo install ramqp-broker`). + Its config types (`BrokerConfig`, `QueuePolicy`, `OverflowBehavior`, + `ClusterMemberConfig`) are now `#[non_exhaustive]` — construct via + `Default`/`new` and set fields — so future knobs arrive without build + breaks. Pre-1.0: the Rust API may still change additively across 0.x. + +### Fixed +- **`ramqp-core` (0.2.5): a session now keys links by (name, role), not name + alone.** AMQP 1.0 §2.6.1 identifies a link by container-id + name + *role*, so + a peer may open a sender and a receiver that share a link name on one session. + The engine previously keyed by name only, so the second same-named attach was + misrouted and dropped — breaking Apache Qpid Proton, whose default link names + are derived from the address (identical for both directions). Found by the new + broker↔proton interop leg; regression-tested in `ramqp-core`. The `ramqp` + client is unaffected in practice (it generates unique link names), but ships + the corrected engine. + ## [0.8.1] - 2026-07-13 ### Security diff --git a/Cargo.lock b/Cargo.lock index 2086ac8..e4836ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,9 +125,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bitvec" @@ -161,9 +161,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ "borsh-derive", "bytes", @@ -172,15 +172,15 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" +checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -225,9 +225,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] @@ -240,9 +240,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.65" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "shlex", @@ -256,9 +256,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chrono" @@ -302,9 +302,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" dependencies = [ "clap_builder", "clap_derive", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstream", "anstyle", @@ -331,7 +331,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -438,9 +438,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -448,18 +448,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -516,7 +516,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -527,7 +527,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -553,7 +553,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "unicode-xid", ] @@ -587,7 +587,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -626,9 +626,9 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fe2o3-amqp" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5827661e79f2c6f06bd7ceac88fcd47cb9359cf632fe730652c74eb970c49a" +checksum = "8d9de0ffb697049b46177ccb5ed8102d8bc61d657be54678efdfd4ec7b6a33d8" dependencies = [ "bytes", "fe2o3-amqp-types", @@ -759,7 +759,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -896,9 +896,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "typenum", ] @@ -929,12 +929,13 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -942,9 +943,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -955,9 +956,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -969,15 +970,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -989,15 +990,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1027,9 +1028,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1070,9 +1071,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", @@ -1135,15 +1136,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -1217,7 +1218,7 @@ dependencies = [ "futures", "maplit", "openraft-macros", - "rand 0.8.6", + "rand 0.8.7", "serde", "thiserror 1.0.69", "tokio", @@ -1236,7 +1237,7 @@ dependencies = [ "proc-macro2", "quote", "semver", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1261,7 +1262,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1289,7 +1290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", "serde", ] @@ -1359,7 +1360,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1493,7 +1494,7 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "ramqp" -version = "0.8.1" +version = "0.8.2" dependencies = [ "base64", "bytes", @@ -1534,7 +1535,7 @@ dependencies = [ [[package]] name = "ramqp-broker" -version = "0.8.29" +version = "0.9.0" dependencies = [ "bytes", "futures-util", @@ -1553,7 +1554,7 @@ dependencies = [ [[package]] name = "ramqp-core" -version = "0.2.4" +version = "0.2.5" dependencies = [ "base64", "bytes", @@ -1571,9 +1572,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -1583,9 +1584,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -1685,14 +1686,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -1702,9 +1703,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -1779,7 +1780,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "rkyv", "serde", "serde_json", @@ -1801,9 +1802,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "once_cell", "ring", @@ -1815,9 +1816,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "zeroize", ] @@ -1835,9 +1836,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "same-file" @@ -1945,7 +1946,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1975,7 +1976,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1999,14 +2000,14 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -2080,9 +2081,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -2130,9 +2131,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -2147,7 +2148,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2195,7 +2196,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2206,14 +2207,14 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -2260,9 +2261,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -2275,9 +2276,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" dependencies = [ "bytes", "libc", @@ -2292,13 +2293,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2368,9 +2369,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap", "toml_datetime", @@ -2406,7 +2407,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2469,8 +2470,8 @@ dependencies = [ "http", "httparse", "log", - "rand 0.9.4", - "sha1 0.10.6", + "rand 0.9.5", + "sha1 0.10.7", "thiserror 2.0.18", ] @@ -2557,9 +2558,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -2568,9 +2569,9 @@ dependencies = [ [[package]] name = "validit" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4efba0434d5a0a62d4f22070b44ce055dc18cb64d4fa98276aa523dadfaba0e7" +checksum = "4417835826b78aa6265724624d424df2b1e6e71a33329227d8b04a88950780dc" dependencies = [ "anyerror", ] @@ -2611,18 +2612,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -2633,9 +2634,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2643,22 +2644,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] @@ -2679,9 +2680,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.102" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -2748,7 +2749,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2759,7 +2760,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2870,18 +2871,18 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" @@ -2917,28 +2918,28 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2958,7 +2959,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] @@ -2998,11 +2999,11 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/README.md b/README.md index babdfbe..6712514 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ broker — with no external AMQP dependencies anywhere. | Crate | What it is | Status | |---|---|---| -| [`ramqp`](https://crates.io/crates/ramqp) | The async **client** — connects to RabbitMQ 4.x, ActiveMQ Artemis, and other AMQP 1.0 brokers | Published (0.7.2; 0.8.1 pending — see [Upgrading to 0.8](#upgrading-to-08)) | -| `ramqp-core` | The role-agnostic **engine**: clean-room codec + type system, framing, session/link state machines, SASL (both directions) | 0.2.4, publishes together with `ramqp` 0.8.1 | -| `ramqp-broker` | The **broker**: store-and-forward AMQP 1.0 server with transient + Raft-replicated quorum queues | In development, working — see [The broker](#the-broker-ramqp-broker) | +| [`ramqp`](https://crates.io/crates/ramqp) | The async **client** — connects to RabbitMQ 4.x, ActiveMQ Artemis, and other AMQP 1.0 brokers | Published (0.8.1 — see [Upgrading to 0.8](#upgrading-to-08)) | +| [`ramqp-core`](https://crates.io/crates/ramqp-core) | The role-agnostic **engine**: clean-room codec + type system, framing, session/link state machines, SASL (both directions) | Published (0.2.4) | +| `ramqp-broker` | The **broker**: store-and-forward AMQP 1.0 server with transient, durable, and Raft-replicated quorum queues | Working, pre-1.0 — first crates.io release (0.9.0) ships with the next tag; see [The broker](#the-broker-ramqp-broker) | Everything is `#![forbid(unsafe_code)]`, async-first, and MIT. @@ -106,7 +106,7 @@ What's new is *optional*, for your `Cargo.toml` only if you want it: ```toml ramqp = "0.8" # the client, exactly as before ramqp-core = "0.2" # just the engine (codec/types/state machines), no client -ramqp-broker = "0.1" # embed the broker (pre-alpha; API unstable, not yet published) +ramqp-broker = "0.9" # embed the broker (working, pre-1.0; config API still settling) ``` A client-only build never compiles broker code, and vice versa — isolation is @@ -117,7 +117,9 @@ by crate boundary, not feature flags. ## The broker (`ramqp-broker`) A performance-first, highly-available AMQP 1.0 broker on the same clean-room -engine. **In development and moving fast** — the design, targets, and phased +engine. **Working, pre-1.0** — the wire behavior is exercised by conformance, +cross-client interop, and partition/chaos suites; the Rust config API is +still settling (`#[non_exhaustive]` types). The design, targets, and phased plan live in [`broker.md`](broker.md); its §11 checkboxes are the live status. Working today: diff --git a/RELEASING.md b/RELEASING.md index 41fcad6..b061c4b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -9,7 +9,7 @@ releases work. This page is the checklist. |---|---|---| | `ramqp-core` | new name (verified available) | **Yes — and always first** | | `ramqp` | exists (0.7.2 published) | Yes, after `ramqp-core` | -| `ramqp-broker` | new name (verified available) | **Not yet** — pre-alpha API; publish once it stabilizes | +| `ramqp-broker` | first publish: **0.9.0** | Yes — after `ramqp-core` (its only registry dependency; the `ramqp` dev-dependency is path-only and stripped at packaging) | | `ramqp-bench-compare` | — | Never (`publish = false`; keeps the `fe2o3-amqp` dev dependency out of the graph) | ## Why order matters (the Cargo.toml mechanics) @@ -29,7 +29,11 @@ fails unless a matching `ramqp-core` already exists on crates.io. Hence: 3. `cargo publish -p ramqp` `ramqp-broker/Cargo.toml` has the same shaped dependency on `ramqp-core`, so -the same rule applies whenever it starts publishing. +the same rule applies to it: core first, then the broker (`release.yml` does +core → ramqp → broker). The broker's first-ever publish needs the token to +have the **publish-new** scope, and it resets the version to a deliberate +**0.9.0** (the pre-publish 0.8.x patch numbers were per-commit workspace +churn, not a release cadence). ### Keeping the version pins in sync diff --git a/bench-compare/README.md b/bench-compare/README.md index c28e8fc..5e9245a 100644 --- a/bench-compare/README.md +++ b/bench-compare/README.md @@ -229,3 +229,42 @@ Known limitation (documented in the code): a paged queue's snapshot keeps spilled bodies **external** (node-local refs) — follower catch-up *via snapshot* for a deep paged queue is not yet supported (log-replay catch-up is); segment shipping is the follow-up. + +## Tuned-incumbent re-run — Phase 10 (provisional) + +The Phase 4/6 tables above ran RabbitMQ at stock defaults; broker.md §3.4 asks +for a re-run against a **tuned** incumbent, and for the quorum leg to be +**durability-parity** (our quorum fsyncs its Raft log via `store-redb`, matched +against RabbitMQ's fsync-backed quorum queue — closing the "in-memory vs fsync" +caveat that made the Phase 6 quorum gap partly a durability gap). Every leg here +runs **over loopback TCP against a broker process**, so the transport path is +identical for all rows (the Phase 4 table compared ours in-process vs RabbitMQ +in docker; this is fairer). + +Closed-loop e2e latency, µs, 20 000 samples ([`tuned/rabbitmq.conf`](tuned/rabbitmq.conf); +tuned Artemis = NIO journal + autotune off). Representative run: + +| leg | p50 | p99 | p99.9 | +|---|--:|--:|--:| +| ramqp-broker transient | 93.7 | 263.0 | 340.4 | +| RabbitMQ 4.x 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 4.x quorum (fsync) | 2260.5 | 3834.4 | 7303.2 | + +The headline holds under tuning and at durability parity: transient p50 ≈ 2.7× +below tuned RabbitMQ classic and ≈ 3× below tuned Artemis, and — the point of +the re-run — our **fsync-backed quorum** is ≈ 7× below RabbitMQ's fsync quorum, +so the Phase 6 gap was *not* merely a durability artifact. + +> **⚠️ PROVISIONAL — indicative only.** These numbers were taken on a +> shared/virtualized box (WSL2), not quiet bare metal, so they are directional, +> not the "defend-forever" figures broker.md §3.4 requires. Reproduce (and +> generate the real numbers on isolated hardware) with the one command: +> +> ```sh +> bench-compare/tuned/run.sh # stands up tuned RabbitMQ (5673) + Artemis (5674), runs all legs +> ``` +> +> Remaining §3.4 items: the bare-metal run, and a latency-under-N-concurrent- +> connections sweep (this table is single-connection closed-loop). diff --git a/bench-compare/src/bin/depth.rs b/bench-compare/src/bin/depth.rs index a6e813b..02fe774 100644 --- a/bench-compare/src/bin/depth.rs +++ b/bench-compare/src/bin/depth.rs @@ -65,12 +65,10 @@ async fn main() -> Result<(), Box> { let samples = env_usize("DEPTH_SAMPLES", 2_000); let data_dir = std::env::var("DEPTH_DATA_DIR").ok(); - let config = ramqp_broker::BrokerConfig { - max_queue_depth: target + samples * 8 + 10_000, - data_dir: data_dir.clone().map(Into::into), - resident_bytes_max: env_usize("DEPTH_RESIDENT_MAX", 64 * 1024 * 1024), - ..Default::default() - }; + let mut config = ramqp_broker::BrokerConfig::default(); + config.max_queue_depth = target + samples * 8 + 10_000; + config.data_dir = data_dir.clone().map(Into::into); + config.resident_bytes_max = env_usize("DEPTH_RESIDENT_MAX", 64 * 1024 * 1024); if let Some(dir) = &data_dir { // Fresh run: stale spill/snapshot dirs would skew nothing, but a // stale durable store would. @@ -143,7 +141,7 @@ async fn main() -> Result<(), Box> { received += 1; if received == filled { last = Some(d); - } else if received % 64 == 0 { + } else if received.is_multiple_of(64) { consumer.accept_through(&d).await?; } } diff --git a/bench-compare/src/bin/drain.rs b/bench-compare/src/bin/drain.rs index 1d0d7bf..a9315de 100644 --- a/bench-compare/src/bin/drain.rs +++ b/bench-compare/src/bin/drain.rs @@ -12,14 +12,11 @@ async fn main() -> Result<(), Box> { let session = conn.begin_session().await?; let mut consumer = session.create_consumer(&address).await?; let mut drained = 0usize; - loop { - match tokio::time::timeout(std::time::Duration::from_secs(1), consumer.recv()).await { - Ok(Ok(d)) => { - drained += 1; - consumer.accept(&d).await?; - } - _ => break, - } + while let Ok(Ok(d)) = + tokio::time::timeout(std::time::Duration::from_secs(1), consumer.recv()).await + { + drained += 1; + consumer.accept(&d).await?; } println!("drained {drained} leftover messages from {address}"); conn.close().await?; diff --git a/bench-compare/tuned/rabbitmq.conf b/bench-compare/tuned/rabbitmq.conf new file mode 100644 index 0000000..1a45546 --- /dev/null +++ b/bench-compare/tuned/rabbitmq.conf @@ -0,0 +1,26 @@ +# Latency-tuned RabbitMQ 4.x config for the ramqp-broker comparison. +# +# The published Phase 4/6 tables ran RabbitMQ at stock defaults; broker.md §3.4 +# calls for a re-run against a *tuned* incumbent so the comparison isn't +# flattered by the incumbent's out-of-box settings. This gives RabbitMQ its +# best shot: cut per-message and per-connection overhead, and keep the +# scheduler/GC quiet during a run. + +# Low latency: disable Nagle on the AMQP listener. +tcp_listen_options.nodelay = true + +# Don't let the memory alarm throttle publishers mid-run on a busy box. +vm_memory_high_watermark.relative = 0.8 + +# Management/stats collection adds per-message and periodic overhead; stretch +# the interval right out (we read results from the bench, not the UI). +collect_statistics_interval = 60000 + +# Bigger frames + more channels so the client-side credit window isn't the +# bottleneck (the bench uses a 1000-credit window). +frame_max = 1048576 +channel_max = 2047 + +# Classic-queue v2 index/store defaults are fine on 4.x; the durability-parity +# leg declares a quorum queue (x-queue-type=quorum) which fsyncs its Raft log — +# the apples-to-apples comparison against ramqp-broker's store-redb quorum. diff --git a/bench-compare/tuned/run.sh b/bench-compare/tuned/run.sh new file mode 100755 index 0000000..080b95b --- /dev/null +++ b/bench-compare/tuned/run.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +# Tuned-incumbent comparison for broker.md §3.4 / Phase 10: re-run the closed- +# loop latency bench against a *tuned* RabbitMQ (rabbitmq.conf here) instead of +# stock defaults, plus a durability-parity quorum leg (our store-redb quorum vs +# RabbitMQ's fsync-backed quorum queue). +# +# Fairness note: unlike the original Phase 4 table (ours in-process vs RabbitMQ +# in docker), EVERY leg here runs over loopback TCP against a broker PROCESS, so +# the transport path is identical for all rows. +# +# bench-compare/tuned/run.sh +# +# Emits a Markdown table to stdout and to $OUT (default bench-compare/tuned/ +# results.md). Requires docker + cargo. Numbers from a shared/virtualized box +# (e.g. WSL2) are INDICATIVE ONLY — see the caveat the script prints. +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" +OUT="${OUT:-$HERE/results.md}" +LAT_N="${LAT_N:-20000}" +RABBIT_IMAGE="${RABBIT_IMAGE:-rabbitmq:4-management}" +BRK="$ROOT/target/release/ramqp-brokerd" +LAT="$ROOT/target/release/latency" + +OURS_PORT=5680 +OURS_Q_PORT=5681 +# Non-default host ports so a pre-existing dev `rabbit` on 5672/15672 is untouched. +RABBIT_PORT="${RABBIT_PORT:-5673}" +RABBIT_MGMT_PORT="${RABBIT_MGMT_PORT:-15673}" +declare -a PIDS=() +DATA_DIR="$(mktemp -d)" + +ARTEMIS_IMAGE="${ARTEMIS_IMAGE:-apache/activemq-artemis:latest-alpine}" +ARTEMIS_PORT="${ARTEMIS_PORT:-5674}" +ARTEMIS_CORE_PORT="${ARTEMIS_CORE_PORT:-61617}" + +cleanup() { + set +e + for p in "${PIDS[@]:-}"; do kill "$p" 2>/dev/null; done + pkill -f "$BRK" 2>/dev/null + docker rm -f rabbit-tuned artemis-tuned >/dev/null 2>&1 + rm -rf "$DATA_DIR" +} +trap cleanup EXIT + +echo ">> building release bench + brokerd (store-redb) ..." +cargo build -p ramqp-bench-compare --release --bin latency >/dev/null +cargo build -p ramqp-broker --release --bin ramqp-brokerd --features store-redb >/dev/null + +# --- tuned RabbitMQ ------------------------------------------------------ +echo ">> starting tuned RabbitMQ ($RABBIT_IMAGE) ..." +docker rm -f rabbit-tuned >/dev/null 2>&1 || true +docker run -d --name rabbit-tuned \ + -p $RABBIT_PORT:5672 -p $RABBIT_MGMT_PORT:15672 \ + -v "$HERE/rabbitmq.conf:/etc/rabbitmq/conf.d/10-tuned.conf:ro" \ + "$RABBIT_IMAGE" >/dev/null +echo ">> waiting for RabbitMQ management ..." +for _ in $(seq 1 60); do + if curl -fsS -u guest:guest http://localhost:$RABBIT_MGMT_PORT/api/overview >/dev/null 2>&1; then break; fi + sleep 2 +done +curl -fsS -u guest:guest -X PUT http://localhost:$RABBIT_MGMT_PORT/api/queues/%2F/rq_classic \ + -H content-type:application/json -d '{"durable":true}' >/dev/null +curl -fsS -u guest:guest -X PUT http://localhost:$RABBIT_MGMT_PORT/api/queues/%2F/rq_quorum \ + -H content-type:application/json -d '{"durable":true,"arguments":{"x-queue-type":"quorum"}}' >/dev/null +echo ">> RabbitMQ queues declared (rq_classic, rq_quorum)." + +# --- tuned Artemis ------------------------------------------------------- +# Tuning applied at `artemis create` time (ARTEMIS_EXTRA_ARGS): NIO journal, +# autotune off, no message paging cap — a low-latency, throughput-oriented +# config (its best shot, mirroring the RabbitMQ tuning intent). +echo ">> starting tuned Artemis ($ARTEMIS_IMAGE) ..." +docker rm -f artemis-tuned >/dev/null 2>&1 || true +docker run -d --name artemis-tuned \ + -p $ARTEMIS_PORT:5672 -p $ARTEMIS_CORE_PORT:61616 \ + -e ARTEMIS_USER=guest -e ARTEMIS_PASSWORD=guest \ + -e "ARTEMIS_EXTRA_ARGS=--no-autotune --journal-type NIO --global-max-messages -1 --relax-jolokia --http-host 0.0.0.0" \ + "$ARTEMIS_IMAGE" >/dev/null +echo ">> waiting for Artemis + declaring an ANYCAST queue ..." +artemis_ready=0 +for _ in $(seq 1 40); do + if docker exec artemis-tuned /var/lib/artemis-instance/bin/artemis queue create \ + --name bench_artemis --address bench_artemis --anycast --durable \ + --preserve-on-no-consumers --auto-create-address \ + --url "tcp://localhost:61616" --user guest --password guest --silent >/dev/null 2>&1; then + artemis_ready=1 + break + fi + sleep 2 +done +[ "$artemis_ready" -eq 1 ] && echo ">> Artemis queue declared (bench_artemis)." \ + || echo "!! Artemis did not become ready — its rows will show n/a." + +# --- our broker: transient + durable single-node quorum ------------------ +echo ">> starting ramqp-brokerd (transient) on :$OURS_PORT ..." +RAMQP_LISTEN=127.0.0.1:$OURS_PORT "$BRK" >"$DATA_DIR/ours.log" 2>&1 & +PIDS+=($!) +echo ">> starting ramqp-brokerd (durable single-node quorum, store-redb) on :$OURS_Q_PORT ..." +RAMQP_LISTEN=127.0.0.1:$OURS_Q_PORT \ + RAMQP_NODE_ID=1 RAMQP_CLUSTER_LISTEN=127.0.0.1:7481 RAMQP_SEEDS=1=127.0.0.1:7481 \ + RAMQP_DATA_DIR="$DATA_DIR/redb" "$BRK" >"$DATA_DIR/ours-q.log" 2>&1 & +PIDS+=($!) +sleep 5 + +# --- run one latency leg, extract p50/p99/p99.9 -------------------------- +# usage: leg