Skip to content

Commit af4431a

Browse files
committed
Start y Websocket Server earlier and make it resilient to crashes
1 parent adcde32 commit af4431a

File tree

2 files changed

+40
-11
lines changed

2 files changed

+40
-11
lines changed

projects/jupyter-server-ydoc/jupyter_server_ydoc/app.py

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from jupyter_ydoc.ybasedoc import YBaseDoc
1111
from pycrdt import Doc
1212
from pycrdt_websocket.ystore import BaseYStore
13-
from traitlets import Bool, Float, Type
13+
from traitlets import Bool, Float, Type, Set, Instance
1414

1515
from .handlers import DocSessionHandler, YDocWebSocketHandler
1616
from .loaders import FileLoaderMapping
@@ -75,6 +75,10 @@ class YDocExtension(ExtensionApp):
7575
model.""",
7676
)
7777

78+
_running_ywebsocket_server = Instance(asyncio.Task, allow_none=True)
79+
ywebsocket_server = Instance(JupyterWebsocketServer, allow_none=True)
80+
81+
7882
def initialize(self):
7983
super().initialize()
8084
self.serverapp.event_logger.register_event_schema(EVENTS_SCHEMA_PATH)
@@ -90,6 +94,13 @@ def initialize_settings(self):
9094
}
9195
)
9296

97+
self.ywebsocket_server = JupyterWebsocketServer(
98+
rooms_ready=False,
99+
auto_clean_rooms=False,
100+
ystore_class=self.ystore_class,
101+
log=self.log,
102+
)
103+
93104
def initialize_handlers(self):
94105
self.serverapp.web_app.settings.setdefault(
95106
"page_config_data",
@@ -103,13 +114,6 @@ def initialize_handlers(self):
103114
for k, v in self.config.get(self.ystore_class.__name__, {}).items():
104115
setattr(self.ystore_class, k, v)
105116

106-
self.ywebsocket_server = JupyterWebsocketServer(
107-
rooms_ready=False,
108-
auto_clean_rooms=False,
109-
ystore_class=self.ystore_class,
110-
log=self.log,
111-
)
112-
113117
# self.settings is local to the ExtensionApp but here we need
114118
# the global app settings in which the file id manager will register
115119
# itself maybe at a later time.
@@ -134,6 +138,33 @@ def initialize_handlers(self):
134138
]
135139
)
136140

141+
async def start_extension(self):
142+
"""Start the y-websocket server.
143+
"""
144+
self.log.info("Starting the Collaborative Document Server.")
145+
146+
def _restart_or_teardown_yserver(_):
147+
"""If the y websocket server task stopped due to an exception, restart it.
148+
149+
If the y-websocket server was cancelled on purpose, tear it down.
150+
"""
151+
self.ywebsocket_server._started = None
152+
self.ywebsocket_server._starting = False
153+
self.ywebsocket_server._task_group = None
154+
# If an exception was raised, restart the websocket server.
155+
if self._running_ywebsocket_server.exception():
156+
self.log.error(self._running_ywebsocket_server.exception())
157+
self.log.warning("Restarting the Collaborative Document Server.")
158+
self._running_ywebsocket_server = asyncio.create_task(self.ywebsocket_server.start())
159+
self._running_ywebsocket_server.add_done_callback(_restart_or_teardown_yserver)
160+
return
161+
self.log.info("Stopping the Collaborative Document Server.")
162+
163+
# Start the websocket server
164+
self._running_ywebsocket_server = asyncio.create_task(self.ywebsocket_server.start())
165+
# If the websocket crashes for any reason, let's restart it automatically and log errors.
166+
self._running_ywebsocket_server.add_done_callback(_restart_or_teardown_yserver)
167+
137168
async def get_document(
138169
self: YDocExtension,
139170
*,

projects/jupyter-server-ydoc/jupyter_server_ydoc/handlers.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,7 @@ def create_task(self, aw):
7070
task.add_done_callback(self._background_tasks.discard)
7171

7272
async def prepare(self):
73-
if not self._websocket_server.started.is_set():
74-
self.create_task(self._websocket_server.start())
75-
await self._websocket_server.started.wait()
73+
await self._websocket_server.started.wait()
7674

7775
# Get room
7876
self._room_id: str = room_id_from_encoded_path(self.request.path)

0 commit comments

Comments
 (0)