Skip to content
This repository was archived by the owner on Oct 3, 2026. It is now read-only.

Make Film Club room mutations atomic without changing stored history - #29

Closed
WalksWithASwagger wants to merge 1 commit into
mainfrom
cursor/atomic-club-room-mutations-19f6
Closed

WalksWithASwagger wants to merge 1 commit into
mainfrom
cursor/atomic-club-room-mutations-19f6

Conversation

@WalksWithASwagger

@WalksWithASwagger WalksWithASwagger commented Sep 20, 2026 •

Copy link
Copy Markdown
Owner

Closes #18.

Outcome

Film Club create/join/submit/delete no longer use unconstrained whole-room read/modify/write. Routes call a small atomic store API so concurrent successful writes are preserved, capacity and create-if-absent are enforced in the mutation, and a submit racing a delete cannot resurrect a room.

Stored history is unchanged: same room:{code} keys, same room JSON shape, no TTL, no key migration, no production rewrite.

Approach

  • Memory store: per-code lock plus cloned snapshots.
  • Redis: Upstash REST cannot WATCH, so this uses the official equivalent compare-and-set path — SET NX for create-if-absent, and a bounded Lua CAS (createScript / EVALSHA). Lua does not re-encode rooms with cjson, so empty arrays and existing critiques are not rewritten by the script.
  • Authorization (member token / owner token) is rechecked inside the mutation.
  • Submit still 404s/403s before rate-limit, matching the previous client-facing order, then writes atomically.
  • Bounded retries only: 5 create-code attempts, 8 CAS attempts. Failures return 409/503 without logging private payloads.
  • Production unconfigured-store behavior is unchanged (isStoreConfigured()).

Route consumers enumerated

Route Old write API New API
GET /api/club/rooms/[code] get get (detached snapshot)
POST /api/club/rooms get + set createIfAbsent
POST .../join get + set joinMember
POST .../submit get + set (could fall back to a stale room after delete) appendSubmission
DELETE /api/club/rooms/[code] get + del deleteIfOwner

set/del are no longer on the write API.

Tests

Deterministic interleavings covered at store and actual route level: join/join, submit/submit, join/submit, submit/delete, create collision, duplicate member update, capacity race, snapshot isolation, legacy critique readability, CAS retry/conflict, and safe unavailable errors.

The Redis path is exercised by an in-process CAS fake (SET NX + Lua-shaped compare-and-set). That is not live Redis concurrency evidence.

Focused command:

npx tsx --test tests/club-store.test.ts tests/club-rooms.test.ts tests/club-submit.test.ts

Verification

Check Result
npm run typecheck pass
npm run lint pass
npm test pass, 63 Node tests
Focused Club tests pass, 34 tests (memory + fake CAS + routes + 1 Redis skip)
npm run build pass
npm run test:browser pass, 36 Chromium checks (desktop + mobile). First attempt needed npx playwright install chromium in this environment.
Redis integration (CLUB_TEST_REDIS_URL + CLUB_TEST_REDIS_TOKEN + CLUB_TEST_NAMESPACE) unverified — no dedicated test Redis in this environment. Memory-only and fake-CAS passes are not Redis concurrency evidence. Command and skip are documented in docs/film-club.md.

Smoke/search checks were not run; #18 does not require them and they need a live server.

Hard gates

  • No data migration or history rewrite.
  • No secrets committed or logged.
  • Do not merge under this packet.

Rollback

A code revert restores the old racy writers against the same keys and shape. It does not rewrite stored rooms. Rooms mutated after deploy are still valid legacy JSON.

Open in Web Open in Cursor 

Replace whole-room get/set/del writes with create-if-absent, join, append, and owner-delete operations. Redis uses SET NX plus bounded Lua compare-and-set because Upstash REST cannot WATCH; memory uses per-code locks and detached snapshots. Stored room:{code} JSON is unchanged.

Closes #18

Co-authored-by: Kris Krüg <WalksWithASwagger@users.noreply.github.com>
@vercel

vercel Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
wedges Ready Ready Preview Sep 20, 2026 10:57pm UTC

Request Review

@WalksWithASwagger

Copy link
Copy Markdown
Owner Author

Duplicate of #28 (already merged; Closes #18). Closing this later draft as superseded.

@WalksWithASwagger
WalksWithASwagger deleted the cursor/atomic-club-room-mutations-19f6 branch September 27, 2026 07:05

This branch was successfully deployed

1 active deployment
Preview — 7c73068d Deployed Sep 20, 2026 by vercel[bot]
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Film Club room mutations atomic without changing stored room history

2 participants