Improve UX for reconnecting after server restart#548
Improve UX for reconnecting after server restart#548Darshan808 wants to merge 27 commits intojupyterlab:mainfrom
Conversation
krassowski
left a comment
There was a problem hiding this comment.
I think this goes in the right direction. The frontend changes fallback to unknown_session so it should be a fine upgrade experience for existing serves (whether jupyter-server-ydoc of jupyverse)
There was a problem hiding this comment.
Pull request overview
This PR improves real-time collaboration reconnection behavior after a server restart by persisting recent collaboration session IDs/versions server-side and by showing a translated, actionable client dialog only when reconnection is unsafe.
Changes:
- Persist recent server session IDs (and package version) under
.jupyter/collaboration_sessions.jsonand use them to decide whether an old clientsessionIdcan safely reconnect. - Update the WebSocket close payload for unsafe reconnects / initialization errors to structured JSON and handle it client-side with translated dialogs and an optional “Reload” action.
- Add unit tests for session-store compatibility logic (Python) and close-payload handling (TS).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
projects/jupyter-server-ydoc/jupyter_server_ydoc/utils.py |
Adds session store + compatibility check helpers and exposes SERVER_SESSION/YDOC_SERVER_VERSION. |
projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py |
Persists current session on connect and conditionally closes with structured JSON payload when reconnect is unsafe. |
packages/docprovider/src/yprovider.ts |
Parses 1003 close payload JSON and presents translated dialogs with optional reload. |
packages/docprovider/src/tokens.ts |
Defines ISessionClosePayload payload shape for close-reason handling. |
tests/test_session_store.py |
Adds Python unit tests for session compatibility decisions. |
packages/docprovider/src/__tests__/sessionClose.spec.ts |
Adds Jest tests validating dialog + reload behavior for 1003 close payloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Michał Krassowski <[email protected]>
krassowski
left a comment
There was a problem hiding this comment.
This makes sense to me. The UI test failures are unrelated, due to a new JupyterLab version release which fixed incorrect fonts in some input elements.
|
@davidbrochart does it look good to you too? |
davidbrochart
left a comment
There was a problem hiding this comment.
Would it be better to use an SQLite database to store the sessions?
On one hand SQLite is already a dependency because pycrdt-store and jupyter-server-file-id use it, on the other hand this is such a simple structure that I am not sure if it is better. No strong opinion here. |
davidbrochart
left a comment
There was a problem hiding this comment.
It seems that this PR introduces a new .jupyter directory. I'm wondering if we could take advantage of the YStore, which is by default an SQLite database in .jupyter_ystore.db?
| import json | ||
| import uuid | ||
| from datetime import datetime, timezone | ||
| from ._version import __version__ # noqa | ||
| import asyncio | ||
| import os |
There was a problem hiding this comment.
Since these imports are not ordered, it looks like the linter doesn't run on this file?
There was a problem hiding this comment.
I think the linter isn't configured for these. I just raised a PR #553
|
|
||
| # Collaboration package version changed → reject | ||
| if previous_version != current_version: | ||
| return False, "version_mismatch" |
There was a problem hiding this comment.
We already have shared document versions, e.g. the YNotebook version. Should we use it or is it the jupyter-collaboration package version that we want to check?
There was a problem hiding this comment.
It is definitely jupyter-collaboration (specifically jupyter-server-ydoc and jupyter-docprovider but collaboration metapackage is a good proxy) that we want to check as changes sometimes come with changes to custom messages sent yjs protocol or can come with changes to yjs version itself.
Maybe we should also consider versions of YNotebook and other documents. I am not sure how easy it would be to add here.
There was a problem hiding this comment.
We can get the document version
document_version = getattr(self.room._document, "version", None).
I made it optional so the connection is still allowed if the previous session didn’t store a document version.
It is not new in the sense that both JupyterLab Desktop and
YStore is configurable and some deployments do not use the SQLite database variant. |
krassowski
left a comment
There was a problem hiding this comment.
It feels like AnyioPath or Locking mechanism could be to blame. I will ask copilot for a pass.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Hmm with the latest change the tests are failing and timing out. I guess it tells us that the locking is problematic. |
|
Not sure where things are going wrong! jupyter-collaboration/tests/test_documents.py Lines 52 to 101 in 0b0dd73 Looking into it 👀 |
| return encoded_path.split("/")[-1] | ||
|
|
||
|
|
||
| async def _get_jupyter_session_store(root_dir: str) -> Path: |
There was a problem hiding this comment.
This function is not async. Could you remind me why we don't use async file operations?
There was a problem hiding this comment.
This was changed back in 14e7ae3 because the tests are failing and timing out. It appears that AnyioPath does not play well with the event loop here. If there is an async implementation that can work here I see no reason to not use it but given that we are looking at a tiny JSON file I do not consider it blocking myself as the blocking time would be minimal, right?
(I think this might be down to interaction between AnyioPath and locking, especially problematic in pytest context)
There was a problem hiding this comment.
It appears that
AnyioPathdoes not play well with the event loop here.
Maybe because we didn't use AnyIO's pytest plugin?
Usually all file operations in a server should be async, but if you don't want to do that here then at least the added async functions that are not actually async (they don't await) should be converted back to sync.
There was a problem hiding this comment.
Yes, that makes sense. @Darshan808 can you explore if anyio pytest plugin can help here and if not just drop the async?



Fixes #535
Description
Jupyter_server_ydoc:
.jupyter/collaboration_sessions.jsonon each connection1003when reconnection would be unsafe (different directory or version mismatch)User Facing Changes
Most server restarts (same directory, same version) no longer interrupt the user at all.