Skip to content

Make likes system robust: denormalized counter + optimistic UI#85

Merged
injoon5 merged 2 commits into
mainfrom
claude/likes-system-architecture-PtJP2
May 26, 2026
Merged

Make likes system robust: denormalized counter + optimistic UI#85
injoon5 merged 2 commits into
mainfrom
claude/likes-system-architecture-PtJP2

Conversation

@injoon5
Copy link
Copy Markdown
Owner

@injoon5 injoon5 commented May 26, 2026

Reads no longer collect every like row to count. A denormalized
likeCounts table (mirroring commentUrlCounts) keeps the count O(1),
and get reads only the counter doc plus the visitor's single
membership row, so a viewer's realtime subscription re-runs only
when the count or their own like changes.

The toggle mutation is replaced with an idempotent setLike(liked):
rapid clicks and double-fires converge on the final desired state
instead of racing, and idempotent re-sends consume no rate-limit
token. liked omitted still toggles for backward compatibility.

The button now updates optimistically and serializes writes (one
in flight, always converging on the latest intent), reconciling
against the realtime query, so like/unlike feels instant and never
drifts out of sync. Adds a likeCounts backfill job + migration flag
with a read-time fallback so counts stay correct before backfill.

https://claude.ai/code/session_01BcpfW29BRVq2KEJYypLVs6

claude added 2 commits May 26, 2026 07:21
Reads no longer collect every like row to count. A denormalized
likeCounts table (mirroring commentUrlCounts) keeps the count O(1),
and get reads only the counter doc plus the visitor's single
membership row, so a viewer's realtime subscription re-runs only
when the count or their own like changes.

The toggle mutation is replaced with an idempotent setLike(liked):
rapid clicks and double-fires converge on the final desired state
instead of racing, and idempotent re-sends consume no rate-limit
token. liked omitted still toggles for backward compatibility.

The button now updates optimistically and serializes writes (one
in flight, always converging on the latest intent), reconciling
against the realtime query, so like/unlike feels instant and never
drifts out of sync. Adds a likeCounts backfill job + migration flag
with a read-time fallback so counts stay correct before backfill.

https://claude.ai/code/session_01BcpfW29BRVq2KEJYypLVs6
Replace the single-shot flush with a serialized sync loop that keeps
the request in flight until the server is driven to the latest intent.
Because inFlight stays true across every send, the reconcile effect
(gated on !inFlight) can only clear the optimistic override after the
server actually reflects the intent — so a fast like/unlike during a
request can no longer settle the button opposite to the server.

On any failure the optimistic state reverts to the server value and an
error is shown. Navigating mid-request abandons the old page's intent
while still letting the in-flight write persist, and a new intent that
arrives while the lock is held is re-driven once it releases.

https://claude.ai/code/session_01BcpfW29BRVq2KEJYypLVs6
@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
web Error Error May 26, 2026 7:30am

@injoon5 injoon5 merged commit 255042f into main May 26, 2026
1 of 3 checks passed
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.

2 participants