Skip to content

Dedup journal.Summary() against retried-shard duplicate records - #72

Merged
srhoods merged 1 commit into
masterfrom
fix-journal-summary-dedup
Aug 16, 2026
Merged

Dedup journal.Summary() against retried-shard duplicate records#72
srhoods merged 1 commit into
masterfrom
fix-journal-summary-dedup

Conversation

@srhoods

@srhoods srhoods commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • journal.Summary() now dedups per (pass, type, rel_path), matching the existing pattern in journal.Orphans() — fixes inflated counts in drsync journal cat --summary, the completion email, the WebUI journal-summary panel, and the persisted journal_type_counts rollup (/report, job detail panel), all of which route through Summary().
  • Root cause: journaling is at-least-once by design (agent/src/jrn.c: "lease-expiry re-runs gives at-least-once journaling (readers dedup)") — a shard whose lease expires mid-run is requeued and re-emits every record it already wrote, not just the remainder. Summary() was one of the readers not honoring that contract.
  • seedVerify/seedDirfix (passctrl.go) hit the identical duplication but are deliberately left un-deduped — both stream the journal in O(batch) memory (TestSeedVerifyMemoryBounded pins this at 1M files), and a whole-pass "seen" set would reintroduce O(N) memory. Safe to leave as-is: a duplicate VerifyEntry/DirMeta only causes a redundant re-verify/re-apply, both idempotent.
  • docs/DESIGN-coordinator.md §5 documents the at-least-once contract and which readers honor it, for future readers.

Context

Found live while investigating a user report of stale destination ACLs surviving a re-run pass. A 22k-file/POSIX-ACL reproduction triggered a lease expiry mid-pass, which inflated journal-derived counts by exactly the retried shard's record count (2000, in that repro) — a real, independently-reproducible bug, but not an explanation for the original report. Duplicate counting cannot cause files to be silently missed with zero journal signal (a re-run shard re-diffs from scratch and will always re-fix or correctly see nothing to fix). That original symptom remains open; the leading theory is source-filesystem metadata-caching staleness (previously observed with VAST appliances), pending further large-scale testing.

Test plan

  • gofmt -l . clean
  • go vet ./... clean
  • go test -count=1 ./coordinator/... — all green, including TestSeedVerifyMemoryBounded (streaming guarantee intact) and new tests TestSummaryDedupsRetriedShard, TestSeedVerifyToleratesRetriedShard, TestSeedDirfixToleratesRetriedShard
  • New tests verified via deliberate-breakage falsification (reverted fix, confirmed failure, restored)

🤖 Generated with Claude Code

https://claude.ai/code/session_01PsdNZLfmAFrMX2VUtkLtmm

Journaling is at-least-once: a shard whose lease expires mid-run is
requeued and re-runs from scratch, re-emitting every record it already
wrote (agent/src/jrn.c: "lease-expiry re-runs gives at-least-once
journaling (readers dedup)"). journal.Orphans() already deduped
correctly; journal.Summary() did not, so a retried shard inflated
`drsync journal cat --summary`, the completion email, and the WebUI
journal-summary panel (and via passctrl.recordJournalTypeCounts, the
persisted journal_type_counts rollup that /report and the job detail
panel read) by exactly its record count.

Found live investigating a user report of stale destination ACLs
surviving a re-run: a 22k-file/POSIX-ACL reproduction hit a lease
expiry mid-pass, inflating counts by 2000. That reproduction did not
explain the original symptom (files silently missed with zero journal
signal) — this fix addresses the duplication bug it surfaced along the
way, not the original report, which remains open pending the user's
own larger-scale test.

seedVerify/seedDirfix hit the same duplication but deliberately stay
un-deduped: both stream the journal in O(batch) memory
(TestSeedVerifyMemoryBounded pins this at 1M files), and a whole-pass
"seen" set would reintroduce the O(N) memory that design avoids. Safe
to leave as-is since a duplicate VerifyEntry/DirMeta only means one
file/dir gets re-verified or re-applied, and both operations are
idempotent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PsdNZLfmAFrMX2VUtkLtmm
@srhoods
srhoods merged commit 116b72c into master Aug 16, 2026
23 checks passed
@srhoods
srhoods deleted the fix-journal-summary-dedup branch August 16, 2026 22:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant