Rebasely Beta: data may be purged

examples/live-notes Public

Markdown · 2.4 KiB · at 6e3dc76

id: note-room
type: concept
owner: justin
reviewed: 2026-10-15
component: note-room
describes:
  - src/note-room.ts
  - src/room.ts
  - src/index.ts

The note room

Each note is one Durable Object of class NoteRoom, found with idFromName(noteId) through the NOTES binding. Everyone who opens the same note id reaches the same object, wherever they are, and that object is the single sequencer described in [[conflict-strategy]].

Two layers

NoteRoom (src/note-room.ts) is the part that knows about the runtime: it accepts sockets, reads frames, sends replies and broadcasts, and owns the SQLite storage. Everything it decides is delegated to Room (src/room.ts), which takes a NoteStore and returns, for each message, a reply for the sender and an optional broadcast for the others. The tests drive Room with an in-memory store, so they run in plain Node without workerd.

Storage

Two tables in the object's SQLite database:

  • note: one row with the text and its version. Room caps the text at 100,000 UTF-16 code units, so the row and every snapshot frame stay small.
  • ops: the op log, keyed by the version each op produced, trimmed to the last 200.

commit() writes the new text, appends the op and trims the log inside one transactionSync, so a note and its log never disagree. The room only updates its in-memory copy after the write.

Hibernation

Sockets are accepted with ctx.acceptWebSocket, the Hibernation API. When nobody has typed for a while the runtime evicts the object but keeps the sockets open; the next frame wakes it, runs the constructor, and calls webSocketMessage. The constructor creates the tables if needed and loads the note, so waking up is the same as starting fresh. Nothing the room needs lives only in memory.

Broadcasting uses ctx.getWebSockets(), which lists every accepted socket even after a wake, so there is no connection list to rebuild.

Each socket also carries its editor (an id, a name and a color) as a serialized attachment, set right after acceptWebSocket. Attachments survive hibernation, so after a wake the room can still say who sent a frame and who is here without reading storage. [[presence]] covers the rest.

Reads without a socket

GET /notes/:id calls the read() RPC method and returns { id, text, version }. It reads the room's in-memory copy, which is always the last committed state.