Reported symptoms
At 15:40 an engineering lead reports that the 2024 acquisition dataset has gone
from the project share. /srv/rbdr-projects/acq-2024 is absent from
rbdr-primary, and absent from /srv/rbdr-dr/projects at the second site.
The share is replicated continuously: a change-triggered agent watches the tree
and invokes rsync -a --delete on the affected subtree within seconds. The tile
has been green all day.
The operator who ran a cleanup at 14:22 removed one directory they believed was a duplicate staging copy, expecting the second site to hold the original. The architecture register lists that site under Recovery, annotated “second copy of all project data”. At 15:52 the destination is listed: every current project directory, and no earlier version of anything.
Evidence provided
The propagation behaviour was measured beforehand on the same rsync build the agent invokes. Its first night ends with “So far this looks like a backup” — a claim the capture does not leave standing.
$ rsync -a --delete src/ mirror/--- mirror now ---
mirror/orders.csv
mirror/reports/q3.txt
invoice.txt recoverable from the mirror? NO - the mirror deleted it too
Elapsed time between the mistake and the loss of the only other copy:
one scheduled interval. Nobody had to make a second mistake.In this estate the interval is not a night.
$ journalctl -u rbdr-replicate --since '2026-08-27 14:22:00'14:22:03 primary audit: operator removed projects/acq-2024 (4,118 entries)
14:22:05 rbdr-dr change detected for subtree projects/acq-2024
14:22:07 rbdr-dr applied: 4,118 deletions, 0 created, 0 bytes, exit 0
14:22:07 rbdr-dr rbdr_replication_lag_seconds 4Illustrative output
The destination holds nothing older than that applied change: no backup directory target, no snapshot, no versioned object store. The register that sent everyone there says otherwise.
$ sed -n '41,45p' /srv/rbdr-docs/rbdr-architecture-register.txtRecovery
rbdr-dr-site second copy of all project data
Availability
rbdr-lb-01/02 service front end
rbdr-app-03/04 standby application nodesIllustrative output
The second site’s change log for the day records no interactive session, no cleanup run, and no writer other than the replication receiver.
Work the evidence before reading on
- The lag series never left its normal band. Is that a reason to exclude replication as the mechanism of loss, or a reason to include it?
- The second site runs its own cleanup of aged directories. Which single piece of evidence removes it, and what would it have shown had the cleanup been responsible?
- What would the second site have to be able to do for the Recovery heading to be correct?
- If the agent ran nightly instead of on change, what would have been different at 15:52 — and what would have been identical?
Root cause
The destination was defined to hold only the present
The agent does not copy events. It makes a destination match a source, and
rsync -a --delete removes entries that exist only at the destination. Under
that definition an operator removing a directory is a difference to be resolved,
and resolving it is the agent working correctly.
Because the destination retained no prior state, resolving that difference consumed the only other copy. The mistake became permanent at 14:22:07.
The register filed the second site under the wrong heading
Everything that site provides is availability: a warm copy of current state, continuity when the primary is unreachable, somewhere to run from. Recovery requires history — the ability to produce a state the source no longer holds. The second site could never do that, and nobody asked whether it could.
The misfiling was read and acted on: in July 2025 the coverage report dropped
/srv/rbdr-projects with the comment “covered by DR replication”, and the 3-2-1
review then passed by counting the replica as copy number two.
The hypothesis the evidence eliminates
The bridge opened on independent modification at the second site — reasonable, since that site runs its own cleanup of aged project directories.
Its change log eliminates it: for 2026-08-27 it records no interactive session and no cleanup run against that path, and every write there carries the replication receiver’s process id. The receiver journal supplies the positive account — 4,118 deletions applied at 14:22:07, creating nothing and transferring no bytes, four seconds after the primary audit record. A local cleanup would have left its own actor in that log, and nothing upstream four seconds earlier.
Resolution
Stop the replication agent for this share first: while it runs, anything restored into the source tree is a change to propagate, and anything placed at the destination is a difference to remove.
Then find a copy the agent never touched: here, an analyst workstation holding a working copy eleven days old and a mail archive holding four deliverables as attachments. Nine weeks of revisions were not recovered.
Rebuild before restoring into it:
RUN=$(date -u +%Y%m%dT%H%M%SZ)
HIST="/srv/rbdr-dr/projects/.rbdr-history/$RUN"
rsync -a --delete --backup --backup-dir="$HIST" --max-delete=500 \
/srv/rbdr-projects/ /srv/rbdr-dr/projects/
rsync(1) documents --backup-dir as storing every displaced or deleted entry
on the receiving side rather than discarding it, and --max-delete as skipping
further deletions past the limit and exiting with error code 25. Then commission
a repository on a target the primary credential cannot rewrite.
Verification
Remove a marker file from a scratch subtree on the primary, let the agent propagate, and read the pre-change bytes back out of the destination history directory; the checksum must match the value recorded beforehand.
Delete more entries than the configured ceiling and confirm the run reports skipped deletions with a non-zero exit status while the destination still holds them.
Delete a directory on the primary and restore it alone from a recovery point taken before that deletion — the case this incident was — as well as the whole share into an isolated path.
Re-run the 3-2-1 review against the corrected register.
Prevention
Classify every copy by the failure it answers. Replication answers loss of a host, a rack or a site; it cannot answer deletion, corruption or encryption, because it is obliged to reproduce them.
Require every Recovery entry to name its history mechanism — retained snapshots, a stated repository retention, versioned objects with a stated expiry. An entry that cannot name one belongs under Availability.
Treat a coverage exemption as a production change. Dropping a path from the backup estate on the strength of a sentence about another system deserves a review.
Read low lag as exposure. Four seconds of lag is four seconds in which a mistake is still undoable from the second copy.