Skip to content

extension-redis can unload document being loaded for new client connection #971

@james-atticus

Description

@james-atticus

Description

During a provider client reconnect, we're observing that before/afterUnloadDocument hooks are being called twice, after which the Yjs document is destroyed and updates from the client are silently lost.

Logging from the reconnect showing before/afterUnloadDocument being called immediately after the document is loaded:

Image

Later logs where Sync/Update messages are received but there are no corresponding out messages to the client nor Document X changed log messages:

Image

Steps to reproduce the bug

I haven't been able to reproduce it directly, but I'm pretty sure the bug is caused by the debounced logic in extension-redis:

  1. Client disconnects
  2. onDisconnect hooks are called (and awaited, as of Fixes a bug in extension-redis that leads to broken sync when clients… #587)
    a. The Redis hook resolves immediately, even though it starts a timeout to unsub
  3. onClose is called, document is unloaded because there are no active connections
  4. Client reconnects, calling setUpNewConnection -> createDocument
  5. Document is not loaded, so loadDocument is called
    a. Document is added to instance with this.documents.set(documentName, document)
    b. onLoadDocument hooks are called
  6. At some point during the async onLoadDocument hooks, the Redis onDisconnect timeout triggers
    a. It calls this.instance.documents.get(documentName), which returns the new document being loaded
    b. There are no active connections for the document, so it is unloaded (connectionCount: 0 in screenshots above)
  7. loadDocument -> createDocument finish running
  8. setUpNewConnection calls createConnection -> new Connection -> this.document.addConnection(this)
  9. Updates come in from client (connectionCount: 1 in screenshots above) however it's already too late

You can reproduce the broken state by adding an extension:

afterLoadDocument: async (data) => {
    data.instance.unloadDocument(data.document);
},

Expected behavior

extension-redis does not call unloadDocument for the newly loaded document. #831 made it so the extension used the latest document from the Hocuspocus instance, rather than the one passed into the hook, which strikes me as odd.

Environment?

  • Hocuspocus version: 2.15.3

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions