Skip to content

sync: a parked conflict keeps its entity in the round - #271

Merged
udaychandra merged 2 commits into
mainfrom
fix/parked-conflicts-stay-in-scope
Sep 20, 2026
Merged

udaychandra merged 2 commits into
mainfrom
fix/parked-conflicts-stay-in-scope

Conversation

@udaychandra

Copy link
Copy Markdown
Contributor

Field report

Mady upgraded to v0.44.2 and synced: ✓ Already in sync with main. — and sail conflicts still lists all 22 last_activity_at conflicts, every one carrying its original detection time. The self-heal from #270 never fired.

Root cause

A protocol-4 round examines two sets: entities main changed since the checkpoint, and dirtyIds() — rows whose rev <> base_rev. A run parked over its heartbeat is in neither: main's entry is already behind the checkpoint, and the stamp is unjournaled so the row is not dirty. Once the first round parked it, no later round ever looked at it again. #270's heal (close the conflict when the entity settles via adopt) is correct but unreachable.

#270's test missed this because it drove SyncEngine.reconcile — the whole-table round — which production no longer runs. That is on me.

Fix

StoreReplica.dirtyIds() now includes every entity with an open conflict (SyncConflicts.pendingIds). Each round re-reconciles them against main's current row (via the existing need op):

  • a conflict that has stopped being one — a latest-wins heartbeat, or main coming to agree — merges or adopts, and adopt closes it;
  • a real conflict is re-recorded with fresh snapshots (so resolve no longer acts on stale ones), and the round reports it instead of printing "Already in sync".

Test

ParkedConflictSyncTest runs a real PagedSyncSession against SyncRpcServer with the production run push policy, in the field's exact state (conflict parked, checkpoint past main's entry, heartbeat unjournaled, neither side with news). Fails on v0.44.2 with the conflict still pending; passes here with merged = 1, no conflicts, and main holding the later stamp.

Also corrects stampActivity's Javadoc, which #270 wrongly changed.

mvn clean verify: 5,267 tests, all coverage gates met.

A protocol-4 round examines what main changed since the checkpoint and what
this box journaled since its base. A run parked over its heartbeat is
neither: main's entry is behind the checkpoint and the stamp carries no
revision. So once a round parked it, no round ever looked at it again, and
the self-heal shipped in 0.44.2 (close the conflict when the entity settles)
could never fire: Mady upgraded, synced, was told 'Already in sync', and
still had 22 conflicts, each with its first detection time.

dirtyIds now includes every entity with an open conflict. Each round
re-reconciles them against main's current row: a conflict that has stopped
being one (a latest-wins heartbeat, or main coming to agree) merges or
adopts and closes; a real one is re-recorded with fresh snapshots, and the
round reports it instead of claiming to be in sync.

The 0.44.2 test exercised SyncEngine's whole-table round, which production
no longer runs. ParkedConflictSyncTest drives a real paged session against
SyncRpcServer with the production run push policy, in the field's exact
state, and fails on 0.44.2.

Also corrects stampActivity's Javadoc, which 0.44.2 wrongly changed to say
the next round pushes the live row: an unjournaled stamp is not dirty, so it
rides along on the run's next real revision, as first written.
…heartbeat

The field's state driven through the command the field runs: a node with a
conflict parked over a run's heartbeat, its checkpoint past main's entry and
neither side with news, runs sail sync through the fleet harness. Fails with
the conflict still pending when StoreReplica.dirtyIds leaves parked
conflicts out.
@udaychandra
udaychandra merged commit fa430b7 into main Sep 20, 2026
3 checks passed
@udaychandra
udaychandra deleted the fix/parked-conflicts-stay-in-scope branch September 20, 2026 22:18
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