Skip to content

Commit b8d9558

Browse files
kriszypclaude
andcommitted
fix(replication): don't let an out-of-order full update revert a newer record
In the out-of-order resequencing path (precedesExisting <= 0), when audit is on and the audit walk finds no succeeding updates to resequence around, a full update fell through to the shared commit and stored the older record over the newer one (recordToStore = recordUpdate), leaving the cluster non-convergent. Skip the losing full update instead — the existing record is newer, so this older update is superseded. Uses a bare return (no writeCommit) to match the superseded-by-newer-put branch above, so no audit record is written referencing this losing update's pre-saved blobs. Reproduced via deployment-tracking: a burst of same-key full puts commits out of order on a loaded peer and the row gets stuck at a non-terminal status. Fixes #1170 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a9c5db2 commit b8d9558

1 file changed

Lines changed: 13 additions & 0 deletions

File tree

resources/Table.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1973,6 +1973,19 @@ export function makeTable(options) {
19731973
);
19741974
if (!incrementalUpdateToApply) return writeCommit(false); // if all changes are overwritten, nothing left to do
19751975
}
1976+
if (fullUpdate && !incrementalUpdateToApply && precedesExisting < 0) {
1977+
// Out-of-order full update whose audit walk found no succeeding updates to
1978+
// resequence around: the existing record is strictly newer (precedesExisting < 0),
1979+
// so this older full update is superseded. Falling through to the shared commit
1980+
// below would set recordToStore = recordUpdate and revert the newer record. Bare
1981+
// return (no writeCommit) matches the superseded-by-newer-put branch above so no
1982+
// audit record is written referencing this losing update's pre-saved blobs.
1983+
// Gated on precedesExisting < 0 (not <= 0) so a same-transaction put-after-delete —
1984+
// which arrives as a tie (precedesExisting === 0) with no committed audit yet —
1985+
// still falls through and applies. (harperdb/harper#1170)
1986+
write.skipped = true;
1987+
return;
1988+
}
19761989
} else if (fullUpdate) {
19771990
// if no audit, we can't accurately do incremental updates, so we just assume the last update
19781991
// was the same type. Assuming a full update this record update loses and there are no changes —

0 commit comments

Comments
 (0)