Skip to content

feat(yjs): improve handling of external file conflicts - #3246

Draft
JammingBen wants to merge 3 commits into
mainfrom
feat/yjs-improved-external-update-handling
Draft

feat(yjs): improve handling of external file conflicts#3246
JammingBen wants to merge 3 commits into
mainfrom
feat/yjs-improved-external-update-handling

Conversation

@JammingBen

@JammingBen JammingBen commented Aug 27, 2026

Copy link
Copy Markdown
Member

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 a has no unsaved changes

  • user a is in a room for file.txt
  • file.txt gets external update
  • user b joins room and fetches the updated file:
    -> flags the room with isStale: true
    -> initiates recovery
  • user a detects the isStale change, stays in the room
  • Recovery finished and every peer in the room gets the updated file

user a has unsaved changes

  • user a is in a room for file.txt
  • file.txt gets external update
  • user b joins room and fetches the updated file:
    -> flags the room with isStale: true
    -> initiates recovery
  • user a detects the isStale change and leaves the room to avoid that their changes get overwritten
  • Recovery finished and every peer in the room gets the updated file (note that user a is not in the room anymore, hence no update for them)

user a has unsaved changes and is offline during recovery

  • user a is in a room for file.txt but goes offline
  • file.txt gets external update
  • user b joins room and fetches the updated file:
    -> flags the room with isStale: true
    -> initiates recovery
  • Recovery finished and every peer in the room gets the updated file
  • user a goes 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

@JammingBen JammingBen self-assigned this Aug 27, 2026
@JammingBen
JammingBen force-pushed the feat/yjs-improved-external-update-handling branch 4 times, most recently from aa51dc5 to 6ec4403 Compare August 27, 2026 14:00
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
JammingBen force-pushed the feat/yjs-improved-external-update-handling branch from 6ec4403 to f6ee402 Compare August 27, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Collaborative editing: handle external file updates

1 participant