Repository navigation
sync: a report of what both devices hold is answered at once and not lost - #80
Merged
Merged
Conversation
…lost A device's "sync-complete" and "in-sync" reports tell the other device what both now hold. Each was sent once with a 2 s limit, and the receiving side answered only after recording it, which waits for the database, held for seconds by a snapshot or a sync. On a busy device the report was lost: its record of what both hold stayed a round behind, and the next save read as both sides having changed, a false conflict after which nothing more was taken (upstream CI #385, the recurring Journey failure; #381's reports timed out both ways for a minute). - The receiving side answers first and records after, still before the handler returns, one report at a time. - The sender sends a state-carrying report again until it is answered: four tries of 5 s, with 1, 2 and 4 s between them. Later reports to that peer wait, so the order holds. Progress reports are still sent once. - Each send of a report carries the same reportId, and a report seen again is not recorded again. A report without one (an older build) is recorded as before. Tests: a slow first answer to sync-complete gets the report sent again, in order, with one ID, while a progress report is sent once (fails with a single try); a report is answered while its recording is held up (fails on the old handler); a report sent twice is recorded once, one without an ID every time (fails on the old handler). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@ChakraFusion is attempting to deploy a commit to the sivadaboi's projects Team on Vercel. A member of the Team first needs to authorize it. |
Author
|
About the red run #394: neither failure comes from this PR. |
ChakraFusion
added a commit
to ChakraFusion/OpenSave
that referenced
this pull request
Oct 17, 2026
…ded at once After a pure pull the puller records the agreed base; the device it pulled from recorded the same only when a later fetch of the puller's save found it unchanged. A puller that edited again first (quick turns, a game saving right after a sync) left that device with a stale base, and its next sync read both sides as changed: a false conflict, after which it took nothing (upstream CI: TestSoak_ManyAlternatingRoundsNeverConflict, Windows, relay). The puller now sends an "agreed" report with the hash it holds; the other side records it as the base when it is exactly what it handed over (its push record), and ignores it otherwise. Older peers ignore the report. Like "sync-complete" since Liquid-co#80, the report is answered at once and sent again until it is answered (carriesState), with the same reportId; a repeat is recorded once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Janlhin
pushed a commit
to Janlhin/OpenSave
that referenced
this pull request
Oct 19, 2026
Entries for Liquid-co#90, Liquid-co#78, Liquid-co#80, Liquid-co#54, Liquid-co#52, Liquid-co#94 and Liquid-co#79 under Fixed; Liquid-co#84, Liquid-co#55, Liquid-co#53 and Liquid-co#74 under Changed; Liquid-co#73 under Added.
ChakraFusion
added a commit
to ChakraFusion/OpenSave
that referenced
this pull request
Oct 19, 2026
…ded at once After a pure pull the puller records the agreed base; the device it pulled from recorded the same only when a later fetch of the puller's save found it unchanged. A puller that edited again first (quick turns, a game saving right after a sync) left that device with a stale base, and its next sync read both sides as changed: a false conflict, after which it took nothing (upstream CI: TestSoak_ManyAlternatingRoundsNeverConflict, Windows, relay). The puller now sends an "agreed" report with the hash it holds; the other side records it as the base when it is exactly what it handed over (its push record), and ignores it otherwise. Older peers ignore the report. Like "sync-complete" since Liquid-co#80, the report is answered at once and sent again until it is answered (carriesState), with the same reportId; a repeat is recorded once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChakraFusion
added a commit
to ChakraFusion/OpenSave
that referenced
this pull request
Oct 20, 2026
…ded at once After a pure pull the puller records the agreed base; the device it pulled from recorded the same only when a later fetch of the puller's save found it unchanged. A puller that edited again first (quick turns, a game saving right after a sync) left that device with a stale base, and its next sync read both sides as changed: a false conflict, after which it took nothing (upstream CI: TestSoak_ManyAlternatingRoundsNeverConflict, Windows, relay). The puller now sends an "agreed" report with the hash it holds; the other side records it as the base when it is exactly what it handed over (its push record), and ignores it otherwise. Older peers ignore the report. Like "sync-complete" since Liquid-co#80, the report is answered at once and sent again until it is answered (carriesState), with the same reportId; a repeat is recorded once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The cause of the recurring Journey failure, found in the logs #57 now prints (run #385):
could not tell Neva-A that neva is sync-complete: ... context deadline exceeded.The same report timeout shows in #381 (
TestCrossingSyncs_AFirstPullIsNotTakenForADifferentSave), there in both directions for over a minute.Why the report was lost.
sync-completeandin-synctell the other device what both now hold. Each was sent once with a 2 s limit, and the receiving side answered only after recording it. Recording waits for the database, which has a single connection and can be held for seconds by a snapshot or a sync on that device. On a busy device, or a slow CI runner with the race detector, the 2 s ran out and the report was dropped.The fix, on the LAN path (the relay doesn't wait for an answer):
reportId, and a report seen again isn't recorded again (the first one arrived; its answer was what got lost). A report without an ID, from an older build, is recorded as before.Tests (each fails without the change):
TestSyncEventReportsThatCarryStateAreSentAgain: a slow first answer tosync-completegets it sent again, in order and with one ID, while a slow progress report is sent once. Fails with a single try.TestAReportIsAnsweredBeforeItIsRecorded: a report is answered while its recording is held up. Fails on the old handler (the client times out).TestAReportSentAgainIsRecordedOnce: a report sent twice is recorded once; reports without an ID are recorded every time. Fails on the old handler.Related: #51 covers the other way the base falls behind: after a pull, nothing reported the agreed state at all, so a quick next edit read as a conflict (the soak failures). Its
agreedevent goes over this same channel; whichever of the two merges second addsagreedtocarriesState.Checks: the unit tests and 21 related e2e tests (Journey, crossing syncs, deletions, soak) pass locally, twice each with the race detector; fork CI is green. One Windows job failed once on
TestConflict_RequiresBothSidesToResolveand passed when re-run: an older rare failure (also upstream run #160), a race between one device resolving a conflict and the other device's watcher starting a sync, which this PR doesn't touch. I'll look at it separately.🤖 Generated with Claude Code