Skip to content

feat: add Cloudflare cache purge by tag for app updates#1511

Merged
riderx merged 4 commits into
mainfrom
riderx/cache-limits-check
Jan 27, 2026
Merged

feat: add Cloudflare cache purge by tag for app updates#1511
riderx merged 4 commits into
mainfrom
riderx/cache-limits-check

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented Jan 27, 2026

Summary

Add Cache-Tag headers to cached responses in the Cloudflare Snippet worker, enabling global cache purge via Cloudflare API. When a new app version is uploaded, purge all cached responses for that app across all zones to prevent stale 429 responses from blocking updates.

Test plan

  • Deploy and verify CF_CACHE_PURGE_TOKEN and CF_CACHE_PURGE_ZONE_IDS env vars are set
  • Upload a new app version and confirm cache is purged via Cloudflare logs
  • Verify plan-upgrade and on-prem cached responses are invalidated on version creation

Checklist

  • Code follows project style
  • Manual testing completed (deploy required)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Cache responses now include per-app tags (on-prem and plan upgrade) and propagate these tags into TTL metadata.
    • Automatic background cache purges for on-prem and plan caches are triggered on app creation, version creation, and plan-related events.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 27, 2026

Warning

Rate limit exceeded

@riderx has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 16 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📝 Walkthrough

Walkthrough

Adds per-app Cache-Tag headers in Cloudflare worker cache flows and a new backend utility to purge those per-app tags via Cloudflare API; integrates purge calls as background tasks in app/version lifecycle and billing webhook handlers.

Changes

Cohort / File(s) Summary
Cloudflare worker cache tagging
cloudflare_workers/snippet/index.js
Introduces local cacheTags in setOnPremCache and setPlanUpgradeCache, setting Cache-Tag headers on on‑prem and plan‑upgrade cache responses and propagating the tag into TTL metadata for on‑prem.
Cloudflare purge utility (new)
supabase/functions/_backend/utils/cloudflare_cache_purge.ts
New module exporting buildOnPremCacheTag, buildPlanCacheTag, purgeOnPremCache, purgePlanCache, purgePlanCacheForOrg. Implements purgeByTags using env vars CF_CACHE_PURGE_TOKEN and CF_CACHE_PURGE_ZONE_IDS, per-zone POSTs to Cloudflare purge API, logging and error handling.
Triggers — invoke purge on events
supabase/functions/_backend/triggers/on_version_create.ts, supabase/functions/_backend/triggers/on_app_create.ts, supabase/functions/_backend/triggers/stripe_event.ts
Import and schedule background tasks to call purgeOnPremCache after app creation/version creation and purgePlanCacheForOrg after relevant billing/plan events. Uses existing backgroundTask mechanism so purges run asynchronously.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/App
    participant CF_Worker as Cloudflare Worker
    participant Supabase as Supabase Backend
    participant CF_API as Cloudflare API

    Client->>CF_Worker: Request endpoint
    CF_Worker->>CF_Worker: Generate response\nset Cache-Tag: "app:<id>" (on-prem/plan)
    CF_Worker-->>Client: Response with Cache-Tag header

    Client->>Supabase: Create new version / create app / billing event
    Supabase->>Supabase: Update DB (apps table)
    Supabase->>Supabase: Schedule backgroundTask(purgeOnPremCache / purgePlanCacheForOrg)
    Supabase->>CF_API: POST /client/v4/zones/<zone>/purge_cache { "tags": [...] } (parallel per zone)
    CF_API-->>Supabase: 200/response
    Supabase-->>Client: Complete request (notifications / webhooks continue)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I hopped through headers, left my mark,

app‑tags glowing in the dark,
When versions shift and plans renew,
I nudge the purge to clear the view,
Fresh cache bound — a bunny's spark ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main feature: adding Cloudflare cache purge functionality triggered by tag, which matches the primary changes across all modified files.
Description check ✅ Passed The PR description includes a clear summary and test plan, though the manual testing checkbox is unchecked. The description adequately covers the changes and testing strategy but lacks complete checklist compliance.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@supabase/functions/_backend/triggers/on_version_create.ts`:
- Around line 11-12: The import order violates the perfectionist/sort-imports
rule; swap the two import statements so that purgeAppCacheTags is imported
before backgroundTask (i.e., import { purgeAppCacheTags } from
'../utils/cloudflare_cache_purge.ts' appears above import { backgroundTask }
from '../utils/utils.ts') to satisfy the linter.

Comment on lines +11 to +12
import { backgroundTask } from '../utils/utils.ts'
import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix import order to satisfy perfectionist/sort-imports.

CI reports the new import should precede ../utils/utils.ts (Line 11-12).

🔧 Suggested fix
-import { backgroundTask } from '../utils/utils.ts'
-import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
+import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
+import { backgroundTask } from '../utils/utils.ts'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { backgroundTask } from '../utils/utils.ts'
import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
import { backgroundTask } from '../utils/utils.ts'
🧰 Tools
🪛 GitHub Actions: Run tests

[error] 12-12: perfectionist/sort-imports: Expected '../utils/cloudflare_cache_purge.ts' to come before '../utils/utils.ts'.

🤖 Prompt for AI Agents
In `@supabase/functions/_backend/triggers/on_version_create.ts` around lines 11 -
12, The import order violates the perfectionist/sort-imports rule; swap the two
import statements so that purgeAppCacheTags is imported before backgroundTask
(i.e., import { purgeAppCacheTags } from '../utils/cloudflare_cache_purge.ts'
appears above import { backgroundTask } from '../utils/utils.ts') to satisfy the
linter.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@supabase/functions/_backend/utils/cloudflare_cache_purge.ts`:
- Around line 95-101: The purgePlanCacheForOrg function currently uses
supabaseAdmin to fetch apps; replace that with the project DB client by calling
getPgClient() or getDrizzleClient() inside purgePlanCacheForOrg and run a SELECT
app_id FROM apps WHERE owner_org = $1 (or equivalent drizzle query) using orgId
as the parameter, then map/iterate the returned rows the same way you did with
the previous data; ensure you handle query errors/empty results consistently
(same variable names like apps or rows) and remove the supabaseAdmin
import/usage in this function.
- Around line 59-61: The code reads response.json().catch(() => null) into
result and then accesses result.success which fails TypeScript strict checks;
fix by narrowing or asserting the JSON shape before property access: replace the
current assignment with a typed assertion or safe narrow, e.g. set result using
const result = (await response.json().catch(() => null)) as { success?: boolean
} | null; or alternatively check typeof result === 'object' && 'success' in
result before using result.success; update the location where cloudlogErr is
called (same block) to use the narrowed/typed result so TypeScript strict mode
is satisfied.

Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts Outdated
Comment on lines +95 to +101
export async function purgePlanCacheForOrg(c: Context, orgId: string) {
// Get all app_ids for this org
const { data: apps, error } = await supabaseAdmin(c)
.from('apps')
.select('app_id')
.eq('owner_org', orgId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use getPgClient/getDrizzleClient for the org apps lookup.

This new DB access uses supabaseAdmin, but backend guidelines require PostgreSQL access through getPgClient()/getDrizzleClient() during the D1 migration. Please refactor this query accordingly. As per coding guidelines, ...

🤖 Prompt for AI Agents
In `@supabase/functions/_backend/utils/cloudflare_cache_purge.ts` around lines 95
- 101, The purgePlanCacheForOrg function currently uses supabaseAdmin to fetch
apps; replace that with the project DB client by calling getPgClient() or
getDrizzleClient() inside purgePlanCacheForOrg and run a SELECT app_id FROM apps
WHERE owner_org = $1 (or equivalent drizzle query) using orgId as the parameter,
then map/iterate the returned rows the same way you did with the previous data;
ensure you handle query errors/empty results consistently (same variable names
like apps or rows) and remove the supabaseAdmin import/usage in this function.

riderx and others added 3 commits January 27, 2026 22:09
Add Cache-Tag headers to on-prem and plan-upgrade responses in the Cloudflare Snippet worker, enabling global cache purge via Cloudflare API. When a new app version is created, purge all cached responses for that app across all zones to prevent stale 429 responses from blocking updates.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
… triggers

- On-prem cache uses `app-onprem:{appId}` tag, purged on app create and version create
- Plan cache uses `app-plan:{appId}` tag, purged on Stripe payment success
- Added purgePlanCacheForOrg to purge all apps in an org when subscription succeeds

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes TypeScript error: Property 'success' does not exist on type '{}'

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@riderx riderx force-pushed the riderx/cache-limits-check branch from 9f51d91 to f502f57 Compare January 27, 2026 22:09
Skip cache purge when not running on workerd (Cloudflare Workers)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx merged commit 15c72f2 into main Jan 27, 2026
11 checks passed
@riderx riderx deleted the riderx/cache-limits-check branch January 27, 2026 22:24
jokabuyasina pushed a commit to jokabuyasina/capgo that referenced this pull request Feb 7, 2026
* feat: add Cloudflare cache purge by tag for app updates

Add Cache-Tag headers to on-prem and plan-upgrade responses in the Cloudflare Snippet worker, enabling global cache purge via Cloudflare API. When a new app version is created, purge all cached responses for that app across all zones to prevent stale 429 responses from blocking updates.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* refactor: separate on-prem and plan cache tags with appropriate purge triggers

- On-prem cache uses `app-onprem:{appId}` tag, purged on app create and version create
- Plan cache uses `app-plan:{appId}` tag, purged on Stripe payment success
- Added purgePlanCacheForOrg to purge all apps in an org when subscription succeeds

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

* fix: add type assertion for Cloudflare API response

Fixes TypeScript error: Property 'success' does not exist on type '{}'

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

* fix: only run cache purge on Cloudflare Workers runtime

Skip cache purge when not running on workerd (Cloudflare Workers)

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

---------

Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
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.

1 participant