feat(yjs): improve handling of external file conflicts - #3246
Draft
JammingBen wants to merge 3 commits into
Draft
Conversation
JammingBen
force-pushed
the
feat/yjs-improved-external-update-handling
branch
4 times, most recently
from
August 27, 2026 14:00
aa51dc5 to
6ec4403
Compare
Disconnect users from a yjs room if they have unsaved changes that would be overwritten by an external update. This ensures they don't lose their unsaved work and can properly save or store it somewhere, before reconnecting to the room via a page reload. Also reflect the disconnected status in the indicator.
The isStale guard only holds when the flag arrives ahead of the rewrite it announces. A peer that was offline while a recovery ran gets flag, rewrite and commit back as one merged update, so the isStale guard never fires. As a result, unsaved local changes would just be overwritten with the new content. Key the guard on a new recoveryEpoch meta value that is bumped with every isStale and never cleared. If the flag is still up the doc is untouched and the peer just leaves the room. Otherwise the rewrite has already dropped the work, so leave the room and re-seed the doc from the last reported content.
JammingBen
force-pushed
the
feat/yjs-improved-external-update-handling
branch
from
August 27, 2026 14:08
6ec4403 to
f6ee402
Compare
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.
Description
Disconnect users from a yjs room if they have unsaved changes that would be overwritten by an external update. This ensures they don't lose their unsaved work and can properly save or store it somewhere, before reconnecting to the room via a page reload.
Also reflect the disconnected status in the indicator.
The second commit tackles an edge case where a user that was offline during a recovery would lose their local work upon reconnect. See down below for explanations.
Scenarios explained
user ahas no unsaved changesuser ais in a room forfile.txtfile.txtgets external updateuser bjoins room and fetches the updated file:-> flags the room with
isStale: true-> initiates recovery
user adetects theisStalechange, stays in the roomuser ahas unsaved changesuser ais in a room forfile.txtfile.txtgets external updateuser bjoins room and fetches the updated file:-> flags the room with
isStale: true-> initiates recovery
user adetects theisStalechange and leaves the room to avoid that their changes get overwrittenuser ais not in the room anymore, hence no update for them)user ahas unsaved changes and is offline during recoveryuser ais in a room forfile.txtbut goes offlinefile.txtgets external updateuser bjoins room and fetches the updated file:-> flags the room with
isStale: true-> initiates recovery
user agoes online again-> the guard on
isStale, that would remove them from the room, never ran, meaning they stayed in the room during recovery and have the updated content now-> they now leave the room
-> they recover their content from the last known state
closes #3103