From 07ebe8b7d79ebd3fc7877aa14a12da0b9f17a644 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 30 Jun 2025 15:38:39 +0200 Subject: [PATCH 1/3] chore(debug): log pending structs in remote ydoc This ydoc tracks the messages already received from the server. So missing messages will also lead to pendingStructs there. Signed-off-by: Max --- src/components/Editor.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 9c05e9ebc93..41022079192 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -850,6 +850,7 @@ export default { filePath: this.relativePath, clientId: this.ydoc.clientID, pendingStructs: this.ydoc.store.pendingStructs, + pendingStructsRemote: this.syncProvider?.remote.store.pendingStructs, clientVectors: [], documentState: this.syncService?.getDocumentState(), } From bb7bde0fd47325fa6e6fb0590994a18296336e7f Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 30 Jun 2025 15:59:10 +0200 Subject: [PATCH 2/3] fix(sync): check pending structs and resync if needed This check will happen every 30 seconds by default. So on average it will take 15 seconds after missing a message. This is also in "sync" with new autosaved versions becoming available roughly every 30 seconds. Signed-off-by: Max --- src/services/y-websocket.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/services/y-websocket.js b/src/services/y-websocket.js index fa2456de317..5fb4d0c45c7 100644 --- a/src/services/y-websocket.js +++ b/src/services/y-websocket.js @@ -289,7 +289,7 @@ export class WebsocketProvider extends Observable { params = {}, protocols = [], WebSocketPolyfill = WebSocket, - resyncInterval = -1, + resyncInterval = 30_000, maxBackoffTime = 2500, disableBc = false, } = {}, @@ -345,7 +345,11 @@ export class WebsocketProvider extends Observable { if (resyncInterval > 0) { this._resyncInterval = /** @type {any} */ ( setInterval(() => { - if (this.ws && this.ws.readyState === WebSocket.OPEN) { + if ( + this.ws + && this.ws.readyState === WebSocket.OPEN + && doc.store.pendingStructs + ) { // resend sync step 1 const encoder = encoding.createEncoder() encoding.writeVarUint(encoder, messageSync) From 842f861fe943f15528107933bb4e906df3a23a76 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 30 Jun 2025 16:05:27 +0200 Subject: [PATCH 3/3] fix(sync): send SyncStep1 right after step could not be applied Signed-off-by: Max --- src/services/y-websocket.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/services/y-websocket.js b/src/services/y-websocket.js index 5fb4d0c45c7..747ed7d4121 100644 --- a/src/services/y-websocket.js +++ b/src/services/y-websocket.js @@ -44,12 +44,24 @@ messageHandlers[messageSync] = ( ) => { encoding.writeVarUint(encoder, messageSync) const decoderForRemote = decoding.clone(decoder) + const pendingStructsBefore = provider.doc.store.pendingStructs const syncMessageType = syncProtocol.readSyncMessage( decoder, encoder, provider.doc, provider, ) + if ( + !pendingStructsBefore + && provider.doc.store.pendingStructs + && !encoder.hasContent + ) { + // The message received left pending structs behind. + // Send SyncStep1 to resync. + console.error('Failed to integrate yjs message. Trying to resync.') + encoding.writeVarUint(encoder, messageSync) + syncProtocol.writeSyncStep1(encoder, provider.doc) + } // Message came from the broadcast channel // Do not track in this.remote and do not emit sync. if (!emitSynced) {