Skip to content
SeeItFlow
System Design · 10 chapters · 31 failure scenarios · ~46 min read

Distributed Logging Failures: Backlogs, Duplicates, Leaks & Recovery

Thirty-one named failures, each walked through as trigger, timeline, symptom, detection, mitigation, recovery, correctness result, prevention, and the trade-off its fix accepts. The numbers throughout are illustrative planning figures for this fictional case study, never measurements of a real system.

Mangesh MandlikLast updated: How lessons are made
F01-F03

Agent and Host-Level Failures

The failures closest to the source are the ones application isolation was built to contain — none of these should ever become visible to the request path.

F01 — Agent memory buffer overflow under a backend outage

Trigger: the collector becomes unreachable for several minutes. Timeline: t+0s the agent's in-memory buffer starts filling as normal shipping stalls; by t+45s, at a per-instance rate of 5 events/s, the buffer reaches its configured cap. Symptom: without a spool, the agent must either drop new events or block the application — dropping is the only policy that doesn't violate application isolation.

Detection & mitigation

A drop counter metric on the agent spikes from zero; self-observability telemetry alerts on any sustained non-zero drop rate. Mitigation is architectural, not reactive: a bounded disk spool (Chapter 06 of Complete Design) absorbs an outage of this length without resorting to drops at all.

Recovery & correctness result

Once the collector recovers, the agent resumes normal shipping immediately — no replay mechanism exists for already-dropped events, because they were never durably recorded anywhere. The correctness result is an honestly-measured, bounded loss window, not a silent one: the drop counter is the permanent record of how much was lost and when.

F02 — Disk spool exhausted during a prolonged outage

Trigger: the same outage as F01 persists well beyond the spool's capacity — say, two hours against a spool sized for 30 minutes. Timeline: the spool fills at t+30min; from that point forward, newly generated events are dropped even though the spool itself is intact and healthy. Symptom: a gap in the record for the overflow period, bounded precisely by when the spool filled and when the outage ended.

Detection & mitigation

The spool's own fill-percentage metric crosses a warning threshold well before it's full, giving operators lead time; mitigation at that point is operational (escalate the outage) rather than architectural — no spool can be made infinite.

Recovery & correctness result

On recovery, the agent drains the spool in FIFO order before resuming live traffic, so backlog records are shipped in their original relative order. The correctness result: everything that fit in the spool survives with its original event_time intact; everything generated after the spool filled is permanently absent, not delayed.

F03-F05

File Rotation and Transport Failures

F03 — File rotation causes duplicated or skipped log lines

Trigger: logrotate renames checkout.log to checkout.log.1 at the same moment the agent is mid-read. Timeline: if the agent's tailer tracked only the file path, it reopens the new, empty checkout.log at offset zero — either re-reading nothing (a gap, if the old inode's remaining unread bytes are lost) or, worse, reading the same bytes twice if checkpoint logic double-counts across the rename. Symptom: a burst of duplicate or missing lines exactly at the rotation boundary, repeatable on every rotation if unfixed.

Detection & mitigation

A spike in duplicate record IDs (if idempotent keys exist downstream) or a detectable gap in a monotonic per-instance sequence number surfaces this. Mitigation: track file identity (inode plus a fingerprint) and an acknowledged byte offset, never checkpointing inside a partial line, as described in Architecture Deep Dive Chapter 03 — this correctly distinguishes a renamed file (same inode, new name) from a copytruncate rotation (same name, truncated content, new effective identity).

Recovery & correctness result

With file-identity tracking, the agent finishes the old file before switching to the new one, so a rename-based rotation produces no gap. After a crash the agent re-reads from its last acknowledged offset, so a few lines can be delivered twice, and downstream deduplication absorbs them. The correctness result is no silent gap and no unbounded duplication: at-least-once delivery, in order within the file. Copytruncate remains lossy for lines written between the last read and the truncation, which is a reason to prefer rename-based rotation.

F04 — Collector mTLS certificate expiry blocks an entire agent fleet

Trigger: a collector-tier TLS certificate expires without rotation. Timeline: at the expiry instant, every agent's next connection attempt fails authentication simultaneously — not a gradual degradation, a fleet-wide cliff. Symptom: agent-side connection error rates jump from near-zero to 100% across every instance in the fleet within seconds of each other.

Detection & mitigation

Certificate expiry should be monitored proactively (days of lead time), making detection at the failure instant itself a sign the proactive check already failed. Immediate mitigation: agents fall back to local disk spooling exactly as in an ordinary collector outage — this failure mode is, from the agent's perspective, indistinguishable from F01/F02 and handled by the same mechanism.

Recovery & correctness result

Once the certificate is rotated and the collector tier restarts or reloads it, agents reconnect and drain their spools. Correctness result depends entirely on whether the outage fit inside spool capacity — same bound as F02.

F05 — A malformed record stalls an entire partition because no dead-letter path exists

Trigger: a logging bug in one service emits a record that violates the expected schema — an oversized payload, a field with the wrong type. Timeline: a naive indexer without a quarantine path throws an unhandled error trying to process it and either crashes outright or retries the same malformed record indefinitely at the same partition offset, both of which block every healthy record queued behind it on that partition.

Detection & mitigation

Consumer lag on the affected partition climbs sharply while every other partition stays healthy — a distinctive single-partition-stall signature. Mitigation: route records that fail schema validation to a dead-letter queue instead of raising an unhandled error, so the indexer can skip past the poison record and keep consuming.

Recovery & correctness result

Once a DLQ path exists, recovery is automatic — the malformed record is quarantined and alerted on, while the rest of the partition continues processing without interruption. Correctness result: a quarantined record is not lost, just deferred for manual inspection and potential reprocessing after a fix; it is categorically different from a record dropped due to a capacity policy.

F06-F08

Durability Boundary Failures

F06 — Promotion-day burst overwhelms a bus-less, direct-to-index pipeline

Trigger: a deployment that skipped the durable bus (Trade-offs & Decisions, Decision 5) hits a 12× traffic burst — an illustrative jump from 50,000 to 600,000 events/s. Timeline: within seconds, indexing throughput, sized for the average, becomes the ceiling for the entire ingest path; collector-side buffers fill, then agent spools fill, in that order, working backward from the bottleneck.

Detection & mitigation

Collector queue depth and agent spool fill rates spike together, fleet-wide, correlated with the traffic event. There is no fast mitigation available once this starts — the architectural fix (a durable bus decoupling ingest from indexing) has to exist before the burst, not be added during it.

Recovery & correctness result

Recovery happens only as fast as indexing throughput allows the backlog across every buffering layer to drain, and any layer whose buffer filled before recovery began has already dropped data by this point. Correctness result: a bus-less design under-provisioned for its real burst ratio has no bounded, calculable loss — loss is whatever each layer's buffer policy happened to produce.

F07 — Ack issued before durable broker commit; collector crash loses “durable” data

Trigger: a collector configured under ack option A (Trade-offs & Decisions, Decision 4) acknowledges a publish the instant the record is queued in its own local memory, before the broker has committed anything. Timeline: at t+0ms the collector acks; at t+8ms, before forwarding completes, the collector process crashes. Symptom: the agent believes the record is durably logged (it received a success ack) — but it was never replicated anywhere.

Detection & mitigation

This gap is invisible from the agent's perspective by construction — the ack lied about the durability stage reached. Detection, if it happens at all, comes later: an investigation expecting to find a specific record in the durable store finds nothing, despite an agent-side log showing a successful ack. Mitigation requires switching to ack option B (acknowledge only after the broker commit) before the fact; there is no after-the-fact fix for records already lost this way.

Recovery & correctness result

No recovery is possible for the specific records lost in the crash window — they were never durable. The correctness result under option B instead: an ack is only issued after a replicated broker commit, so a collector crash after ack can never lose the record, by construction, because durability was already established before the ack was sent.

F08 — Redaction rule fails open; a secret is persisted durably

Trigger: a logging statement includes a credential in an unusual format the redaction pattern library doesn't recognize. Timeline: the collector's redaction stage runs, finds no pattern match, and — under a fail-open policy — forwards the record unredacted; it becomes durable in the broker and is later indexed, with the secret intact and now searchable by anyone with query access to that tenant's logs.

Detection & mitigation

Detection typically comes from a secret-scanning sweep over indexed content, or from the credential itself being observed in unexpected use. Mitigation at that point is incident response: rotate the exposed credential immediately, and purge or redact the indexed copies after the fact — which does not undo the exposure window that already occurred.

Recovery & correctness result

There is no way to make an already-exposed secret retroactively unexposed; recovery is limited to credential rotation and purging the stored copy. The correctness result under a fail-closed redaction policy instead: a record the redaction engine cannot confirm is safe is held back or quarantined rather than forwarded, trading a false positive (a safe record briefly delayed) for eliminating this false negative entirely.

F09-F10

Partitioning and Consumer-Group Failures

F09 — Naive service-name partition key creates one saturated hot partition

Trigger: checkout-api, the highest-volume service in the fleet, is keyed purely by service name. Timeline: during the promotion burst, checkout-api's share of traffic routes entirely to one partition, which climbs to 95% utilization while sibling partitions for payment-service and inventory-service sit at 15-20%. Symptom: checkout-api's own logs specifically fall behind on indexing, while every other service's logs stay current — a service-specific, not fleet-wide, freshness problem.

Detection & mitigation

Per-partition consumer lag metrics show one partition's lag diverging sharply from its peers. Mitigation without a repartition: temporarily throttle or sample checkout-api's lowest-priority log levels to relieve the hot partition, accepting reduced fidelity for that service until a real fix ships.

Recovery & correctness result

Full recovery requires repartitioning with a composite key (service + instance or shard), which requires a migration — existing data in the old partition layout doesn't retroactively redistribute. Correctness result: no data is lost, only delayed, as long as the single hot partition's retention window outlasts the delay — the same backlog math as F16 applies to a single partition instead of the whole bus.

F10 — Consumer-group rebalance storm during indexer autoscaling

Trigger: an autoscaler adds five new indexer instances at once in response to rising lag. Timeline: each new consumer joining the group triggers a rebalance, redistributing partition assignments; if five instances join within the rebalance timeout window, the group can rebalance repeatedly before settling, each cycle pausing all consumption briefly. Symptom: indexing lag, already rising, temporarily gets worse immediately after the 'fix' is applied, before improving.

Detection & mitigation

A spike in rebalance events correlated with the scale-up event is visible in consumer-group metrics. Mitigation: stagger new consumer joins rather than adding them all simultaneously, and use a cooperative (incremental) rebalancing strategy where supported, which avoids a full stop-the-world reassignment on every membership change.

Recovery & correctness result

The group settles once joins stop and a stable rebalance completes; consumption resumes from each partition's last committed offset, so no records are lost — only momentarily delayed. Correctness result: a temporary, bounded freshness dip, never a correctness violation, as long as offsets are managed correctly through the rebalance.

F11-F13

Clock Skew and Index-Commit Crash Failures

F11 — Clock skew across hosts produces a misleading event ordering

Trigger: one host's system clock drifts four seconds behind its peers, undetected. Timeline: two events that happen in the same real second on two different hosts are logged with event_time values four seconds apart. The slow host's event carries a timestamp four seconds earlier than it should, so sorted by event_time it appears to have happened before an event that actually preceded it. A fast clock does the reverse and pushes its events four seconds later than they really were.

Detection & mitigation

Detection: monitor time-sync offset on every host, because by the time an investigator notices an ordering inconsistency in search results the skew has probably existed for a while. A second signal is built into the data: compare event_time with observed_time per source. A source whose gap is consistently negative (the event is stamped after the collector saw it) has a fast clock; a source whose gap is large and constant has either a slow clock or a long hold time in its agent, and the agent's monotonic hold-time metric separates the two. Mitigation in the query layer: always keep observed_time, which the platform stamps on a monitored clock, as an alternative sort key so an investigator can cross-check.

Recovery & correctness result

No retroactive correction is applied to already-recorded event_time values, because rewriting them would fabricate data. If the offset of a source is known from monitoring, a query tool may display a corrected time as a clearly labeled derived value beside the original. Correctness result: this design never claims a single trustworthy global ordering across hosts. It offers two honestly labeled orderings, event_time (the producer's claim) and observed_time (the platform's receipt time, which also includes buffering delay), and lets the investigator choose which fits the question (invariant I9). For true causal order across services, use trace and span relationships rather than wall-clock timestamps.

F12 — Index-then-commit crash produces a duplicate record (recoverable)

Trigger: an indexer writes a record to the hot store, then crashes before committing the corresponding bus offset. Timeline: t+0ms the index write succeeds; t+4ms, before the offset commit, the process crashes; on restart, the indexer resumes from the last committed offset, which is still before this record, and re-reads and re-indexes it. Symptom: the same record appears to have been indexed twice from the bus's perspective, though the store itself holds one document.

Detection & mitigation

If the index write is a true upsert keyed by a stable record ID, this is invisible to a searcher — the second write overwrites the first with identical content. Detection, where it matters, is via indexer-side duplicate-write counters, useful for understanding crash frequency even when the user-facing effect is zero.

Recovery & correctness result

No explicit recovery action is needed — replay self-heals via the idempotent upsert. Correctness result: exactly-once visible search behavior is achieved despite at-least-once delivery, specifically because the index write is idempotent, not because the pipeline achieves true exactly-once delivery (invariant I3).

F13 — Commit-then-index crash produces a silent, unrecoverable skip

Trigger: the reverse checkpoint ordering — an indexer commits the bus offset first, then attempts the index write. Timeline: t+0ms the offset commit succeeds; t+4ms, before the index write, the process crashes. On restart, the indexer resumes from the offset it already committed, which is now past this record. Symptom: the record is never indexed and never will be through normal consumption — it simply doesn't exist in search.

Detection & mitigation

This is detectable only through an independent count-reconciliation process — comparing total records produced to total records indexed over a time window — never through the indexer's own consumption metrics, which show a perfectly healthy, monotonically advancing offset with no visible error. Mitigation requires exactly this kind of independent audit, run continuously, because the failure produces no local symptom.

Recovery & correctness result

Recovery is only possible if the raw record is still available from an upstream retention window (the broker's own retention, if long enough, or an independent raw-archive copy) and a reconciliation process specifically re-reads and re-indexes the gap. Correctness result: this ordering should not be the default (Trade-offs & Decisions, Decision 7) precisely because its failure mode is silent and requires external detection rather than self-healing.

F14-F15

Storage Cardinality and Query Overload

F14 — High-cardinality label explosion from request_id used as a label

Trigger: an engineer adds request_id as a stream label in a label-chunk-style storage tier, intending to make correlation easier. Timeline: within hours, the number of distinct streams grows from dozens (service × region × level) to millions, each stream holding only one or a few events. Symptom: index overhead and chunk fragmentation climb sharply; ingestion into this tier slows, and storage cost for metadata alone grows disproportionately to actual log volume.

Detection & mitigation

A stream-count metric (or equivalent cardinality gauge) spikes abruptly, correlated with the label-schema change. Immediate mitigation: revert the label configuration for new writes; already-created high-cardinality streams typically need to age out via normal retention rather than being surgically removed.

Recovery & correctness result

No data is lost — this is a cost and performance failure, not a durability one. Recovery is simply reverting the schema and waiting for the bad streams to retire via TTL. Correctness result: the fix is architectural (move request_id to a structured field or metadata, never a label) rather than a configuration tuning knob.

F15 — Unbounded free-text query storm starves query workers

Trigger: an operator runs a wildcard regex search with no time-range filter, effectively requesting a scan of the platform's entire retained corpus. Timeline: within seconds, every available query worker is consumed by this one request; other operators' searches queue behind it or time out.

Detection & mitigation

A single query's resource consumption (bytes scanned, worker-seconds) spikes far above typical query cost, visible in query-layer telemetry. Mitigation: an enforced per-query scan-byte budget and concurrency cap, applied at plan time before any shard is dispatched (Architecture Deep Dive Chapter 07), rejects or truncates the offending query before it can monopolize capacity.

Recovery & correctness result

Once the budget-enforcing query planner is in place, recovery is automatic — the storm query is rejected or bounded, and other queries proceed normally without ever being affected. Correctness result: separating query and ingest resource pools (Trade-offs & Decisions, Decision 12) additionally guarantees this failure, even unmitigated, could never degrade the write path.

F16-F18

Indexing Outages and Backlog Capacity

F16 — A 30-minute indexer outage creates a 72 GB ingest backlog

Swipe to pan
Backlog Only Drains if Consumers Outrun Arrivals72 GB backlog, 80 MB/s consume vs. 40 MB/s ingress → net 40 MB/s → ~30 minoutage startsrecovery begins (80 MB/s)drainedtime →
Backlog accumulates at the full ingress rate for the entire outage duration

Trigger: the indexing tier goes fully offline for 30 minutes — a bad deploy, a dependency outage. Timeline: because ingest and indexing are decoupled by the durable bus, applications keep writing successfully the entire time; the bus's oldest-unprocessed-message age climbs steadily and linearly for the full 30 minutes. Symptom: search freshness degrades continuously and visibly, while write-side error rates stay at zero.

Detection & mitigation

The oldest-unprocessed-message-age metric, watched through the self-observability path (invariant I10), crosses an alert threshold well before the outage resolves. At an illustrative 40 MB/s average ingress, 30 minutes (1,800 seconds) of accumulation is 40 × 1,800 = 72,000 MB ≈ 72 GB of raw, unindexed backlog by the time indexing resumes.

Recovery & correctness result

Recovery requires indexing throughput to exceed ingress for a sustained period (see F17's drain math). Correctness result: this is a freshness incident, not a data-loss incident, as long as the bus's retention window exceeds the outage duration plus the subsequent drain time — the backlog math only stays survivable if that condition holds.

F17 — Backlog drain under-provisioned; backlog grows instead of shrinking

Trigger: indexing resumes after F16's outage, but capacity is only scaled to match the 40 MB/s average ingress rate rather than exceed it. Timeline: because new events keep arriving throughout the drain at the same 40 MB/s, matching that rate exactly means the 72 GB backlog never shrinks — the backlog simply stops growing.

Detection & mitigation

The oldest-unprocessed-message age plateaus instead of declining — a clearly distinguishable signal from a successful drain, where it should be dropping. Mitigation: scale indexing capacity meaningfully above ingress — this design's reference target is roughly 80 MB/s raw-equivalent against the ongoing 40 MB/s ingress.

Recovery & correctness result

At a net drain rate of 80 − 40 = 40 MB/s, the 72 GB backlog clears in 72,000 MB ÷ 40 MB/s = 1,800 seconds, or 30 minutes. Correctness result: the general rule — consumer throughput must strictly exceed sustained arrival rate, not merely match it — is the only condition under which any backlog actually drains rather than merely stabilizing.

F18 — Broker disk approaches its retention bound during a sustained backlog

Trigger: an outage or burst runs long enough, or is large enough, that the backlog threatens to exceed the broker's own configured retention/disk bound, before indexing has caught up. Timeline: broker disk-headroom metrics climb past a warning threshold, then a critical threshold, independent of whether indexing has started recovering yet.

Detection & mitigation

Broker-disk-headroom alerts fire ahead of actual exhaustion. Mitigation has two parts: upstream agents fall back further onto their own local spools (buying additional time for sources that can tolerate it), and the platform applies an explicit, documented load-shedding policy by log class — throttling or dropping best-effort debug traffic first, while protecting reserved retention headroom for error and audit-class events.

Recovery & correctness result

Recovery proceeds as indexing drains the backlog (per F17's math), relieving broker disk pressure from the read side while load-shedding relieves it from the write side. Correctness result: a class-aware, explicit shedding policy preserves the most important data under genuine capacity exhaustion; the alternative — a silent, undifferentiated overwrite of the oldest records regardless of class — is never acceptable, because it turns a capacity problem into an unaudited, undisclosed data-loss event.

F19-F21

Archive, Security, and Tenant-Isolation Failures

F19 — Archive rehydration query latency underestimated by an on-call engineer

Trigger: an on-call engineer, mid-incident, runs a query against 60-day-old archived data expecting hot-tier response times. Timeline: the query takes several minutes rather than the sub-second response the engineer is used to from the hot tier, because cold-archive storage is optimized for cost, not interactive latency.

Detection & mitigation

This isn't a system failure in the technical sense — it's an expectation mismatch. Mitigation is interface-level: the query UI should clearly label which tier a query will hit and give an honest latency estimate before the engineer commits to waiting, rather than presenting a spinner indistinguishable from a hot-tier query.

Recovery & correctness result

No recovery action is needed beyond the query eventually completing. Correctness result: the honest trade-off (Trade-offs & Decisions, Decision 9) stands — cheap long-term retention and fast interactive search are not simultaneously achievable, and communicating that trade-off clearly at query time is itself part of the design.

F20 — A regional network partition isolates a collector tier from its region's broker

Trigger: a network partition within one region separates the collector tier from the local broker cluster, while both remain individually healthy. Timeline: collectors in the affected region begin queuing or rejecting publishes immediately; agents in that region fall back to local spooling, same as any collector-side outage from the agent's perspective.

Detection & mitigation

Collector-to-broker publish error rates spike in the affected region specifically, while the broker's own health checks (reachable from elsewhere) show it as healthy — a signature that distinguishes a partition from a genuine broker failure. Mitigation is the same spool-and-wait behavior as other collector-side outages; there is no safe cross-region reroute for this traffic without violating residency (F23 covers that constraint directly).

Recovery & correctness result

Once the partition heals, collectors resume publishing and agents drain their spools. Correctness result: bounded by spool capacity exactly as in F01/F02 — a network partition is, from the data-loss-math perspective, just another outage duration to measure against spool and bus retention.

F21 — A caller attempts to query another tenant's logs by spoofing a tenant_id header

Trigger: a client authenticated as tenant-beta sends a search request with a tenant_id field set to tenant-acme — either an integration bug or a deliberate probe. Timeline: the request arrives at the query planner, which resolves authorized scope from the verified credential (tenant-beta), not from the request body.

Detection & mitigation

The planner's scope-resolution step detects the mismatch between the claimed and credential-derived tenant immediately, before any shard is queried. Mitigation is structural, not reactive: this check exists on every single query, unconditionally (Architecture Deep Dive Chapter 08) — there's no separate detection step because prevention is built into the request path itself.

Recovery & correctness result

The request is rejected with a 403 before touching any tenant's data; no recovery action is needed because no data was exposed. Correctness result: invariant I4 holds by construction — a caller-supplied tenant_id is a hint at best, never an authorization boundary, and this scenario is exactly what that invariant exists to prevent.

F22-F24

Regional, Governance, and Retention Failures

F22 — A tier-migration job silently stalls, leaving data in an expensive tier past its window

Trigger: the background job responsible for aging data from hot to warm storage encounters an unhandled error and stops running, without crashing loudly. Timeline: data that should have moved to warm storage at the 7-day mark keeps accumulating in the hot tier indefinitely; the first visible symptom is often a cost anomaly, not a functional one.

Detection & mitigation

A per-tier data-age distribution metric (what fraction of hot-tier data is older than its intended window) catches this directly; a pure cost-dashboard anomaly is a slower, indirect signal of the same problem. Mitigation: restart the migration job and verify it processes the accumulated backlog of overdue data.

Recovery & correctness result

No data is lost — this is purely a cost and tiering-policy violation, not a durability or search-correctness one. Recovery completes once the migration job catches up on the backlog of overdue records.

F23 — A residency-bound region fails; cross-region failover is not permitted

Swipe to pan
Independent Regional Pipelines, Governed FederationNo silent cross-region replication of raw log dataRegion: us-eastfull local pipelineRegion: eu-westresidency-bound pipelineFederated Querypolicy-governed, audited
Bounded local buffering and reduced availability, not a silent cross-border reroute

Trigger: eu-west suffers a regional outage affecting its logging pipeline. Timeline: for a tenant whose compliance agreement requires EU data residency, routing their in-flight logs to us-east during the outage is not available as an option — doing so would move their data across a boundary their policy forbids, regardless of the availability benefit.

Detection & mitigation

The regional outage itself is detected through standard regional health monitoring. Mitigation is the agent-level spool fallback already covered in F01/F02/F04/F20 — the only difference here is that there is no cross-region escape valve available for this specific tenant class, by design.

Recovery & correctness result

If the outage outlasts the local spool's capacity, the tenant experiences a genuine, disclosed reduction in log availability — some events are delayed or lost — rather than a silent residency violation. Correctness result: the honest trade-off from Trade-offs & Decisions, Decision 15, accepted explicitly rather than discovered during the incident.

Trigger: legal counsel identifies a record as relevant to an investigation and requests a hold, but the request arrives after that record's normal retention TTL has already triggered deletion. Timeline: by the time the hold request reaches the logging platform, the underlying record — if it lived only in the tier that already expired it — may no longer exist to be held.

Detection & mitigation

This is detected the moment the hold request is processed against a record ID that no longer resolves. Mitigation depends entirely on whether a longer-retention or immutable archive copy still exists elsewhere (e.g. a compliance-grade cold archive with its own, longer default retention) — if one does, the hold can still be honored there even though the primary tier already deleted its copy.

Recovery & correctness result

If no surviving copy exists anywhere, this is an unrecoverable gap, and it must be reported as such to legal counsel honestly rather than hidden. Correctness result: this scenario is exactly why a legal hold should be checked proactively against records approaching their retention boundary, not only applied reactively after a request arrives — invariant I8 is about accountability for this exact race, not a guarantee that it can never occur.

F25-F31

Seven Additional Required Scenarios

These seven close specific gaps against the module's own failure-bank specification — distinct trigger points not covered by the twenty-four scenarios above.

F25 — Application process OOM with unflushed in-memory logs

Trigger: an application process accumulates an in-memory log buffer (batched for write efficiency) and is OOM-killed by the kernel before that buffer flushes to disk or stdout. Timeline: the batch window is typically hundreds of milliseconds to a few seconds; any log lines written during that window, including the ones that would explain *why* memory grew unbounded, die with the process. Symptom: an incident investigation finds a gap in the log stream in the seconds immediately preceding the crash — precisely when the most diagnostically useful lines would have been written.

Detection & mitigation

This is invisible until investigated — there's no error, just an absence. Detection comes from comparing expected log volume (from a healthy baseline) against actual volume in the window before a crash, or noticing a suspiciously clean gap right before a crash-loop restart. Mitigation is structural, not reactive: minimize the in-memory batching window for a process already showing memory pressure, or write log lines synchronously to a local buffer the OOM killer can't take with it (a sidecar's buffer survives the app process dying, which is the whole reason the agent model exists).

Recovery & correctness result

There is no recovery for the specific lines lost in an unflushed in-process buffer — they never left the process's memory. The correctness result: any log line is only as durable as the earliest point it leaves the originating process; this is exactly why the sidecar/agent-based pipeline (Complete Design, Chapter 03) exists instead of trusting the application process to flush its own logs reliably under memory pressure.

F26 — Container killed before the stdout collector reads its last lines

Trigger: a Kubernetes pod eviction or deployment rollout sends SIGTERM, and the orchestrator's grace period expires before the node-level stdout collector (reading from the container runtime's log file) catches up with the last lines the container wrote right before exit. Timeline: the container writes final lines and exits inside its grace period; the collector, which polls or tails on its own schedule, hasn't necessarily read to EOF before the container's log file is rotated away or the pod's log directory is garbage-collected post-termination.

Detection & mitigation

Detection: a pattern of missing 'final lines' specifically correlated with pod terminations (not crashes generally) — comparing container exit timestamps against the collector's last-read-offset timestamp for that container's log file surfaces the gap. Mitigation: extend the termination grace period specifically to give the collector a bounded extra window to catch up, and have the collector treat a container-terminating event as a signal to immediately flush-read that container's remaining log file content, rather than waiting for its normal poll interval.

Recovery & correctness result

No recovery once the container's log file is garbage-collected post-termination — the source no longer exists. Correctness result: 'the container exited successfully' and 'every line it wrote was collected' are two different claims, and this failure is exactly the gap between them — a collection pipeline must treat the window between container exit and log-file garbage collection as a real, bounded deadline, not an afterthought.

F27 — Sidecar/agent crashes after reading a line but before checkpointing its offset

Trigger: the agent reads a batch of lines from the source file and hands it to the next durable stage (its fsynced spool or the collector), receives the acknowledgment, and then crashes before persisting its read-offset checkpoint for that batch. Timeline: on restart, the agent resumes from its last persisted checkpoint — which is before the batch it already forwarded — and re-reads and re-forwards the same lines a second time. Symptom: the same log lines appear twice downstream, timestamped identically, with no error anywhere in the pipeline.

Detection & mitigation

Detection: a duplicate-content signature (same source, same offset range, same content hash) arriving twice in the durable store, distinguishable from a legitimate repeated log message by the identical byte-for-byte offset range. Mitigation is architectural, not reactive: downstream deduplication keyed on a stable per-record identity (source + offset range, or a content hash) at the point of durable commit, so a resend is absorbed rather than double-counted.

Recovery & correctness result

No recovery needed if downstream idempotency-based dedup exists — recovery *is* the dedup. Correctness result without it: this ordering (hand off, receive the acknowledgment, then checkpoint) can only produce at-least-once delivery, never exactly-once, by construction. Checkpointing earlier, for example after appending to an in-memory buffer, flips the failure mode to at-most-once: a crash leaves the checkpoint ahead of data that was never persisted, an unrecoverable skip. That is why the chosen order is read, hand off, wait for the acknowledgment, then checkpoint, paired with downstream dedup: a duplicate is recoverable, a skip is not.

F28 — Broker partition leader dies mid-write

Trigger: the broker partition leader for a given topic-partition crashes or is network-partitioned away at the exact moment a batch of records is being written, before replication to followers completes. Timeline: under an acks=1 (leader-only) setting, the producer may have already received an ack for records the leader never replicated before dying; under acks=all with min.insync.replicas enforced, a record that is not on enough in-sync replicas is never acknowledged, so the producer sees a failure or timeout and can retry. A retry after a lost acknowledgment can duplicate a record unless the producer is idempotent or the downstream dedups.

Detection & mitigation

Detection: a leader-election event (visible in broker cluster metadata/metrics) correlated with a drop in acked-write throughput and, under acks=1, a gap between producer-reported-acked records and what the new leader actually has. Mitigation: acks=all with a minimum in-sync-replica count greater than one is what prevents this loss window from existing at all — the producer simply never gets an ack for a record that didn't survive the leader's death, and retries instead.

Recovery & correctness result

Under acks=1, records acked but not yet replicated before the leader died are unrecoverable — they were never anywhere but the dead leader's local log. Under acks=all with min.insync.replicas ≥ 2 and unclean leader election disabled, every acknowledged record was on at least two replicas, and a new leader is chosen only from in-sync replicas, so a single leader failure produces at worst a producer-visible retry, never a silent loss of something the producer believed had succeeded. If every in-sync replica is gone, the partition stays offline rather than electing a stale replica; enabling unclean election would trade that unavailability for permanent loss of acknowledged records.

F29 — A deployment's new log shape triggers an indexing mapping explosion

Trigger: a new application version adds a field whose value is unexpectedly high-cardinality or inconsistently typed (e.g. a field that's sometimes a number and sometimes a string, or a field holding a unique ID per request used as a dynamically-mapped key). Timeline: a schema-on-write search index attempting automatic field mapping either rejects records with a type conflict (silently dropping them from search) or, worse, creates a new mapped field per distinct value, growing the index's field-mapping metadata without bound as traffic continues.

Detection & mitigation

Detection: index cluster field-mapping count climbing in lockstep with a specific deployment's rollout, or a spike in type-conflict rejection errors in the indexer's own logs immediately following that deployment. Mitigation: a strict, versioned schema with an explicit allowlist of indexed fields and types (Architecture Deep Dive's schema chapter) rejects or quarantines a record with an unexpected shape at ingest time, rather than letting an uncontrolled schema-on-write index absorb it and silently degrade.

Recovery & correctness result

Recovery requires identifying and reindexing (or dropping and rebuilding) the polluted index if mapping explosion has already degraded cluster performance — an expensive, often hours-long operation on a large cluster. Correctness result with strict schema enforcement instead: a malformed-shape record is quarantined at ingest, visible in a dead-letter path for investigation, and never reaches the index at all — the index's mapping stays bounded regardless of what any single deployment ships.

F30 — Recent logs are absent from search: missing, or just not yet indexed?

Trigger: an on-call engineer queries for logs from the last 30 seconds during an active incident and finds nothing, and has to decide in real time whether the data was lost somewhere in the pipeline or simply has not been indexed yet. Timeline: each stage stamps time on a different machine's clock (see diagram): t_event on the producing host, t_durable on a broker, t_indexed on an indexer. A query for events from the last 2 seconds can find nothing even when nothing was lost, if the pipeline's normal indexing lag exceeds 2 seconds. Producer clock error makes it worse in one direction: a producer whose clock runs 4 seconds slow stamps events 4 seconds in the past, so a query window built from event_time can miss them or show them out of place.

Swipe to pan
Three Stage Timestamps, Three Different ClocksAcross hosts, a timestamp difference is the true delay plus the clocks' disagreementt_eventproducing host's clock —when the line was writtent_durablebroker's clock —when the commit was acceptedt_indexedindexer's clock —when it became queryable(t_later − t_earlier) across two hosts = true delay + (later host's clock error − earlier host's clock error)Producer clock 4 s fast: t_durable − t_event reads 4 s too small, even negative. Producer clock 4 s slow: it reads 4 s too large.Trust a difference only when the clock error is bounded and much smaller than the delay being measured.Time spent waiting inside one process (for example in the agent spool) is measured with a monotonic clock: no sync needed.'Missing from search' and 'late' look identical in one query; per-stage lag from the platform's own clocks tells them apart.
Three stage timestamps from three different clocks: 'missing' and 'late' look identical in one query, and cross-host differences include clock error

Detection & mitigation

Detection: measure the pipeline stages with timestamps from the platform's own monitored clocks, for example t_indexed minus t_durable and t_durable minus observed_time, and measure waiting inside the agent with a monotonic clock. A dashboard of those lags lets an on-call engineer check whether current end-to-end lag is within normal bounds before concluding anything is lost. Do not use now minus event_time as the lag metric: it includes the producer's clock error. Without this instrumentation the only tool available is waiting a bit and querying again, which is a poor incident-response posture.

Recovery & correctness result

No recovery is needed if the record is simply late: it becomes visible once the indexer has processed it, and the lag dashboard would have shown that this was expected. If lag is elevated beyond the normal range, that is a distinct, actionable signal (an indexing backlog, per F16/F17) rather than a mystery. Correctness result: 'missing from search' is not a well-formed incident claim on its own. It has to be qualified, for example 'missing while indexing lag is 3 seconds, normal is under 10,' to be actionable.

F31 — PII is discovered in an already-indexed, already-archived log

Trigger: a logging statement includes a customer's email address or similar PII that the redaction pattern library didn't recognize (a new field, an unusual format). Timeline: by the time this is discovered — typically via a compliance audit or a customer complaint — the record has already been indexed (searchable by anyone with query access to that tenant) and has aged into at least one colder storage tier, possibly archived to long-term object storage with its own separate retention policy.

Detection & mitigation

Detection: a periodic PII-scanning sweep over indexed content (pattern-matching plus, ideally, a classifier) run as an ongoing compliance control, not a one-time check. Mitigation has three separate, necessary parts because the data now exists in three separate places: revoke or redact the live searchable copy in the hot index, locate and redact or delete the colder-tier copy, and locate and redact or delete the archived copy — a single 'delete from the database' action does not reach all three.

Recovery & correctness result

Recovery cannot undo any exposure that already happened (a query that already returned the record, a dashboard that already displayed it) — recovery is limited to stopping *further* exposure from this point forward. Correctness result: a system promising 'PII removal' without an explicit answer for the archive tier is making an incomplete promise — a lifecycle policy that forgets a record was copied to cold archive before redaction was applied is exactly how this goes wrong.