Skip to content

Contributing: build, test and change the broker

Build the broker and the clients, run the test suites and the tutorial programs, and the rules a change follows: docs, changelog and the checks CI runs.

Updated View as Markdown

Queen is one repository with one Rust binary at its center: the broker, which links the proxy and the Kafka facade as libraries and embeds the dashboard. It keeps its state in a data directory, so building it, testing it and running it needs no database and no other service. A clean clone and a Rust toolchain are enough to build a node and run it, and that is a property we work to keep.

Pull requests target master. Run the suite of the component you touched, and read the rules below before you change the engine: each one is there because breaking it is silent until it is expensive.

Why
a broker is trusted with data people cannot afford to lose. Rules that a machine checks (a lint, a golden file, a docs gate) are the ones that still hold a year later.

Every commit carries a Signed-off-by: line from its author, which git commit -s adds. It certifies the Developer Certificate of Origin: the author wrote the change, or has the right to submit it under the Apache 2.0 license. .github/workflows/dco.yml refuses a pull request with a commit that lacks the line, because the project can license only code its contributors had the right to give. How the project is run and who runs it are in GOVERNANCE.md and MAINTAINERS.md.

The repository

Path What it is
server/ The broker: crate queen-engine, binary queen. src/rsm/ is the engine, src/handlers/ the HTTP edge.
clients/ The SDKs (client-js, client-py, client-go, client-rust, client-php, client-cpp) and client-cli (queenctl).
protocols/queen-kafka/ The Kafka wire facade, linked by the kafka feature.
proxy/ The multi-tenant proxy, linked by the server feature; proxy/console/ is its operator console.
app/ The dashboard (Vue and Vite). It builds into server/webapp/dist, which the binary embeds.
shared/ JavaScript time-range presets and conversions used by the dashboard and proxy console.
crates/queen-protocol/ Shared wire types for the Rust client and the embedded library API.
test/ run.sh (the Docker test matrix), jepsen/, recovery/ (failure rehearsals), raft/ (the determinism lint and harnesses).
benchmark-queen/ Archived benchmark sessions: harness, raw output, results.
webdoc/ This site.
book/ The little book of QueenMQ, in LaTeX.
figures/ The drawings of the book and the site, one module each.
examples/tutorials/ The programs behind every piece of code in the book and on the site, in six languages.

Build and run

The committed server/webapp/dist and proxy/console/dist let a clean clone compile without Node. When you change app/, rebuild the dashboard and then the broker, because the assets are embedded at compile time:

npm --prefix app ci && npm --prefix app run build
cargo build --release --manifest-path server/Cargo.toml

When changing shared/timeRanges.mjs, rebuild both frontends before the broker: run npm --prefix proxy/console ci && npm --prefix proxy/console run build alongside the dashboard build above. The common hourly presets feed every picker; extended periods, system metrics, lag thresholds and console usage add their choices in the same module. The Dockerfile copies shared/ into both frontend build stages.

Run one node on a throwaway data directory and check it:

QUEEN_RAFT_DIR=./queen-data ./server/target/release/queen
curl -s localhost:6632/health

/health answers 503 with "status":"settling" until the node knows a leader that hears from a majority and has caught up, then 200. Delete the directory to start empty. The version comes from server/server.json (server/build.rs embeds it), the minimum Rust version is 1.88 (rust-version in server/Cargo.toml), and ./build.sh builds the product image from the root Dockerfile.

Test

Command What it runs
cargo test --manifest-path server/Cargo.toml --locked The broker’s in-process tests: engine, crash, recovery and cluster tests under server/src/rsm/tests/.
test/run.sh Every client suite against a fresh single node, with the tenancy header off and on (single, tenanted), in Docker.
test/run.sh --suite js --topo single,ha The same suite against a three-node openraft cluster as well.
test/run.sh --suite http Every KV and timer route at the raw wire, with no SDK in between.
test/raft/lint/deny-bites.sh Proves the determinism lint still refuses a clock, an environment read and a random number.

test/run.sh builds the broker image as queen:test, gives every suite and topology its own Compose project with fresh volumes, and prints a pass/fail matrix. The tenancy parity gate fails the run when a suite’s single and tenanted results differ, because the tenancy header must change nothing for a client that does not send it. CI runs the same matrix (.github/workflows/tests.yml).

The Jepsen suite is in test/jepsen/ (Clojure and Leiningen, five nodes over SSH): run-matrix.sh runs one test per line of a matrix file from the control node. The failure rehearsals behind the recovery runbook are in test/recovery/. Neither runs in CI.

Rules for engine changes

Apply is pure. rsm/apply.rs, rsm/state/ and rsm/store/ may not read a clock, the environment or a random number: server/clippy.toml lists those calls, and the three modules deny them. Time and ids come from the planner, inside the entry. This is the rule every replica’s correctness rests on, so after you change the list, run test/raft/lint/deny-bites.sh, which plants one call of each kind in rsm/apply.rs and requires clippy to refuse every one.

Bytes on disk are versioned. Effects and outcomes share one catalogue version. A new field is a new version decoded beside the old one, never an edit of an existing encoder, and the golden fixtures in server/src/rsm/tests/golden/ fail when an encoding changes. A cluster raises its version only once every member can read it, which is what lets a rolling upgrade run nodes of two releases side by side.

Nothing blocks the async runtime. Every I/O and RPC has a deadline, store and file calls run on blocking threads, and no std::sync::Mutex is held across an .await. A blocking call on a runtime thread stalls every request scheduled behind it.

Rules for the docs

sourceOfTruth is a debt list. Each page names the files that govern it, and check-sourceoftruth.mjs fails a page whose last commit is older than the last commit of any of them. Update the page in the same change as the code.

The site follows the book. The little book of QueenMQ (book/, LaTeX, built by book/build.sh) and these pages tell the same story in the same order. Change one, change the other. book/build.sh --publish puts a new edition on the site: the PDF, its cover and edition.json in webdoc/public/book/, which the book page reads.

Code on a page ran. A page shows code with <SdkCode id="push" />: the guide:start(push) region of each language’s programs under examples/tutorials/ (00-five-minutes, book-client-guide, book-use-cases), cut by webdoc/scripts/lib/guide-regions.mjs, the same cutter the book, the homepage and the README check use. examples/tutorials/run.sh runs every program against a broker, and each program checks its own results. A region JavaScript lacks fails the build; a region another language cannot express is listed, with its reason, in webdoc/src/lib/book-code.ts. Keep region lines to 92 characters, so they fit the book’s page.

Drawings have one source. Each figure is a module in figures/src/<chapter>/<id>.mjs; node figures/build.mjs writes a light and a dark SVG, the book prints them as PDFs, and the site shows them with <Fig id="..." />. Every figure is 1000 units wide, so they all print at the same type size.

Older snippets. The reference pages still include regions of the client test suites (docs:start(<id>)), turned into partials by webdoc/scripts/gen-snippets.mjs.

Generated partials are not edited by hand. The route tables, the environment variables and the metric families are read out of the Rust by webdoc/scripts/gen-*.mjs; run pnpm --dir webdoc gen. Where a generator mirrors Rust logic in JavaScript (the access levels, the proxy’s route classes), it guards the mirror with a fingerprint of the Rust function and refuses to run when the function changes, until someone re-reads it.

One gate. pnpm --dir webdoc verify runs the generator drift check, the build, the markdown and brief checks, the linter, the prose check, the sourceOfTruth check, the search-surface check and the README check.

Rules for benchmarks

A number is published only with an archived artifact under benchmark-queen/: the harness, the raw output and the conditions (hardware, node count, shape, duration). The site’s benchmark figures are rendered from those artifacts by webdoc/scripts/charts.py, and the drift check fails when a figure no longer matches its data. A number without an artifact is a claim nobody can check, so it stays out.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close