Durable, NSQ-compatible messaging for Kubernetes — built in Rust.
Architecture · NSQ performance boundaries · Kubernetes operations · Console operations · v0.8.4 release
RustQueue 0.8.4 is a Kubernetes-native, NSQ V2-compatible message queue for trusted internal networks. It is written in Rust and uses a deliberately simple share-nothing model: each Broker owns one durable RWO PVC, while Kubernetes provides scheduling, rollout and discovery.
Current release: v0.8.4. RustQueue is a production candidate for workloads that accept single-PVC durability and at-least-once delivery. It does not replicate messages between Brokers and is not an HA replacement for a replicated log.
The complete architecture and reliability contract is documented in
docs/architecture/share-nothing-v7.md.
| Area | Contract |
|---|---|
| Durability | Default PUB/MPUB/DPUB return after local segment fsync; opt-in write_ack and nsq_relaxed return after append with explicit crash-loss windows; FIN/REQ use a durable channel WAL |
| Delivery | At least once; a restart may redeliver a message without a durable FIN |
| Compatibility | NSQ V2 core commands, lookup, standard Stats fields, TLS/mTLS, AUTH, Snappy, Deflate, fan-out and ephemeral channels |
| Kodo | Default-off compatibility profile: stable publish Gateways from /nodes, real Broker owners from /lookup, and no upstream Kodo change |
| Message size | Conservative 20 MiB default; stable 100 MiB protocol/storage ceiling; Kodo profile validates exactly 104,857,500 bytes |
| Deployment | Kubernetes 1.28+, SSD-backed RWO PVCs, StatefulSet Broker ordinals, retained claims |
| Scaling | Add Brokers without a central queue coordinator; consumers connect to every owner of a Topic |
| Data model | Local Topic/Channel state; no cross-Broker ordering or global channel catalog |
The single-copy model is intentional. If a PVC is permanently lost, the messages stored on that Broker are lost. Configure disk pressure protection, monitor the exported metrics, and choose PVC/storage failure policies that fit your workload before deploying to production.
- Larger durable Channel groups.
FINandREQrequests now share a bounded 1 ms Channel WAL group commit of up to 1,024 requests. Every success still crosses the affected WALfsyncbefore the client sees completion. - More delivery concurrency. Channel WAL
fsyncruns after releasing the Topic state lock and uses an independent commit gate, so delivery reservations can continue while the durable boundary is being written. - Lower hot-path allocation cost. WAL commands use stack encoding, TCP and
Channel operations share
Arc<str>channel identities, Channel futures avoid an extraBox, and each session bounds active Channel operations at 256. - Stricter benchmark completion. The benchmark consumer waits for EOF and verifies a complete final drain before accepting a result.
- No compatibility change. The v7 on-disk format and the durable
FIN/REQfsync contract remain unchanged.
See the v0.8.4 release notes and NSQ performance boundaries for the contract and benchmark interpretation.
Every release contains native Linux binaries, the Console UI, source, the Helm Chart and a checksum manifest:
| Asset | Contents |
|---|---|
rustqueue-0.8.4-linux-x86_64.tar.gz |
Linux x86_64 binaries, Console UI and example configuration |
rustqueue-0.8.4-linux-aarch64.tar.gz |
Linux ARM64 binaries, Console UI and example configuration |
rustqueue-0.8.4-source.tar.gz |
Source archive for the tagged commit |
rustqueue-0.8.4.tgz |
Helm Chart |
SHA256SUMS-0.8.4 |
SHA-256 checksums for every downloadable artifact |
arch="$(uname -m)"
curl -LO "https://github.com/SamuelSupe/rustqueue/releases/download/v0.8.4/rustqueue-0.8.4-linux-${arch}.tar.gz"
curl -LO "https://github.com/SamuelSupe/rustqueue/releases/download/v0.8.4/SHA256SUMS-0.8.4"
sha256sum --check --ignore-missing SHA256SUMS-0.8.4
tar -xzf "rustqueue-0.8.4-linux-${arch}.tar.gz"standard producer -> proxy Service ----------------------------+
|
Kodo producer -> discovery /nodes -> Gateway Service ----------+
(3 advertised identities) |
v
one publish-ready Broker -> its PVC
consumer -> discovery /lookup -> every broker that owns the topic
-> direct NSQ TCP connections
operator -> eligible nodes -> StatefulSet ordinal + retained RWO PVC
- In the default
durablemode, a successfulPUB,MPUB, orDPUBhas passed local segmentfsync.write_ackreturns after append but delays consumption until background fsync;nsq_relaxedreturns and exposes the append immediately. Both relaxed modes can lose their unsynced tail. - Concurrent publishes to one topic use a bounded group commit (up to 64
requests or 8 MiB, with at most 1 ms coalescing delay) and all wait for the
same durable boundary before receiving
OK. - Per-topic publish and channel-commit workers retire after 60 idle seconds.
The defaults cap durable topics at 10,000 and each worker class at 1,024;
exhausted capacity returns retryable HTTP
429instead of growing tasks and memory without bound. - IDs are durably reserved in blocks, so restarts may create harmless gaps but never reuse a broker-scoped message ID.
- Concurrent
FINandREQfor one topic share a bounded group commit (up to 1,024 requests with at most 1 ms coalescing delay). The Topic state lock is released during the WALfsync, while an independent gate serializes durable Channel mutations. A successful response has passed every affected local channel WALfsync. - Delivery is at least once. A restart redelivers messages without durable FIN.
- The broker PVC is the only copy; permanent PVC loss loses its messages.
- Topics and channels are broker-local. Lookup consumers union all owners.
- Normal GC retains every message accepted while no durable Channel exists, across restart and without a bootstrap timeout. The first durable Channel starts at that persisted unrouted boundary. Deleting the last durable Channel starts a new boundary at the current Topic tail; ephemeral Channels do not clear it.
- Once a durable Channel exists, a later Channel can still bootstrap from the last 90 seconds. This covers one official Go client default 60-second lookup poll plus its 30% jitter. The Kodo profile forces 180 seconds so one failed lookup request still gets a second discovery opportunity.
- The stable v7 single-message limit is 100 MiB. The conservative defaults remain 20 MiB per message and 64 MiB per MPUB body; the opt-in Kodo profile raises them to 100 MiB and 128 MiB respectively.
- Publish bodies are written to the segment with vectored header/metadata/body I/O. The durable path does not build a second batch body or a full record buffer, and admission charges the input plus bounded encoding metadata.
- Backlog has no message-count ceiling. Sealed segments keep only constant-size summaries in RAM; fixed-size message metadata is paged through one bounded Broker cache. Publish admission is therefore governed by the configured PVC watermarks, not by an arbitrary number of messages.
- GC locates retention boundaries by monotonic indexes instead of rescanning a full backlog every five seconds. A normal tick rotates across at most 128 Topics, and it seals an active segment only when that whole tail is already reclaimable. Scrub snapshots immutable files under the Topic lock, then verifies them lock-free with a default 64 MiB/s I/O limit.
- Slow consumers share a 512 MiB node-wide delivery working-set budget; each connection can request at most 32 MiB of payload. Cache misses charge both the file-read buffer and delivered body before I/O, and cancelled reads return their memory permits and in-flight reservations.
rustqueued: local durable NSQ broker, standard NSQ Stats compatibility and authenticated HTTP management API.rustqueue-discovery: stateless EndpointSlice-derived lookup service with incremental topic/channel indexes and revision-head registry polling.rustqueue-proxy: bounded producer TCP/HTTP proxy; new TCP connections choose a least-active ready Broker and are rotated after a jittered five-minute default lifetime so a small long-lived producer pool follows fleet changes. In Kodo mode the same binary runs as three publish Gateway Pods behind one highly available Service. Each Gateway terminates the NSQ producer protocol, enforces the 100 MiB single-message limit, and may retry another Broker only after an explicit pre-commit rejection. A failure after the full body was sent is returned as ambiguous and is never retried automatically.rustqueue-operator: creates the StatefulSet, retained PVCs, discovery, proxy, RBAC, disruption budgets, PVC expansion and drain-aware one-at-a-time rolling updates. The 0.8 Kodo profile adds atomic Discovery cutover, producer-restart fencing and fail-closed decommissioning.rustqueue-console: Kubernetes and broker observability backend serving the bilingual Carbon UI, with default-off native Topic/Channel management.rustqueuectl: Kubernetes-aware cluster status, targeted maintenance, rollout, storage expansion, fan-out stats and scrub commands.
The broker implements IDENTIFY, AUTH, SUB, PUB, MPUB, DPUB, RDY,
FIN, REQ, TOUCH, NOP, and CLS, including TLS/mTLS, Snappy, Deflate,
output buffering, sampling, fan-out and ephemeral channels.
The host does not need Rust. Builds and tests use the pinned Docker toolchain:
make check
make test
make clippy
make image
docker compose up -dThen publish through the compatible HTTP API:
curl -X POST --data-binary 'hello' \
'http://127.0.0.1:4151/pub?topic=events'
curl 'http://127.0.0.1:4151/stats?format=json'Run the official client behavior matrix:
make compatThis covers official Go and Python clients across direct publishing and consumption, lookup, MPUB, DPUB, REQ, TOUCH, RDY, fan-out, sampling, ephemeral channels, Snappy, Deflate, TLS, mTLS and external AUTH.
Prerequisites:
- Kubernetes 1.28 or later;
- an SSD-backed
ReadWriteOnceStorageClass that supports PodfsGroupownership andallowVolumeExpansion: truefor online growth; - eligible nodes labelled for brokers;
- the broker runtime and operator images in the cluster registry.
kubectl label node worker-1 rustqueue.io/eligible=true
helm upgrade --install rustqueue deploy/helm/rustqueue \
--namespace rustqueue --create-namespace \
--set queue.image=registry.example/rustqueue:0.8.4 \
--set queue.storageClassName=ssd-rwoThe producer endpoints are exposed by the proxy Service. Consumers should use
both discovery replicas as lookupd endpoints. The generated runtime Secret is
internal; set queue.registrySecretName to use a pre-created Secret containing
admin-token, registry-token and console-token. The console token can read
broker observations and, only when Console management is explicitly enabled,
call the narrow native Topic/Channel management API. It cannot authorize drain,
scrub, upgrade or the NSQ-compatible admin API. Client TLS is optional and
always supplied through an existing Kubernetes Secret; the operator does not
run a CA.
Kodo compatibility is implemented entirely by RustQueue as a separate,
default-off deployment profile. Kodo continues to use RustQueue Discovery:
/nodes returns publish Gateways and /lookup returns consumer-facing Broker
owners.
helm upgrade --install rustqueue deploy/helm/rustqueue \
--namespace rustqueue \
--set 'queue.image=registry.example/rustqueue@sha256:<64-lowercase-hex-digest>' \
--set queue.kodoCompatibility.enabled=trueThe chart pins this profile to three Brokers, storage feature level 2, a
180-second bootstrap-retention window, a 100 MiB RustQueue single-message
ceiling, a one-CPU/2 GiB Broker request, and three stable Gateway Pods with
required hostname anti-affinity. The profile therefore needs three schedulable
nodes. Each Gateway requests one CPU and 768 MiB, is not CPU-limited, and is
limited to 1 GiB memory so its fixed 512 MiB in-flight body budget is
scheduler-backed.
The reviewed Kodo source enforces an application maximum of 104857500 bytes
(100 MiB minus 100 bytes) and sets the go-nsq producer write deadline to three
seconds. A maximum-size publish therefore needs more than roughly 34 MiB/s of
application goodput from Kodo to the Gateway, with additional headroom for
scheduling and network variance. Validate that exact 104857500-byte path with
the included acceptance before rollout. The profile also requires an immutable
lowercase @sha256 image; imagePullPolicy=Never is accepted only for a
preloaded local test image.
For an existing deployment whose running binary still advertises the legacy
32 MiB capability, first roll the new immutable image with Kodo compatibility
disabled and wait for Ready; enable the Kodo profile in a second change. The
operator will not cross the 100 MiB/feature-level-2 fence while an old Broker is
still running.
Activation is fail-closed and ordered. The operator first waits for at least
two Ready Brokers, then starts all three Gateways before disrupting an old
Broker. This allows a migration to recover while one of the three target
Brokers is unavailable; the Broker target remains three. It rolls Discovery
Pods to a distinct kodo mode label while the Discovery Service still selects
the existing direct mode. Only after the complete Kodo-mode set is Ready does
it atomically switch the Service selector. Broker rollout and maintenance
remain blocked until
KodoCutoverReady=True and KodoProducerRestartConfirmed=True.
queue.kodoCompatibility.cutoverGraceSeconds defaults to 630 seconds because
Kodo's default nsq.refresh_at is 300 seconds. Set it to at least two deployed
Kodo refresh_at intervals plus 30 seconds. This preserves a second refresh
opportunity if the first request overlaps the Discovery Service cutover. New
installations may point Kodo at the Discovery Service after
KodoGatewaysAdvertised=True.
The reviewed Kodo refreshNodes() implementation replaces cached producers
when an advertised address changes. RustQueue still requires an explicit
publisher restart as a cutover fence, so rollout safety does not depend on that
private cache behavior remaining unchanged in a later Kodo build. After
KodoGatewaysAdvertised=True, restart every Kodo publisher process and wait
for it to become Ready, then change
queue.kodoCompatibility.producerRestartNonce to a new value:
helm upgrade rustqueue deploy/helm/rustqueue \
--namespace rustqueue \
--reuse-values \
--set-string queue.kodoCompatibility.producerRestartNonce="$(date +%s)"The Operator captures the nonce only after Gateway advertisement and will not
drain, replace, or place a Broker into maintenance until the value changes.
Losing Gateway advertisement resets the fence and requires another restart
confirmation. An existing Kodo deployment is fully migrated only after both
KodoCutoverReady=True and KodoProducerRestartConfirmed=True.
During an existing-cluster migration, do not publish messages above the
previous Broker limit until the RustQueue resource itself reports Ready=True;
Gateway advertisement and explicit producer restart intentionally precede the
Broker configuration rollout.
/nodes then advertises three producer identities on ports 4150, 4152, and
4153 of one ClusterIP Gateway Service. Every port targets every Ready Gateway,
and every Gateway can publish to every publish-ready Broker. A single Gateway
restart therefore does not turn an advertised identity into a dead Pod address.
/lookup continues to expose the real Broker owners for consumption. During a
Broker restart it returns the healthy topic shards instead of blocking every
consumer; the separate complete-inventory signal remains false until all three
Stats shards are back.
When ServiceMonitor support is enabled, the dedicated Gateway monitor scrapes
the metrics-only port 4160; Gateway Stats ports do not accept HTTP publish
requests. Any increase in unknown publish outcomes raises the critical
RustQueueAmbiguousPublish alert.
Gateway ingress accepts only Pods matching
queue.kodoCompatibility.allowedPodSelector (by default
app.kubernetes.io/name=kodo) in the RustQueue namespace. Label the Kodo
workload accordingly. For a separate namespace, set
allowedNamespaceSelector to labels present on that namespace; the chart's
general same-namespace policy explicitly excludes Gateway and Broker Pods.
In Kodo mode, Broker TCP ingress is restricted to RustQueue Pods and that same
Kodo peer selector. Broker HTTP remains reachable for stats and monitoring, but
its publish endpoints require the internal admin token. Kodo consumers and
publishers share the NSQ TCP protocol and port, so a permitted Kodo Pod remains
technically able to publish directly; keep the selector limited to the trusted
Kodo workload and use the advertised Gateway addresses for every producer.
Discovery /nodes advertises those Gateways for publishing while /lookup
continues to advertise the currently healthy real Broker owners for
consumption. Automatic Kodo channel cleanup is hard-disabled: the CRD accepts only
cleanupEnabled: false, every RustQueue binary rejects an attempt to enable
the compatibility cleanup path, and /channel/delete returns a 404 whose body
does not contain CHANNEL_NOT_FOUND, so unchanged Kodo stops before contacting
Brokers.
Cleanup must remain disabled until RustQueue has a cluster-wide atomic,
authenticated deletion transaction.
Enabling this profile activates the storage feature-level-2 rollback fence;
records above the legacy 72 MiB boundary require that feature level. Disabling
the profile is an explicit decommission, not a migration back to unsafe direct
publishing. First stop every Kodo workload using this Discovery Service, then
set queue.kodoCompatibility.enabled=false and
queue.kodoCompatibility.decommissionConfirmed=true. Without that
confirmation the Operator fails closed before changing any Broker, Discovery,
or Gateway resource. After confirmation it completes the target Broker rollout
while the Gateways remain available, switches Discovery to direct mode, and
removes the Gateway resources; no Kodo workload may be restarted against that
direct mode. The profile retains feature level 2 as the monotonic storage floor
and preserves enough delivery memory to consume previously stored 100 MiB
messages. Channel message_count remains monotonic across empty and eviction,
while requeue_count and timeout_count are persisted with the Channel WAL
and checkpoint. Kodo can therefore calculate rate thresholds across Broker
restarts without a counter rollback. Run the real Kodo
nsqadmin.go parser and admin-flow replay before releasing this profile. The
replay is fully offline and copies that upstream
file unchanged into an isolated test module; it also rejects an unreviewed
hash for the Kodo producer discovery, producer client, or admin source. Only
unrelated Kodo globals are stubbed:
KODO_SOURCE_DIR=/path/to/kodo make kodo-replay
# Or include it in the complete gate:
KODO_ACCEPTANCE=1 KODO_SOURCE_DIR=/path/to/kodo ./scripts/release-gate.shThe chart enables RustQueue Console by default as a ClusterIP-only Service. It does not create an Ingress and has no built-in login. Put the Service behind your existing VPN, SSO or authenticated Ingress when broader access is needed:
kubectl -n rustqueue port-forward svc/rustqueue-console 4180:4180Open http://127.0.0.1:4180. Console never reads message bodies, Secrets or
container logs. It keeps a bounded 15-minute live trend in memory and resets
that trend when its Pod restarts. Topic/Channel management is disabled by
default; enable it with --set console.management.enabled=true. See the Console security and deployment
guide.
Console polls a small observation head every two seconds. It fetches the full Topic/Channel catalog only on Broker, registry-revision or management-fence changes, with a 30-second fallback refresh. This keeps observation traffic bounded by small per-Broker heads instead of transferring every catalog on every poll.
Runtime Pods run as UID/GID 65532 with a read-only root filesystem. Broker Pods
set fsGroup=65532 and fsGroupChangePolicy=OnRootMismatch so dynamically
provisioned PVCs are writable without an init container.
The chart runs two Operator replicas. A Kubernetes Lease permits only one to mutate resources, while the standby takes over after lease expiry. PDBs protect the Operator, discovery and Broker availability from concurrent voluntary disruption. PDBs cannot make a single-copy Broker PVC redundant.
Operational state is persisted in the RustQueue status. Standard-style
Conditions report readiness, progress, degradation, storage, upgrade,
maintenance, Broker availability and retained PVCs. currentOperation records
the exact target and stage; the last 20 replaced operations remain in
operationHistory. See the Kubernetes operations runbook.
Examples using a locally built rustqueuectl:
rustqueuectl --namespace rustqueue status
rustqueuectl --namespace rustqueue brokers
rustqueuectl --namespace rustqueue maintenance rustqueue-4 enable
rustqueuectl --namespace rustqueue maintenance rustqueue-4 disable
rustqueuectl --namespace rustqueue storage 500Gi
rustqueuectl --namespace rustqueue stats
rustqueuectl --namespace rustqueue scrubPrometheus Operator resources are optional:
helm upgrade --install rustqueue deploy/helm/rustqueue \
--namespace rustqueue \
--set monitoring.serviceMonitor.enabled=true \
--set monitoring.prometheusRule.enabled=trueThe default rules cover loss of Operator leadership, disk pressure, storage errors, protective message eviction, sustained throttling and high fsync p99.
For a functional validation on local OrbStack Kubernetes:
make k8s-acceptance
make k8s-multi-acceptanceThe first acceptance validates the HA operator, StatefulSet/PVC attachment,
discovery, proxy, official Go client behavior, Pod restart with the same PVC,
durable backlog recovery, and the safety gate that refuses to roll the only
broker. The second runs three real broker Pods with three independent RWO PVCs
and two genuinely different A/B broker binaries. It validates PDBs, online PVC
growth, targeted maintenance, canary approval, Operator leader failover,
durable operation history, two-step feature activation and rollback fencing
while publishing and consuming through discovery. The acknowledged-message
ledger must finish with missing=0. OrbStack has one
physical node, so this test uses capacity-only Kubernetes Node objects and
test-only direct Pod placement; production anti-affinity is unchanged. A unit
fixture covers discovery indexing for 500 brokers. No 500-broker deployment or
load test is part of the functional gate.
The v0.8.4 CI/CD workflow publishes a Release only after the non-Kubernetes
release gate, both native Linux builds, packaging and checksum verification
succeed. The v0.8.0 Kodo compatibility baseline additionally passed the
unmodified Kodo source replay, an exact 104,857,500-byte PUB/DPUB with one
Gateway failover, and a three-Broker operational ledger with 3,239 expected and
unique messages, zero missing, zero duplicates and zero publish errors.
The broker checks its data filesystem once per second. At either the configured
high watermark or minimum-free threshold it stops accepting publishes. HTTP
returns 429 with Retry-After; TCP returns E_THROTTLED. Consumption and FIN
remain available.
Normal segment GC runs first. If pressure lasts beyond the configured grace period and protective eviction is enabled, the oldest complete local segment may be removed. RustQueue persists every affected channel gap and a structured audit intent before deleting the segment. Protective eviction is a last-resort availability policy and intentionally loses the evicted messages.
A runtime storage write, fsync, or checksum failure is fail-closed: the storage handle is isolated, readiness is withdrawn, and the broker exits so Kubernetes can restart it through normal tail recovery. Payload reader leases also prevent GC, protective eviction, or topic deletion from racing an active disk read.
The producer proxy defaults to 10,000 simultaneous TCP connections, a 64 MiB
request-body limit, a 512 MiB node-wide in-flight HTTP body budget, and a
jittered 300-second TCP tunnel lifetime. A zero lifetime disables rotation.
Override them with RUSTQUEUE_PROXY_MAX_CONNECTIONS,
RUSTQUEUE_PROXY_MAX_MESSAGE_BYTES, RUSTQUEUE_PROXY_MAX_BODY_BYTES,
RUSTQUEUE_PROXY_MAX_INFLIGHT_BYTES,
RUSTQUEUE_PROXY_TCP_COMMAND_TIMEOUT_MS, and
RUSTQUEUE_PROXY_TCP_MAX_CONNECTION_AGE_SECONDS.
Discovery gives every Kubernetes EndpointSlice list an explicit 1.5-second
deadline. Override it with RUSTQUEUE_ENDPOINT_SLICE_TIMEOUT_MS; timeouts are
reported by rustqueue_discovery_endpoint_slice_timeouts_total and stale
Broker observations still expire after five seconds.
Compatible endpoints:
/pub,/mpub,/stats,/ping,/info/topic/create|delete|empty|pause|unpause/channel/create|delete|empty|pause|unpause
In the Kodo compatibility profile, automatic /channel/delete cleanup is
intentionally fail-closed. Use RustQueue's authenticated native management
flow for administrative changes.
Native broker endpoints:
GET /v1/healthGET /v1/registryGET /v1/registry/headGET /v1/capabilitiesGET|POST /v1/drainGET /v1/statsGET /v1/observe(console token, no message bodies)GET /v1/observe/head(console token, lightweight runtime/revision head)POST /v1/manage/topics/{action}(console token, only when enabled)POST /v1/manage/channels/{action}(console token, only when enabled)POST /v1/manage/fences/sync(console token, only when enabled)POST /v1/storage/scrubGET /metrics
Discovery serves /lookup, /topics, /channels, /nodes, /ping, /info,
/v1/publishers, /v1/publishers/head, /v1/health, and /metrics. The
producer proxy also exposes /metrics on its HTTP listener.
Horizontal scaling is deliberately bounded by client concurrency rather than a central coordinator. Least-active placement prevents random TCP connection collisions, but 16 persistent producer connections can use at most 16 Brokers at once. Lookup queries no longer scan every registry and steady-state polling transfers only small revision/readiness heads; nevertheless a consumer still needs one connection per actual Topic owner. This is a share-nothing cost, not an unbounded or zero-cost scaling claim.
Latency histograms cover publish and channel-WAL fsync, Topic-lock wait/hold
for publish and delivery reservation, publish and FIN/REQ group-commit
queueing, publish and channel ACK, payload reads, scrub/GC, proxy backend
calls, and discovery registry polling. Queue aggregates have fixed cardinality
by default; [metrics].detailed_queue_metrics enables bounded
per-topic/channel series up to max_detailed_series.
Delivery-budget bytes, waiters and cumulative waits are exported as bounded
aggregate gauges/counters.
[queue].publish_ack_mode defaults to "durable": successful publish commands
follow local segment fsync. "write_ack" returns after append while consumers
remain bounded by last_durable_position; "nsq_relaxed" also exposes the
appended tail immediately. Background sync runs at the first of
relaxed_sync_messages, relaxed_sync_bytes, or relaxed_sync_interval_ms.
rustqueue_publish_unsynced_messages, rustqueue_publish_unsynced_bytes, and
rustqueue_publish_sync_lag_seconds expose the crash-loss window, with bounded
per-Topic variants when detailed metrics are enabled. A sync failure stops new
writes. Keep both relaxed profiles separate from durable-PUB results.
RustQueue 0.8 keeps disk format v7. Format v7 is a clean break: a v6 or older
directory is refused and there is no in-place migration. Within v7, record tags
and existing fields are append-only.
Every binary declares its reader/writer feature range and protocol message/body
limits, and every PVC persists its active writer and minimum reader levels in
/data/COMPATIBILITY. The operator probes the target image and every running
broker before replacement; an incompatible target is blocked before a PVC is
touched.
Rollouts are durable status-driven operations. A replacement must be Ready
before another Broker is drained. They may be paused, optionally stop after one
canary for explicit revision approval, fail closed after a configured timeout,
and be retried with a new nonce. rollbackToImage uses the same preflight and
is rejected after the PVC reader fence makes that image unsafe. TLS Secret
resource-version changes are included in the Pod revision and use this same
rolling path.
Adding a record kind is a two-step rollout: first roll the capable binary to all
brokers, then explicitly increase storageFeatureLevel. The storage layer
rejects writes above the active feature level. Once activated, the PVC rollback
fence rejects a binary that cannot read the enabled level, so recovery is a
forward upgrade instead of an unsafe rollback. The operator still drains and
replaces one broker at a time while preserving retained PVC identity.
The on-disk layout is:
/data/FORMAT
/data/COMPATIBILITY
/data/broker.meta
/data/topics/<hex-topic>/manifest
/data/topics/<hex-topic>/segments/*.rqlog
/data/topics/<hex-topic>/segments/*.rqidx # sealed-segment recovery metadata
/data/topics/<hex-topic>/channels/<hex-channel>.checkpoint
/data/topics/<hex-topic>/channels/<hex-channel>.wal
/data/dlq-outbox/ # compact CRC-protected binary transfer intents
/data/audit/
Tail short writes are truncated during startup. Sealed segments reopen from an atomic recovery index by reading its CRC-protected fixed header and first/last queue entries; individual metadata entries carry their own CRC and are paged on demand. Full sidecar checksums run in background scrub, so startup is O(segment count), not O(message count). Missing or invalid indexes fall back to a full segment scan. Cold payload corruption is isolated by payload-read CRC or background scrub. Automatic scrub and normal GC start after a configurable 30-second quiet period so a large PVC can become Ready without immediately competing with maintenance I/O; active-tail corruption, invalid channel WAL and identity mismatch fail startup closed.
Crash-boundary integration tests stop worker processes with actual SIGKILL
after append, fsync-before-ACK, checkpoint file fsync-before-rename, and GC
delete boundaries. Reopened stores are checked against an acknowledged-message
ledger; ambiguous pre-ACK writes may duplicate, but acknowledged entries may
not be missing. Injection code is available only under the compile-time
crash-injection test feature and is absent from normal release behavior.
The fuzz smoke matrix covers protocol, compression, storage records, channel WAL/checkpoint replay, topic manifests, proxy HTTP metadata, and discovery registry responses.
./scripts/release-gate.sh is the repeatable production gate. It runs format,
build, unit, clippy, Helm, fuzz and official-client compatibility checks;
KODO_ACCEPTANCE=1 KODO_SOURCE_DIR=/path/to/kodo additionally runs the pinned,
unmodified Kodo parser/admin replay and the 100 MiB Gateway acceptance;
K8S_ACCEPTANCE=1 additionally requires the OrbStack context and runs base,
Console-management and multi-broker Kubernetes acceptances, including forced
Console restart after one owner of a multi-owner operation completes. The same
non-Kubernetes gate runs in GitHub Actions.
RustQueue 0.8 does not provide message replication, backups, exactly-once delivery, a global channel catalog, online data migration, cross-region replication, or Broker/PVC lifecycle controls in Console.