From da2c065e5938ba15040670004686dda7c0f531c7 Mon Sep 17 00:00:00 2001 From: Erik Michelson Date: Fri, 21 Aug 2026 11:10:31 +0200 Subject: [PATCH] fix(realtime): check for online users before clearing note The disconnect handler for the realtime connection checks whether the disconnecting socket was the last user for that note. If that's the case and the note had unsaved changes, it persists them to the database which is an async progress. In the callback of this async progress, the note and all its pending changes (should be empty) are deleted. However, when the user reconnects quickly because of a flaky connection, it could happen that they create new pending changes before the database saving finished. As there was no additional "no-users" check before clearing the note, this could lead to content loss. Signed-off-by: Erik Michelson --- lib/realtime.js | 20 +++++++++----------- public/docs/release-notes.md | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/realtime.js b/lib/realtime.js index 3903eb6f..0284bb02 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -1,4 +1,5 @@ 'use strict' +const util = require('util') // realtime // external modules const cookie = require('cookie') @@ -569,16 +570,13 @@ function disconnect (socket) { if (note.server.isDirty) { updateNote(note, function (err, _note) { if (err) return logger.error('disconnect note failed: ' + err) - // clear server before delete to avoid memory leaks - note.server.document = '' - note.server.operations = [] - delete note.server - delete notes[noteId] - if (config.debug) { - logger.debug(notes) - getStatus(function (data) { - logger.debug(JSON.stringify(data)) - }) + // Check again for online users before clear, because users could reconnect during the async updateNote + if (Object.keys(note.users).length <= 0) { + // clear server before delete to avoid memory leaks + note.server.document = '' + note.server.operations = [] + delete note.server + delete notes[noteId] } }) } else { @@ -596,7 +594,7 @@ function disconnect (socket) { if (disconnectSocketQueue.length > 0) { disconnect(disconnectSocketQueue[0]) } if (config.debug) { - logger.debug(notes) + logger.debug(util.inspect(notes)) getStatus(function (data) { logger.debug(JSON.stringify(data)) }) diff --git a/public/docs/release-notes.md b/public/docs/release-notes.md index f1762edc..bdc1e1a8 100644 --- a/public/docs/release-notes.md +++ b/public/docs/release-notes.md @@ -9,6 +9,7 @@ ### Bugfixes - All links will be opened in new tabs instead of the same tab as the note. This was changed when the external link warning was implemented and now the old behavior is restored. +- Fixed a bug in the realtime disconnect handler that could lead to data-loss of users with a flaky connection ## 1.11.1 2026-07-24