What happens when something breaks, and what WombatKV does about it automatically vs what the operator has to do manually.
Companion to: consistency.md (what's guaranteed
when nothing breaks).
WombatKV's DST harness (crates/wombatkv-dst/) exercises 16
deterministic failure classes against the substrate. The same classes
form this document's "what we tested" baseline.
| Class | Trigger | WombatKV behavior |
|---|---|---|
| TransientS3Failure | S3 GET returns 503 / throttle | Retry per S3ErrorKind::is_retryable; surface clean error after retry budget |
| CorruptBlockBytes | S3 returns bytes with bad BLAKE3 chain hash | Chain-hash verification rejects; warm-restore does NOT install corrupt KV |
| PartialChainSave | Crash between block PUT and chain-head PUT | Next bootstrap ignores orphan blocks; LOOKUP returns the last fully-published chain tip |
| ConcurrentSameKeySave | Two PUTs of same content-hash | Both succeed; content-addressing makes them idempotent |
| DaemonRestartMidLookup | Daemon restart during embedded-client lookup | Client retries against new daemon or surfaces DaemonUnavailable; no orphan-state panic |
| SidecarDriftAfterChain | Chain head publishes but sidecar PUT fails | Chain is visible without compressor frontier; engine recomputes frontier from chain bytes |
| FoyerEvictionMidGet | LRU evicts a block mid-lease | Lease's Arc keeps bytes alive past eviction (use-after-free safe) |
| TransportConnectionDropMidRPC | TCP/HTTP daemon connection dropped mid-RPC | Client reconnects cleanly or surfaces DaemonUnavailable |
| TransportPartialReadOnHeader | Daemon socket returns fewer bytes than requested | Accumulator-buffer loop re-reads; no off-by-one decode |
| TransportSlowWrite | Per-byte stalls on daemon socket write | Client-side timeout discipline; no head-of-line indefinite block |
| WireEnvelopeCorruption | Daemon ships bad-magic/version/CRC/oversized/truncated envelope | Client rejects cleanly, no state corruption |
| OldSidecarV3InBucket | Bucket has pre-RFC-0018 sidecar from prior alpha | Loader rejects with clear operator hint ("wipe sidecar bucket and re-save") |
| OldBlockV1InBucket | Bucket has pre-RFC-0018 block v1 from prior alpha | Same, clear hint, never load corrupt KV |
| SlateDbWriteFailure | L1 metadata put fails | Daemon surfaces error or falls back to S3-truth; never silently drops |
| SlateDbManifestCorruption | SlateDB on-disk manifest corrupt at startup | Daemon fails loudly with operator-actionable hint; never starts with empty index against populated bucket |
| MultiEnginePrefixConflict | Two engines claim same SHM prefix | Daemon serializes or refuses cleanly |
- Validates the SHM segment-name budget (macOS POSIX SHM 31-char cap). Fail-loud if a prefix would overflow.
- Builds
S3ObjectStoreConfigfrom env (WMBT_KV_BUCKETrequired; no AWS_* fallback by design). - Opens (or creates) SlateDB at
WMBT_KV_SLATEDB_PATH. Failure to open is logged + daemon continues with empty L1 (next read re-hydrates from S3-truth). - Bootstraps L0 radix from L1 SlateDB (~25ms for 27 blocks). If L1 is empty, lazy-bootstrap from S3 on first lookup.
- Spawns the optional LRU eviction worker
(
WMBT_KV_NAMESPACE_MAX_BYTES).
- Wire envelope: validates magic + version + CRC32C in
decode_envelope. Mismatch → reject + close connection (drop-on-bad-frame semantics). - Block envelope (KVB1 v2): validates magic + version + body CRC32C
- length. Mismatch → reject, do not install KV.
- Sidecar envelope (RTT1 v4): same: CRC + version + magic checked.
- Chain hash: BLAKE3-20 over
(parent, content_hash)validates restore chain integrity.
- SIGTERM/SIGINT/SIGHUP installs flips the shutdown flag.
- Per-prefix worker loops poll the flag between request batches.
- In-flight requests are dispatched + ACK'd before exit.
- Async-PUT drain budget:
SHUTDOWN_DRAIN_TIMEOUT(10s default; seecrates/wombatkv-daemon/src/constants.rs). - SHM segments are
shm_unlink'd on the way out. - SlateDB index closed.
- Main joins on each worker.
If the drain exceeds 10s, the daemon logs "drain timeout" and exits anyway; the operator can SIGKILL if a worker is permanently stuck.
Wipe the bucket + restart. The alpha breaking-window policy doesn't maintain back-compat readers. Daemon will refuse to start against a mixed-version bucket and print:
ds4_session_install_raw_tail: unsupported sidecar version (alpha breaking-window: v3 envelopes don't load under v4; wipe sidecar bucket and re-save)
Action:
aws s3 rm s3://$WMBT_KV_BUCKET --recursive # or mc rm --recursive
# Restart daemon; it will re-populate as engines write new state.Daemon logs the corruption + exits with non-zero status. Action:
# Move corrupt manifest aside for inspection
mv $WMBT_KV_SLATEDB_PATH ${WMBT_KV_SLATEDB_PATH}.corrupt-$(date +%s)
# Restart daemon; it will re-hydrate L1 from S3 (slower first
# bootstrap, ~minutes for large buckets, sub-second thereafter).Daemon retries per S3ObjectStoreConfig retry policy
(WMBT_KV_S3_GET_RETRIES). After retry budget, surfaces error to
caller. The engine sees the error and falls back to its own cold
prefill. WombatKV does not silently mask S3 unavailability as
"warm restore worked."
Next daemon startup ignores orphan blocks. The engine sees a shorter chain than it last published; it must re-extend the chain from its current tip. Idempotent retries via content-addressing make this safe.
Daemon refuses the second attach. Operator must either:
- Use a different prefix per engine (
--prefix engineA --prefix engineB), or - Let the first engine finish + detach, then the second attaches.
This is the "cross-host RYW is eventual" gap documented in
CONSISTENCY.md. Today the fix is to restart host B's daemon
(re-bootstraps L1 from S3). Post-RFC 0019 (HELLO handshake), this
will be automatic.
-
Daemon stderr
wombatkv_shm_daemonJSON events:event:"starting", daemon readyevent:"ready_remote_only": TCP/HTTP listeners readyevent:"shard_listening", per-shard compio bind succeededevent:"worker_exit_shutdown", clean shutdownevent:"shard_error", listener crashed; daemon may have lost a transport
-
wombatkv-daemon[slatedb]: hydrated N blocks: L1 bootstrap done -
wombatkv-daemon[lru]: starting eviction worker, eviction enabled -
wombatkv-daemon: failed to open store: InvalidConfig(...), env config wrong; daemon exits
assertion failed: DST coverage should catch these before productionpanic:catch_unwindboundaries in cabi prevent C-side propagation, but daemon panics indicate a logic bug; please capture a backtrace + file an issue
S3 is the source of truth for WombatKV. The recommended production posture:
- Bucket versioning + MFA-delete: protects against accidental rm. AWS S3 + most S3-compatible stores support this natively.
- Cross-region replication: for tier-1 availability beyond one region. Configure via S3 Replication Rules; WombatKV does not orchestrate this.
- Periodic layout audit:
wombatkv-wal-layout-auditwalks the bucket and verifies key-shape invariants. Run weekly in prod as a tripwire for drift. - Periodic restore test: point a staging engine at a read-only mirror of the prod bucket once a month. If warm restore works, your DR strategy is real.
WombatKV does NOT provide:
- Backup orchestration (use AWS Backup / s3-batch-operations)
- Point-in-time recovery beyond S3 versioning
- Snapshot consistency across multiple buckets
book/src/concepts/consistency.md, formal consistency modelbook/src/operations/dst.md: DST primitives + failure classes- RFC 0018, wire envelope discipline (CRC32C / version / magic)
- RFC 0019 (sketched, not implemented), cross-host HELLO handshake
crates/wombatkv-dst/src/fault.rs, full fault enum + scheduler