Files
rplace/docs/canvas-resize-procedure.md
T
tiennm99 c3f7c02f6d feat(canvas): migrate canvas + cooldown storage to DO SQLite
Move pixel state and rate-limit cooldowns out of Upstash Redis and into
the existing CanvasRoom Durable Object's SQLite-backed storage. Worker
becomes a thin validation/proxy; the DO does atomic cooldown check +
pixel write + WS broadcast in one in-memory step.

Why: eliminate external dependency, keep $0/month free-tier forever,
exploit single-threaded actor for strong consistency without round-trips.

Architecture:
- canvas_chunks: 256 BLOB rows × 64 KB; CHUNK_COUNT derived from
  CANVAS_WIDTH × CANVAS_HEIGHT / CHUNK_BYTES so resize is config-only.
- cooldowns: user_id → expires_at, 1% sample-rate lazy GC.
- Worker forwards /api/canvas, /api/place, /api/ws to DO endpoints.
- POST /admin/migrate-from-upstash: token-gated one-shot importer.

Phases 1-3 code complete; Upstash dependency stays until prod migration
runs and 7-day rollback window passes (Phase 4).

Plan: plans/260509-2309-canvas-on-do-storage/
2026-05-09 23:53:21 +07:00

1.7 KiB
Raw Blame History

Canvas Resize Procedure

The canvas dimensions are driven by two constants. Storage chunks auto-derive, so resizing is a config change followed by a redeploy — no migration code needed.

Steps

  1. Edit src/lib/constants.js:

    export const CANVAS_WIDTH  = 8192;   // was 4096
    export const CANVAS_HEIGHT = 8192;   // was 4096
    

    TOTAL_PIXELS and CHUNK_COUNT recompute automatically. Bumping to 8192×8192 raises CHUNK_COUNT from 256 to 1024 (still well under the 1 GB single-DO limit, which would allow ~32K×32K).

  2. Build and deploy:

    npm run deploy
    
  3. The DO lazy-initializes any missing chunks on the next read. New bytes are zero-filled (palette index 0 — pure black). Existing pixels keep their (x, y) coordinates; the canvas just becomes larger around them.

Caveats

  • Shrinking the canvas leaves orphan chunk rows past the new CHUNK_COUNT. Existing reads are unaffected (they only iterate up to the new limit), but storage usage stays high until they're cleaned up. To reclaim: connect to the DO and run DELETE FROM canvas_chunks WHERE chunk_id >= NEW_CHUNK_COUNT.
  • Aspect ratio change (non-square) is fine. The 1-D byte-layout (y * CANVAS_WIDTH + x) still holds. Just make sure clients pull the new constants too — frontend reads them from the same module.
  • Storage cap. SQLite-backed DO storage is 1 GB on the Free plan. A 1-byte-per-pixel canvas fits up to roughly 32,768 × 32,768 before hitting that ceiling.

Free-tier monitoring

After resize, watch:

  • Cloudflare Workers requests/day (free cap: 100,000)
  • Durable Object storage size (free cap: 1 GB per DO)

Both visible on the Cloudflare dashboard for the rplace project.