WeaveDocs

Failure Modes

What happens to each Weave primitive under node crashes, peer disconnects, storage exhaustion, replication conflicts, and clock skew — and how to recover.

What this page covers

A per-primitive crash-and-recovery table. For each failure scenario, what state survives, what is lost, and what the operator (or the application) needs to do.

Generic recovery state machine

Every primitive in Weave follows the same general recovery shape: open performs replay or rebuild from a verifiable source-of-truth (Strand), and on-read hash checks catch corruption that survived replay.

Failure scenarios

ScenarioDefinition
Process crashThe Weave node is terminated without graceful shutdown
Disk fullLocal storage runs out of space mid-write
Peer disconnectA remote peer becomes unreachable mid-replication
Replication conflictTwo peers attempt incompatible writes that race
Clock skewLocal wall-clock disagrees with peer wall-clock
Data corruptionA storage block is unreadable or fails its hash check

Strand

ScenarioWhat survivesWhat is lostRecovery
Process crash mid-appendBlocks that returned Ok from appendThe in-flight blockOn open, the WAL is replayed; partial blocks are discarded
Disk fullExisting blocks remain readableThe block that triggered the errorFree disk, retry; no rollback needed
Peer disconnectLocal replica is intactThe blocks that had not yet replicatedReconnect; replication catches up using the Merkle bitfield
Replication conflictEach writer's strand is internally consistentNothing — writers do not share strands by designUse Nexus to combine multiple writers' strands
Clock skewStrand order is sequence-number based, not timeNothingNone required
Data corruptionAll blocks except the corrupted oneThe corrupted blockStrand provides per-block hashes; corrupted blocks fail their check on read and the caller can request re-replication from a peer

Strand recovery procedure

  1. 1
    Open the strand

    Strand::new(config) opens the storage and runs WAL replay.

  2. 2
    Truncate inconsistent tail

    Drop any incomplete sequence at the tail; the WAL guarantees the prefix is consistent.

  3. 3
    Mark missing blocks on read

    If a block fails its hash check, mark it as missing in the bitfield and request re-replication.

  4. 4
    Catch up via replication

    Replication fills gaps from any peer holding a valid copy. Once the bitfield is full, the strand is healthy.

Lens

ScenarioWhat survivesWhat is lostRecovery
Process crash mid-putSnapshots up to the last commitThe uncommitted batchReopen; in-flight batch is discarded
Disk fullLast committed snapshotThe pending writeFree disk, retry put
Peer disconnectLocal Lens is intactPending replication progressReconnect; Strand backing the Lens catches up
Replication conflictEach writer's Lens snapshots are independentn/a (single writer per Lens)Use Nexus for multi-writer KV
Data corruptionSnapshots not touching the corrupted blockReads through the affected key rangeRun the Lens' built-in repair (see Lens internals)

Locus

ScenarioWhat survivesWhat is lostRecovery
Process crash mid-locus_write_fileFiles committed before the crashThe in-flight file (its metadata block may or may not have committed)On open, journal replay completes or rolls back the file write
Disk fullAlready-written filesThe file that triggered the error; the metadata block may indicate it exists with zero bytesRe-write the file with the same path
Peer disconnectLocal mount intactPending sync to peersReconnect; Locus' sync engine resumes
Replication conflict (two writers touching the same path)Both writers' Strands persist their intentThe conflicting state is observable on both peersLocus exposes conflict markers; resolve at the application layer
Data corruptionFiles outside the affected block rangeAffected filesRe-fetch from a peer with a healthy replica

Nexus

ScenarioWhat survivesWhat is lostRecovery
Crash mid-materializationUpstream strands intactThe materialized view's tipRe-derive the view from the upstream strands
Upstream strand unavailableThe view continues to serve up to the unavailable pointNew data from that sourceReconnect to the upstream; resume from the saved offset
Conflicting upstream writesAll upstreams retainedn/a (Nexus is deterministic given its inputs)None required; the view re-derives consistently

Forum

ScenarioWhat survivesWhat is lostRecovery
Crash mid-takeThe Strand may or may not have the Take tombstoneIf the tombstone landed but live_tuples did not update, the next process restart restores it; if the tombstone did NOT land, the tuple appears live to other peersWorker handlers must be idempotent (see Take Tuple)
Disk fullTuples already writtenThe tuple being writtenRetry the write
Peer disconnectLocal Forum state intactReplication of Write / Take blocksReconnect; Strand catches up
Two peers both take a tuple before the tombstone replicatesBoth peers' Strands persist both Take recordsUseful semantics — the workflow must handle double-consumptionUse coordinator-claim pattern (see Distributed Coordination)
Warning

Forum take is not a distributed mutex. Two peers can both take a tuple before the tombstone replicates, and both take calls will return Some. Worker handlers must be idempotent or use the coordinator-claim pattern.

Gnosis

ScenarioWhat survivesWhat is lostRecovery
Crash before index updateTriple is in the StrandThe in-memory index entryOn restart, rebuild indices from the Strand (the SDK does this on open_gnosis)
Disk fullTriples already committedThe triple being addedRetry
Index corruptionThe StrandThe three DashMapsRebuild from the Strand

Basis

ScenarioWhat survivesWhat is lostRecovery
Crash before HNSW updateVector is in the StrandThe HNSW nodeOn restart, rebuild_index is needed (today this requires a remove of a present id; future: explicit compact())
Disk fullExisting vectorsThe pending insertRetry
Memory exhaustionThe vector StrandSearch abilityShard the corpus across multiple Basis instances on different processes
Index strand corruptionNo real impact todayNothing (the index strand is a stub)Delete and recreate

Weft

ScenarioWhat survivesWhat is lostRecovery
Crash mid-publishChunks that hit diskThe publish is incomplete; the manifest may not exist yetRe-publish; chunk ids are deterministic so duplicates are silently merged
Disk full during chunk storeAlready-stored chunksThe chunk that triggered the errorFree disk, re-publish
All peers serving a chunk disappearManifest still valid (proves the root)The chunk's bytes are unrecoverable from the networkRe-publish from a producer who has the source bytes
Chunk fails hash verification on fetchOther chunks intactThe malicious peer's response is discardedThe fetcher tries another peer from the availability list
Manifest signature invalidn/aThe manifest is rejectedRe-fetch from the producer or a trusted mirror

weave-dht

ScenarioWhat survivesWhat is lostRecovery
Node crashPeers' replicas of records the node hostedThis node's view of the DHTRe-join; bootstrap from peers; refetch needed records
Network partitionEach partition's recordsCross-partition reachabilityHeals automatically on reconnection; queries during the partition may fail
Eclipse (adversary controls all neighbors)Local dataUseful DHT lookupsMitigation: use α = 3 parallel queries (default), monitor for unusual neighbor turnover

weave-swarm

ScenarioWhat survivesWhat is lostRecovery
Connection deathOther connectionsThe disconnected peerThe swarm reattempts; pending events are queued up to a bound
Backpressure exhaustionRecent messagesOlder messages (dropped)Tune the priority queue limit

Recovery procedures by primitive

Quick reference

PrimitiveCold startAfter data corruptionAfter partition heal
StrandWAL replay (automatic)Mark missing block, request re-replicationReplication catches up via bitfield
LensOpen snapshot (automatic)Repair via Lens internalsUnderlying Strand replication
LocusJournal replay (automatic)Re-fetch affected filesSync engine resumes
NexusRe-derive view (automatic)Re-derive from upstreamsCatches up with upstreams
ForumRebuild live_tuples from StrandWorker idempotency handles itStrand replication
GnosisRebuild indices from StrandDiscard bad blocks, continueStrand replication
BasisEmpty HNSW (rebuild on first remove)Use vector_strand as source of truthStrand replication
WeftReopen store; chunks are content-addressedDiscard corrupted chunks; re-fetchRe-advertise availability

Monitoring failure modes

SignalMeaning
WeaveNode::peer_count is zeroNo replication will progress
Strand len() diverges between peersReplication backlog
Repeated WeftError::InvalidHash from a peerPossible adversary or corrupted mirror; consider scoring
Process memory growing unbounded with Basis activeVector corpus exceeded sizing; shard
Lens snapshot count growing unboundedCompaction backlog; trigger manual compact
Locus journal growing unboundedJournal trim is not running

Where to next

  • Threat Model — adversarial scenarios in addition to these crash scenarios
  • Architecture — where each failure is contained in the stack