Skip to content

feat: adopt Cloudflare WebSocket Hibernation API#137

Draft
kptdobe wants to merge 1 commit into
mainfrom
fix/ws-hibernation-api
Draft

feat: adopt Cloudflare WebSocket Hibernation API#137
kptdobe wants to merge 1 commit into
mainfrom
fix/ws-hibernation-api

Conversation

@kptdobe
Copy link
Copy Markdown
Contributor

@kptdobe kptdobe commented May 8, 2026

Problem

During image drag-and-drop sessions in DA, Durable Objects are evicted when the browser briefly drops WebSockets (while busy uploading blobs). Each eviction clears the in-memory docs Map. On reconnect, bindState re-fetches from da-admin and triggers a 1-second revert timer, causing the just-inserted images to disappear.

Root cause

webSocket.accept() (the non-Hibernation API) causes the DO to be evicted whenever a WebSocket disconnects. Any work in flight (debounced saves, bindState async chain) is cancelled with Outcome: "canceled", and the in-memory Yjs state is lost.

Solution

Adopt the Cloudflare WebSocket Hibernation API (ctx.acceptWebSocket()), which keeps the DO alive across WebSocket disconnects:

  • DocRoom.fetch() calls this.ctx.acceptWebSocket(server) + server.serializeAttachment({ docName, auth, authActions }) instead of server.accept()
  • New class methods webSocketMessage, webSocketClose, webSocketError handle CF Hibernation routing
  • setupWSConnection gains a hibernation flag (default false) — when true, skips addEventListener registration since CF calls the class methods directly
  • New exports handleWebSocketMessage and handleWebSocketClose in shareddoc.js allow webSocketMessage/webSocketClose to re-hydrate Yjs state after a cold start

Tests

  • Updated 4 existing WebSocket tests to use ctx.acceptWebSocket mock + serializeAttachment
  • Added 6 new tests: cold-start re-hydration, warm-start (no redundant bindState), webSocketClose cleanup, webSocketError cleanup, no-op close on unknown doc, read-only auth restoration

🤖 Generated with Claude Code

@bosschaert
Copy link
Copy Markdown
Contributor

In theory this seems like a good idea. I don't have any experience with the hibernation API so we need to make sure that we test it very well.

@karlpauls any thoughts?

Use ctx.acceptWebSocket() instead of ws.accept() so the Durable Object
survives WebSocket disconnects without losing in-flight Yjs state. Added
webSocketMessage/webSocketClose/webSocketError class methods for CF's
Hibernation routing, with serializeAttachment/deserializeAttachment to
restore auth context after hibernation. setupWSConnection gains a
hibernation flag to skip addEventListener registration when CF handles
routing directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kptdobe kptdobe force-pushed the fix/ws-hibernation-api branch from f0ce60b to 354428d Compare May 11, 2026 07:55
@kptdobe kptdobe requested a review from bosschaert May 11, 2026 12:04
@kptdobe
Copy link
Copy Markdown
Contributor Author

kptdobe commented May 13, 2026

Need more testing, moving to draft PR.

@kptdobe kptdobe marked this pull request as draft May 13, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants