#[repr(u8)]pub enum Durability {
Durable = 1,
Buffered = 3,
}Expand description
Re-export of weir_core::Durability so the common producer path needs a
single crate import (weir_client::Durability).
Durability tier requested by the producer for a given record.
There are two tiers because there are two behaviours. Durable writes and
fdatasyncs the record — as part of one batch-boundary group fsync —
before the ACK, so an ack means the bytes are on stable storage and will
replay after a crash. Buffered trades that for latency: it acks after the
in-memory write, before any fsync, so a Buffered ack survives a process
crash but not power loss.
§Wire values
0x01 and 0x03 are fixed and must not change without a WIRE_VERSION
bump. 0x02 is retired and permanently reserved:
it was Batched, which fsynced once per batch while Sync fsynced once per
record. Both moved to the batch-boundary group fsync, at which point the two
tiers became the same thing. try_from still accepts 0x02 so producers
built against weir 1.x keep working; the encoder only ever emits 0x01.
Reusing 0x02 for a future tier would turn every 1.x client into a silent
mis-tier, so it stays reserved.
Variants§
Durable = 1
Durable before ACK: written and fdatasynced via the batch-boundary
group fsync before the ACK is sent.
§Evidence under power loss
5,311 simulated power-loss episodes over 24 hours, 42,814,591
acknowledged records, zero lost
(docs/benchmarks/chaos-phase2/2026-08-28-first-power-loss-measurement.md).
Zero I1 violations, zero I2 leaks, zero ledger conflicts, and the WAB
was completely empty at shutdown — nothing quarantined, nothing
unconfirmed, nothing dead-lettered.
The fault is a dm-flakey drop_writes device that discards writes
while reporting fsync success, applied after the daemon is killed so
the loss window contains only data that was dirty at the instant of
death — the definition of power loss.
Each episode carried an independent check that the fault actually
fired: a known block written before the fault, overwritten while it
was engaged, and read back after the remount. It was destroyed in
all 5,311. This matters more here than for
Buffered: a tier that loses nothing cannot use
“something went missing” as proof the fault was real, so without that
check the result would be unfalsifiable.
Zero violations across 5,311 episodes bounds the per-episode violation rate at roughly 0.06% with 95% confidence. It is not proof of correctness — no finite run is — but it is the strongest evidence behind weir’s central claim.
Buffered = 3
Memory write only. ACK is sent after the record enters the in-memory queue — survives a process crash, but not power loss.
§How much is at risk
Measured, not estimated: 706 real power-loss episodes on ext4 over
dm-flakey, 604,485,602 acknowledged records
(docs/benchmarks/chaos-phase2/2026-08-28-first-power-loss-measurement.md).
Loss is uniformly distributed between zero and one writeback interval — a power cut lands at a random point in a periodic writeback cycle, so what has not yet reached the platter is uniform between “just flushed” and “about to flush”. The ceiling, not the mean, is what characterises the tier.
On that hardware, at ~72,000 records/s, the ceiling was 1.76 seconds of acknowledged writes (126,782 records) with a median of 0.89 s.
What generalises is the shape: the bound is one writeback interval of whatever your throughput is, and it does not grow with uptime, queue depth, or how long the process has been running. The specific figures are a property of that filesystem, its mount options and that record rate — measure your own if the number matters to you.
§WAB compression raises this materially
The figures above are for the default uncompressed WAB. A 40-episode
controlled comparison — identical in every parameter except
wab_compression — found that enabling zstd makes the loss bimodal
rather than uniform, and roughly triples the ceiling: 449,876 records
(~5.7 s) against 124,897 (~1.8 s). The two modes separate cleanly, with
no episode landing between 127,711 and 307,263, and the lower mode
reproduces the uncompressed distribution almost exactly. Compression
packs far more records behind each unflushed byte, so a power cut takes
proportionally more of them.
Correctness is unaffected — zero I1 and I2 violations, and recovery quarantined torn compressed segments at the same rate as uncompressed ones. This is a change in how much the tier can lose, not in whether it keeps its contract.
Treat the multiplier as indicative, not as a number to plan with: the chaos load generator’s payload is far more compressible than real data, so the mechanism is demonstrated but its size on a realistic workload is unmeasured.
Durable is not exposed this way by construction: it
fdatasyncs at the batch boundary before acking. Note the run above
was single-tier (tier="U"), so it measured Buffered only and is not
itself evidence about Durable.
Implementations§
Source§impl Durability
impl Durability
Sourcepub const Sync: Durability = Self::Durable
👎Deprecated since 2.0.0: renamed to Durable
pub const Sync: Durability = Self::Durable
renamed to Durable
Former name of Durability::Durable.
Sourcepub const Batched: Durability = Self::Durable
👎Deprecated since 2.0.0: Batched was always identical to Sync; use Durable
pub const Batched: Durability = Self::Durable
Batched was always identical to Sync; use Durable
Former tier that was identical to Sync in behaviour. See the type docs.
Trait Implementations§
Source§impl Clone for Durability
impl Clone for Durability
Source§fn clone(&self) -> Durability
fn clone(&self) -> Durability
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreimpl Copy for Durability
Source§impl Debug for Durability
impl Debug for Durability
Source§impl Display for Durability
impl Display for Durability
impl Eq for Durability
Source§impl PartialEq for Durability
impl PartialEq for Durability
impl StructuralPartialEq for Durability
Source§impl TryFrom<u8> for Durability
impl TryFrom<u8> for Durability
Source§fn try_from(value: u8) -> Result<Durability, <Durability as TryFrom<u8>>::Error>
fn try_from(value: u8) -> Result<Durability, <Durability as TryFrom<u8>>::Error>
Permissive by design: 0x02 (retired Batched) decodes to
Durability::Durable so weir 1.x producers keep working.