Poise
Composable, runtime-independent load-balancing primitives for Rust.
Poise separates membership, eligibility, selection, dispatch, and feedback so that sophisticated routing remains explainable. Simple policies are small and direct. Affinity, topology, health, discovery, and readiness integrations build on the same contracts instead of hiding a second balancing system behind an adapter.
[!IMPORTANT] Poise is pre-1.0. Its behavioral contracts are heavily tested, and all six crates are published on crates.io. Use compatible
0.1requirements and review release notes before upgrading because pre-1.0 API changes remain possible.
Engineering book
The Poise engineering book is
the primary guide to policy choice, composition, failure semantics, operations,
performance, security, and verification. It is generated from the Markdown in
docs/, shares the Invariant Orrery's visual language, and retains mdBook's
search, keyboard navigation, deep links, print view, and theme controls.
Start with Getting started for a first integration, Choosing a policy for a production decision, or Composition for the complete control loop.
Why Poise exists
Load balancing is larger than choosing an index. A production request passes through several independently fallible decisions:
flowchart LR
D[Discovery] --> M[Versioned membership]
M --> E[Eligibility]
H[Health + circuits] --> E
L[Load signals] --> E
E --> P[Policy]
P --> S[Selection]
S --> R[Readiness-aware dispatch]
R --> F[Outcome feedback]
F --> H
F --> L
R --> O[Bounded observability]
Poise gives each boundary a concrete contract. Policies select; they do not dispatch. Discovery publishes immutable generations; it does not mutate a live candidate slice in place. Health and load are candidate signals; they do not silently replace selection semantics. Tower retains readiness after selection, and telemetry avoids endpoint-derived cardinality by default.
Workspace
| Crate | Responsibility | Runtime dependency |
|---|---|---|
poise-core |
Candidate contracts, selection policies, affinity, topology, and load trackers | None |
poise-discovery |
Atomic versioned snapshots, subscriptions, reconciliation inputs, and graceful draining | None |
poise-health |
Active probes, passive circuits, rolling outcomes, and group-relative outlier detection | None |
poise-tower |
Readiness-correct dispatch and optional snapshot-to-service reconciliation | Tower |
poise-tokio |
Optional probe timing and asynchronous discovery waits | Tokio |
poise-observe |
Fixed-cardinality counters and optional structured tracing | Optional Tower / tracing |
There is intentionally no umbrella crate. Applications depend only on the layers they use, and the deterministic policy core does not inherit an async runtime.
Policy surface
| Family | Included primitives | Primary contract |
|---|---|---|
| Stateless selection | Random, weighted random, power of two choices, least loaded | Select only eligible, in-bounds candidates |
| Stateful rotation | Round robin, smooth weighted round robin | Stable cycles and explicit identity-keyed state |
| Affinity | Rendezvous, weighted rendezvous, ring hash, Maglev | Deterministic placement and bounded disruption under churn |
| Load-aware affinity | Bounded-load weighted rendezvous | Preserve affinity while enforcing prospective capacity |
| Priority | Weighted priority tiers, overprovisioned failover, panic thresholds | Select a health scope before endpoint weighting |
| Locality | Health-adjusted locality weighting and spillover | Preserve priority and topology metadata |
| Feedback | In-flight accounting and peak EWMA | RAII-safe load updates under completion and cancellation |
See the focused contracts for weighted rendezvous, ring hash, Maglev, bounded-load affinity, priority routing, and locality routing.
Minimal policy example
use ;
let backends = ;
let mut policy = new;
let selected = policy.pick?;
assert_eq!;
# Ok::
Selection returns an index plus policy-specific decision metadata. The caller retains ownership of the candidate collection and decides how dispatch, readiness, retries, and feedback are performed.
Composition model
Membership
poise-discovery publishes immutable, monotonically versioned snapshots.
Readers keep valid handles to older snapshots while a newer generation becomes
visible atomically. Removal can enter a draining state before physical
retirement, and failed batches do not partially commit.
Eligibility
Candidates expose identity, status, and weight without forcing one concrete backend type. Health wrappers compose active classification, passive circuit state, outcome windows, and underlying service availability. Draining and operator opt-out are never revived by panic routing.
Selection
Policy implementations are deterministic when seeded, make empty and
ineligible outcomes distinct, and do not own dispatch. Cached policies rebuild
transactionally when relevant membership changes.
Dispatch
poise-tower polls readiness before policy selection and retains the selected
service's readiness permit until the call. Invalid custom-policy indices,
readiness failures, cancellation, and response errors remain explicit and
endpoint-aware.
Feedback and observation
Load guards distinguish completion from cancellation. poise-observe records
decision and attempt outcomes with fixed dimensions, saturating counters, and
optional tracing spans without placing telemetry dependencies in the policy
data plane.
For the full boundary model, read Architecture, the Tower dispatch contract, discovery reconciliation, Tokio integration, and observability.
Verification standard
Poise treats behavioral laws as public API.
- 240 authored test entry points cover examples, edge conditions, distributions, concurrency, cancellation, and transactional failure.
- 14 Proptest laws run 256 generated cases each by default and shrink failures into committed regression seeds.
- Mutation testing requires every terminating viable
poise-coremutation to be caught. The recorded full campaign examined 642 sites with zero viable survivors; watchdog-detected nontermination remains visible. - Six Loom models exhaustively explore scheduler interleavings around in-flight accounting and health state transitions.
- MSRV verification runs the workspace and all features on Rust 1.85.
- Doctests, strict Clippy, rustfmt, rustdoc, dependency policy, and package dry-runs are release gates.
Run the fast local gate:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
Run the scheduler models separately:
scripts/model-check.sh
Mutation testing is intentionally explicit and resource-bounded. It is not part of the default local hook:
scripts/mutants-core.sh --jobs 1
Do not run the fuzz targets without reading the resource limits in Fuzzing and concurrency. The complete strategy is documented in Testing and verification and Mutation testing.
Invariant Orrery
The interactive capability showcase replays a structured verification record as a bounded kinetic system. It does not execute tests, fuzzers, or mutation workloads in a visitor's browser. The renderer caps particles and device pixel ratio, pauses offscreen, and provides semantic and reduced-motion fallbacks.
Its data contract distinguishes live, recorded, stale, failed, and unavailable evidence. GitHub Pages publishes failing runs as broken proofs while leaving the associated Actions workflow red. The same Pages artifact serves the engineering book, so the visualization and reference material always describe one revision.
Performance stance
The core is designed to be allocation-conscious and deterministic, but Poise does not publish unsupported performance claims. Criterion baselines across small and large backend sets are still a release-roadmap item. Until those baselines land, evaluate the exact candidate counts, churn rates, and policy families used by your deployment.
Compatibility and stability
- Minimum supported Rust version: 1.85.
- Edition: Rust 2024.
- Unsafe Rust: forbidden by workspace lint.
- SemVer: pre-1.0 compatibility rules apply; behavior documented as a contract is changed deliberately and called out in release notes.
- Runtime neutrality:
poise-core,poise-discovery, andpoise-healthdo not require Tokio.
Contributing
Start with CONTRIBUTING.md. Changes to selection arithmetic, hashing, health boundaries, cached membership, topology, or concurrent load tracking need tests at the strongest applicable layer—not only a new example.
The repository uses Conventional Commits so release automation can distinguish fixes, features, and breaking changes. Install the repository-owned validation hook with:
scripts/install-hooks.sh
Releases
All workspace crates share one version. release-plz prepares dependency-aware
release pull requests, performs SemVer API checks, updates the changelog, creates
per-crate tags, and publishes in dependency order after the release PR is
merged. A local scripts/bump-version.mjs command is available for an explicit
maintainer override, and the pre-commit hook rejects partial version bumps.
The first crates.io publication completed on August 5, 2026. Subsequent releases use GitHub OIDC without a long-lived registry secret after trusted publishers are configured. See Release engineering.
Project status
The advanced policy, health, discovery, Tower, Tokio, observability, property, mutation, and Loom foundations are implemented. Remaining pre-1.0 priorities include criterion regression baselines, retry/hedge exclusion, adaptive concurrency, capacity-aware routing, and governance. See the roadmap.
Security
Please report vulnerabilities privately according to SECURITY.md. Do not open a public issue for a suspected vulnerability.
Support
If Poise is useful to your infrastructure, you can support its continued engineering and maintenance through TokenTip.
License
Poise is licensed under either the Apache License, Version 2.0 or the MIT license, at your option.