mirror of
https://github.com/tiennm99/rplace.git
synced 2026-10-15 18:16:04 +00:00
fix(canvas): code-review fixes + sync user-facing docs to DO storage
Fixes from code review of canvas-on-do migration (commit c3f7c02):
- worker.js /api/ws: rewrite request URL to '/ws' so the DO pathname
switch dispatches correctly. The original c.req.raw kept '/api/ws'
which the DO never matched → 404 on every WS upgrade.
- migrate-from-upstash.js pickSampleOffsets: use TOTAL_PIXELS - 1 for the
last byte instead of CANVAS_WIDTH * CANVAS_WIDTH (only correct when
the canvas is square; constants explicitly invite non-square).
- chunk-storage.js writePixels: clarify atomicity comment — the loop is
atomic *because it has no awaits*, not because of any implicit DO
transaction. Added guidance for future maintainers.
- cooldown-store.js tryAcquire: GC sweep wrapped in try/catch so a
transient failure can't drop the user's allowed: true response.
Docs:
- README.md: drop Upstash from tech stack, redraw architecture,
document new project layout (durable-objects/lib, admin/), add
CHUNK_BYTES to configuration table.
- docs/system-architecture.md: full rewrite for DO-storage data flow,
document SQLite schema, race-safe rate-limit pattern, free-tier table.
- docs/deployment-guide.md: drop Upstash setup, add optional one-shot
migration runbook, update free-tier table to actual May 2026 limits.
Tests: 112 pass, 6 skipped (pending Phase 4 rewrite via
@cloudflare/vitest-pool-workers). Bundle dry-run clean.
Local wrangler dev smoke test was attempted but the sandboxed env
hangs HTTP requests at the workerd layer (TCP connects, no response).
Routing fix verified by code inspection; user must verify in their
own dev or production.
This commit is contained in:
1 parent
cdf2295ef6
commit
b890dfb3b7
7 files changed
+271
-137
No files matched your search
+82
-43
@@ -3,46 +3,66 @@
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 18+
|
||||
- Cloudflare account (free tier)
|
||||
- Upstash Redis database (free tier)
|
||||
- Cloudflare account (Free plan is sufficient)
|
||||
- `wrangler` CLI (installed as dev dependency)
|
||||
|
||||
## Step 1: Create Upstash Redis Database
|
||||
No external storage to provision. Canvas pixels and rate-limit cooldowns
|
||||
live inside the `CanvasRoom` Durable Object's SQLite-backed storage.
|
||||
|
||||
1. Go to [console.upstash.com](https://console.upstash.com/)
|
||||
2. Create a new Redis database
|
||||
3. Choose a region close to your users
|
||||
4. Copy the **REST URL** and **REST Token**
|
||||
|
||||
## Step 2: Configure Secrets
|
||||
|
||||
```bash
|
||||
# Set secrets in Cloudflare (not in code)
|
||||
npx wrangler secret put UPSTASH_REDIS_REST_URL
|
||||
npx wrangler secret put UPSTASH_REDIS_REST_TOKEN
|
||||
```
|
||||
|
||||
For local development, create `.dev.vars`:
|
||||
|
||||
```
|
||||
UPSTASH_REDIS_REST_URL=https://your-url.upstash.io
|
||||
UPSTASH_REDIS_REST_TOKEN=your-token
|
||||
```
|
||||
|
||||
## Step 3: Deploy
|
||||
## Step 1: Deploy
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run deploy
|
||||
```
|
||||
|
||||
This runs `vite build` (compiles Svelte → dist/) then `wrangler deploy` (uploads Worker + static assets).
|
||||
This runs `vite build` (compiles Svelte → `dist/`) then `wrangler deploy`
|
||||
(uploads the Worker, static assets, and Durable Object class).
|
||||
|
||||
## Step 4: Verify
|
||||
The first deploy applies the `wrangler.json` migration that registers
|
||||
`CanvasRoom` as a SQLite-backed DO class.
|
||||
|
||||
1. Visit your Worker URL (e.g., `https://rplace.your-subdomain.workers.dev`)
|
||||
2. Canvas should load (empty/dark on first visit)
|
||||
3. Select a color, click to place a pixel
|
||||
4. Open a second browser tab — pixel should appear via WebSocket
|
||||
## Step 2: Verify
|
||||
|
||||
1. Visit your Worker URL (e.g., `https://rplace.your-subdomain.workers.dev`).
|
||||
2. Canvas should load (empty / black on first visit — palette index 0).
|
||||
3. Select a color, click to place a pixel.
|
||||
4. Open a second browser tab — pixel should appear via WebSocket.
|
||||
5. `curl -I https://your-url/api/canvas` should report
|
||||
`cf-cache-status: HIT` after a couple of warm requests (10 s edge cache).
|
||||
|
||||
## (Optional) One-Shot Migration from Upstash
|
||||
|
||||
Only if you have an existing Upstash-backed deployment to import.
|
||||
|
||||
```bash
|
||||
# 1. Set credentials for the legacy Upstash instance
|
||||
npx wrangler secret put UPSTASH_REDIS_REST_URL
|
||||
npx wrangler secret put UPSTASH_REDIS_REST_TOKEN
|
||||
|
||||
# 2. Generate and set a migration token
|
||||
npx wrangler secret put MIGRATION_TOKEN
|
||||
# Paste a random 32-byte hex value
|
||||
|
||||
# 3. Deploy
|
||||
npm run deploy
|
||||
|
||||
# 4. Run the import once
|
||||
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
||||
https://your-worker.workers.dev/admin/migrate-from-upstash
|
||||
# Expect: {"ok":true,"bytes_imported":16777216,"samples_checked":N,"mismatches":[]}
|
||||
|
||||
# 5. Verify in browser; wait 7 days as rollback safety
|
||||
# 6. Run Phase 4 cleanup (see plans/260509-2309-canvas-on-do-storage)
|
||||
```
|
||||
|
||||
After Phase 4 cleanup deletes the migration code, also delete the
|
||||
secrets:
|
||||
```bash
|
||||
npx wrangler secret delete UPSTASH_REDIS_REST_URL
|
||||
npx wrangler secret delete UPSTASH_REDIS_REST_TOKEN
|
||||
npx wrangler secret delete MIGRATION_TOKEN
|
||||
```
|
||||
|
||||
## Custom Domain
|
||||
|
||||
@@ -53,23 +73,42 @@ npx wrangler domains add rplace.yourdomain.com
|
||||
|
||||
## Monitoring
|
||||
|
||||
- **Cloudflare dashboard**: Worker analytics, request logs, DO metrics
|
||||
- **Upstash console**: Redis command count, memory usage, latency
|
||||
- **Cloudflare dashboard**:
|
||||
- Workers analytics — requests/day, errors, CPU time
|
||||
- Durable Object metrics — storage size, request rate
|
||||
- Cache analytics — `cf-cache-status` HIT ratio on `/api/canvas`
|
||||
|
||||
## Cost Estimates (Free Tier)
|
||||
## Free-tier Footprint
|
||||
|
||||
| Resource | Free Limit | rplace Usage |
|
||||
| Resource | Free Cap (May 2026) | rplace at hobby scale |
|
||||
|---|---|---|
|
||||
| CF Workers | 100K requests/day | Canvas reads + pixel placements |
|
||||
| CF Durable Objects | Free with Workers | WebSocket connections |
|
||||
| Upstash Redis | 10K commands/day | BITFIELD reads/writes + cooldown SET NX EX |
|
||||
| Workers requests | 100,000 / day | ~100 / day @ 50 users |
|
||||
| DO storage / object | 10 GB | 16 MB canvas |
|
||||
| DO storage / account | 5 GB | 16 MB total |
|
||||
| BLOB row size | 2 MB | 64 KB chunks (32× under) |
|
||||
| Per-DO request rate | 1,000 / s soft | ~1 / s |
|
||||
|
||||
For hobby traffic (< few hundred users/day), free tiers are sufficient. Upstash pay-as-you-go ($0.2/100K commands) is the first thing to hit limits.
|
||||
Bandwidth is unlimited on Workers. With the 10 s edge cache on
|
||||
`/api/canvas`, the dominant request driver is `/api/place` (1 per
|
||||
placement). At 1-req-per-second-per-user rate-limit, 50 concurrent
|
||||
users × 24h × 3600s = 4.3 M theoretical max — but realistic hobby
|
||||
sessions stay well under 100K/day.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Canvas loads empty**: Check Upstash credentials in secrets
|
||||
- **Pixels don't persist**: Verify BITFIELD support — test with `redis-cli BITFIELD rplace:canvas:v2 SET u8 #0 42`
|
||||
- **Old 32-color canvas still visible after deploy**: the canvas key is versioned (`rplace:canvas:v2`). The old `rplace:canvas` key is orphaned — run `redis-cli DEL rplace:canvas` once to reclaim memory.
|
||||
- **WebSocket not connecting**: Ensure Durable Object migration ran (check `wrangler.json` migrations)
|
||||
- **Rate limiting not working**: Verify `SET key value NX EX 1` returns `"OK"` / `null` as expected on your Upstash tier
|
||||
- **Canvas loads empty**: expected on first deploy — DO `canvas_chunks`
|
||||
table is empty until pixels are placed (or migration runs).
|
||||
- **WebSocket not connecting**: verify the wrangler migration applied
|
||||
via `wrangler tail` — should see no errors on DO instantiation.
|
||||
- **`cf-cache-status` shows MISS**: edge caching may need an extra
|
||||
`caches.default.put` wrap if `Cache-Control` headers aren't honored
|
||||
through the worker → DO → response chain. Verify with two consecutive
|
||||
`curl -I` requests; second should HIT.
|
||||
- **Migration import fails with `size_mismatch`**: the legacy Upstash
|
||||
data isn't 16 MB. Resize CHUNK constants or delete the partial Upstash
|
||||
data and start fresh.
|
||||
- **`already_populated` from migration endpoint**: pass `?force=1` to
|
||||
overwrite. Use only when you're certain.
|
||||
- **Storage billing meter ticking up**: per-account 5 GB free cap. A
|
||||
16 MB canvas is harmless; the worry only appears if you stand up many
|
||||
rooms or hit a runaway insert.
|
||||
Reference in new issue
Block a user