Skip to content

fix(web): a vote flushes only its product's caches, and only when it changes - #1320

Merged
JOY (JOY) merged 3 commits into
devfrom
fix/1306-vote-revalidation
Oct 29, 2026
Merged

JOY (JOY) merged 3 commits into
devfrom
fix/1306-vote-revalidation

Conversation

@JOY

@JOY JOY (JOY) commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Refs #1306 (web part; the DB guard change is proposed on the issue for JOY, so the issue stays open)

From the apps/web security review. Severity: medium (load amplification through cache flushes; no data exposure).

What (app/api/reviews/[id]/vote/route.ts)

  • The voter's stored vote is read first (their own row, RLS). The same vote again is not written and flushes nothing. A report over a stored up/down vote counts as a change, because the report clears the vote.
  • A vote that changes counts revalidates only its product's caches: the new per-master review tag (masterReviewsCacheTag, added to the review reader and summary caches in server/rate/index.ts) and the product page tag. It no longer flushes the global products, stats or reviews tags.
  • Cache flushes are throttled per voter (6 per minute per instance). Votes past that still count; the caches catch up on their own interval (300 s for reviews and products, 600 s for stats).

Tests

  • write-paths.test.ts: an unchanged vote reads the stored vote and is not written; a report over an up-vote is written.
  • core-rate-architecture.test.ts: the vote route revalidates the per-product tags, not the global ones.

Review gate

Codex adversarial, 2 rounds (cap 2-3):

  • Round 1: the global reviews tag was still flushed, and a report over a vote was treated as unchanged. Both fixed in f820f51.
  • Round 2: no new issue. It also checked that the remaining caches (leaderboard, stats, merchant pages) refreshing on their interval is an acceptable trade-off, and that the revalidate allowlist accepts the new tag.

Checks

Web pnpm test, tsc --noEmit, eslint, prettier: green locally.

🤖 Generated with Claude Code


📌 TL;DR

This PR optimizes the review voting API to prevent cache stampedes by replacing broad global cache invalidations with targeted, rate-limited revalidation of specific product and review tags. It also introduces logic to skip database writes and cache flushes entirely when a user submits a duplicate vote.

🎯 Type of Change

  • 🚀 New feature
  • 🐛 Bugfix
  • 🧹 Refactor
  • ⚡ Performance
  • 📚 Documentation
  • ⚙️ CI / Configuration

🔍 Changes Walkthrough

File Summary of Changes
apps/web/src/app/api/reviews/[id]/vote/route.ts Added a BoundedSlidingWindowRateLimiter to throttle cache revalidations per voter. Implemented a "same vote" check to skip DB writes if the vote hasn't changed. Replaced global RATE_CACHE_TAGS invalidation with targeted reviewProductTags resolution.
apps/web/src/server/rate/cache-tags.ts Added masterReviewsCacheTag helper to generate specific cache tags for a product's reviews, enabling granular invalidation.
apps/web/src/server/rate/index.ts Updated getCachedMasterProductReviewsByKey and getCachedMasterProductReviewSummary to include the new masterReviewsCacheTag in their cache tag arrays.
apps/web/src/app/api/v1/rate/__tests__/write-paths.test.ts Added tests to verify that duplicate votes do not trigger DB writes or cache flushes, and that reporting over an existing vote correctly triggers a write.
apps/web/src/server/rate/__tests__/core-rate-architecture.test.ts Updated architectural assertions to ensure the vote route uses targeted cache tags (masterReviewsCacheTag, productCacheTag) instead of global tags (RATE_CACHE_TAGS.REVIEWS, RATE_CACHE_TAGS.PRODUCTS).

📊 Architectural Flow

sequenceDiagram
    participant Client
    participant VoteAPI as Vote API Route
    participant DB as Database
    participant Cache as Next.js Cache

    Client->>VoteAPI: POST /api/reviews/[id]/vote
    VoteAPI->>DB: Check existing vote (RLS)
    alt Vote is unchanged
        VoteAPI-->>Client: 200 OK (No write, no cache flush)
    else Vote is changed
        VoteAPI->>DB: Write/Update vote
        VoteAPI->>VoteAPI: Check Rate Limiter (per voter)
        alt Rate Limit Passed
            VoteAPI->>DB: Resolve Product & Master Product IDs
            VoteAPI->>Cache: Revalidate specific tags (Product, Master Reviews)
        else Rate Limit Exceeded
            VoteAPI->>VoteAPI: Skip revalidation (Cache catches up on interval)
        end
        VoteAPI-->>Client: 200 OK
    end
Loading

JOY (JOY) and others added 3 commits October 29, 2026 02:28
…changes

#1306 (web part). Every vote request revalidated the global
products, stats and reviews tags, and the database limit counts rows per
review, so repeated actions on one review could keep the catalogue
caches cold.
- the stored vote is read first: the same vote again is not written and
  flushes nothing;
- a changed vote revalidates the review caches and its product's page
  tag, not the global products and stats tags;
- flushes are throttled per voter (6 per minute per instance); votes
  past that still count and the caches catch up on their own interval.
The database guard change is proposed on the issue for JOY.

Refs #1306

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #1306

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex round 1 on #1306:
- review readers and summaries are also tagged per master
  (masterReviewsCacheTag); a vote revalidates that tag and the product's
  page tag, no longer the global reviews tag;
- a report over a stored up/down vote counts as a change (the report
  clears the vote), so it is written.

Refs #1306

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, someone with write access to this repository can comment @claude review on this pull request to trigger a review.

@vercel

vercel Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
rate Ready Ready Preview Oct 28, 2026 7:42pm UTC

Request Review

@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 53 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 47faad1e-5d25-4b9c-a47e-055545efeea3
Commits

Reviewing files that changed from the base of the PR and between 4eca23d and f820f51.

Files selected for processing: 5
  • apps/web/src/app/api/reviews/[id]/vote/route.ts
  • apps/web/src/app/api/v1/rate/__tests__/write-paths.test.ts
  • apps/web/src/server/rate/__tests__/core-rate-architecture.test.ts
  • apps/web/src/server/rate/cache-tags.ts
  • apps/web/src/server/rate/index.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

dos[bot]
dos Bot approved these changes Oct 28, 2026

@dos dos Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏱️ Code Review completed (5 eligible files · 10,900 chars · 1 model call(s) · 1 PR unit(s))

Whole diff with bounded context at base 4eca23d / head f820f51.

✅ CLEAN_PASS: Complete diff reviewed

@JOY
JOY (JOY) merged commit f39ad3b into dev Oct 29, 2026
18 checks passed
@JOY
JOY (JOY) deleted the fix/1306-vote-revalidation branch October 29, 2026 00:57

This branch was successfully deployed

1 active deployment
Preview — f820f513 Deployed Oct 28, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:frontend Frontend / Next.js / UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant