Skip to content

Fix mixin type mismatch and document the untested hibernation wake path#24

Merged
grrowl merged 2 commits into
mainfrom
fix/syncable-followups
Jul 2, 2026
Merged

Fix mixin type mismatch and document the untested hibernation wake path#24
grrowl merged 2 commits into
mainfrom
fix/syncable-followups

Conversation

@grrowl

@grrowl grrowl commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

This closes out two of three findings from a Codex review pass on the Syncable mixin work (PR #22 / #23). Here is where each finding landed.

Finding 1, the missing ORDER BY rowid guard on WITHOUT ROWID tables. This was real, but it was already fixed on main before this branch was cut, in PR #23 (commits ef4903a and 8f5005c). assertSyncCompatible now rejects a WITHOUT ROWID table and a table with a shadowing rowid column, both with a clear error at registerSync, and ADR-0015 already carries the corrected ordering note. No new work needed here. This PR just carries that history forward.

Finding 2, the mixin's declared WebSocket handler types did not match what they return. This was real and small. In src/server/mixin.ts, the SyncMixin interface declared webSocketClose and webSocketError as returning Promise, but the actual overrides return void synchronously since there is nothing to await, they just drop bookkeeping for a closed socket. This is now fixed. Both are retyped to void or Promise, which matches how Cloudflare's own DurableObject interface types these two handlers. webSocketMessage is genuinely async and was left alone. I checked the built dist/server/mixin.d.ts and confirmed it now emits the corrected signature.

Finding 3, the host-matrix test suite never forces a real hibernation wake. This is a real gap and I could not close it honestly. The cohosting guarantee depends on the mixin's constructor restoring only tagged sockets after a wake, but the in-CI suite only asserts the tag partition on a single live instance, it never tears the instance down and reconstructs it the way a real hibernation eviction would. I looked into whether the test tooling can simulate this. Cloudflare's vitest-pool-workers does now ship evictDurableObject and evictAllDurableObjects helpers that do exactly this, gracefully evicting a Durable Object while preserving storage and hibernating its WebSockets, then letting you observe the reconstructed instance. But those helpers only landed in vitest-pool-workers 0.16.20, which requires vitest 4. This repo is pinned to vitest-pool-workers 0.12.21 against vitest 3.2.4, so using them means a major vitest upgrade, which is out of scope for a small test-gap fix. Rather than fake this coverage, I added an honest comment in tests/host-matrix.test.ts explaining the gap and a matching sentence in ADR-0015's Consequences section naming the wake-time restore call site and the upgrade this is blocked on.

Test plan

  • Full suite green: 193 tests across 43 files, up from 191
  • npm run typecheck passes
  • npm run build passes, and I confirmed the emitted mixin.d.ts carries the corrected webSocketClose/webSocketError types

🤖 Generated with Claude Code

grrowl and others added 2 commits July 2, 2026 23:27
webSocketClose and webSocketError were declared as returning Promise<void>
while the mixin's overrides return void synchronously (nothing to await --
both just drop bookkeeping for the closed socket). Retype both to
void | Promise<void>, matching @cloudflare/workers-types's own DurableObject
interface and the actual implementation. webSocketMessage is genuinely async
and is unchanged. Verified the emitted dist/server/mixin.d.ts reflects the
new signature after build; no runtime behavior changes.

Codex review finding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The host-matrix suite (ADR-0015) pins the cohosting tag-partition contract
on a single live instance, but never forces a real hibernation eviction, so
the wake-time restore itself (getWebSockets(SYNC_TAG) in the mixin's
constructor) is verified by code-reading only, not by an automated test.

Investigated whether @cloudflare/vitest-pool-workers can simulate that:
evictDurableObject/evictAllDurableObjects do exactly this (graceful evict,
storage preserved, WebSockets hibernated not closed, instance reconstructed)
but only from 0.16.20 onward, which requires vitest@^4 -- this repo pins
vitest-pool-workers 0.12.21 against vitest 3.2.4, so pulling the helpers in
means a major vitest bump. That is out of scope for this fix, so rather than
fake the coverage, add an honest comment in the test file plus one ADR-0015
Consequences sentence naming the gap and the upgrade it is blocked on.

Codex review finding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@grrowl grrowl merged commit 414beb8 into main Jul 2, 2026
1 check passed
@grrowl grrowl deleted the fix/syncable-followups branch July 2, 2026 15:37
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.

1 participant