fix(plugin): on_premise Retry-After 1h, soften channel limits, edge retain - #2865
fix(plugin): on_premise Retry-After 1h, soften channel limits, edge retain#2865riderx wants to merge 8 commits into
Conversation
Return Retry-After / X-RateLimit-Reset / Cache-Control (default 24h) on on_premise_app 429s so clients back off. Snippet caches and refreshes those headers so the worker is skipped for the remaining window. Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThe pull request centralizes on-premise rate-limit responses, updates channel-self limits, adds reset-aware enumeration responses, and makes Cloudflare caching preserve and refresh rate-limit metadata. Subscription updates now purge organization on-premise caches. ChangesOn-premise rate-limit handling
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant CloudflareWorker
participant EdgeCache
participant SupabaseFunction
Client->>CloudflareWorker: Request protected resource
CloudflareWorker->>EdgeCache: Read cached response
EdgeCache-->>CloudflareWorker: Cached response or miss
CloudflareWorker->>SupabaseFunction: Fetch on-premise response on miss
SupabaseFunction-->>CloudflareWorker: 429 with retry and reset metadata
CloudflareWorker->>EdgeCache: Store response with derived TTL
CloudflareWorker-->>Client: Response with refreshed rate-limit headers
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4f80b221-0c3e-4956-9405-d7a4b51c76fd) |
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
All reported issues were addressed across 11 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Soften per-device per-operation burst ceiling from 5/s to 10/s to reduce accidental 429s for aggressive old plugin clients. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_2a4a78fd-d2b7-4327-80c9-cfa85fb73104) |
Reduce identical setChannel backoff from 5s to 1s so aggressive old clients are less likely to hit a sticky 429 latch. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4316ee74-5891-4cb3-b756-3b0f6faaae37) |
24h was heavier than needed for edge/client backoff; 1h still damps bad on-prem traffic while allowing faster recovery from misclassification. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_d48218d5-4203-4c7b-8a0d-d9b59d2fdb25) |
- Enumeration 429 stays private/no-store with the real 15m resetAt so it cannot poison the app-keyed public edge cache - Batch /stats on-prem returns top-level onPremiseAppResponse - Purge on-prem + plan edge caches when Stripe plan recovery succeeds - Snippet writes Cache-Control + absolute X-RateLimit-Reset on put and refreshes Retry-After from that reset on cache hits Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f832b0e8-7098-432a-bc6d-68951cb23e1a) |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@cloudflare_workers/snippet/index.js`:
- Around line 198-215: Update withFreshRateLimitHeaders to derive Cache-Control
from the valid X-RateLimit-Reset value and remaining retry duration: set it to
no-store when the reset time has passed, otherwise set it to a max-age matching
the remaining seconds. Preserve the existing Retry-After and reset-header
updates, and leave behavior unchanged when the reset header is missing or
invalid.
In `@supabase/functions/_backend/plugin_runtime/plugins/stats.ts`:
- Around line 307-313: Preserve the existing /stats API contracts in the result
handling around onPremiseAppResponse and BRES: keep need_plan_upgrade returning
its original payload meaning and status rather than mapping it to
on_premise_app. For batch responses, retain the existing per-event successful
result format instead of replacing it with a top-level 429. Detect the plugin
version before selecting the new response format, using the legacy payload and
status for older or unsupported versions.
In `@supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts`:
- Around line 38-51: Keep the cached rate-limit payload synchronized with
refreshed headers in onPremiseAppResponse and withFreshRateLimitHeaders. Update
both supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts lines
38-51 and supabase/functions/_backend/utils/rateLimitInfo.ts lines 38-51 so
cache hits rewrite moreInfo.retryAfterSeconds and its reset metadata using the
refreshed values, or consistently make headers the sole retry metadata source;
add or update tests to verify the response payload matches the refreshed
headers.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f648add7-562c-4adb-9f1f-22a34acb01c3
📒 Files selected for processing (19)
cloudflare_workers/snippet/index.jssupabase/functions/_backend/files/files.tssupabase/functions/_backend/plugin_runtime/plugins/channel_self.tssupabase/functions/_backend/plugin_runtime/plugins/stats.tssupabase/functions/_backend/plugin_runtime/utils/channelSelfRateLimit.tssupabase/functions/_backend/plugin_runtime/utils/plugin_stats.tssupabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.tssupabase/functions/_backend/plugin_runtime/utils/update.tssupabase/functions/_backend/plugin_runtime/utils/updateOracleGuard.tssupabase/functions/_backend/triggers/stripe_event.tssupabase/functions/_backend/utils/channelSelfRateLimit.tssupabase/functions/_backend/utils/cloudflare_cache_purge.tssupabase/functions/_backend/utils/plugin_stats.tssupabase/functions/_backend/utils/rateLimitInfo.tstests/channel-rate-limit.test.tstests/channel_self.test.tstests/cloudflare-snippet.unit.test.tstests/stats.test.tstests/update-oracle-guard.unit.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual) → reviewed against open PR#845fix/latch-only-on-premiseinstead of the default branch
There was a problem hiding this comment.
All reported issues were addressed across 11 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
- Refresh Cache-Control + moreInfo.retryAfterSeconds on edge cache hits - Extract app_id from /stats batch arrays for edge cache keys - Revert batch /stats on-prem to HTTP 200 per-event (compat) - Paginate org cache purge via shared helper - Gate Stripe edge purge to payment/recovery transitions only Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f8c3f3a3-1667-4b52-9393-723f3c750b8d) |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@supabase/functions/_backend/triggers/stripe_event.ts`:
- Around line 1151-1153: Update the recovery condition in the Stripe webhook
status handling around toStripeInfoUpdate() to detect recovery using
currentStripeInfo?.past_due_at, rather than relying only on previousStatus ===
'past_due'. Preserve the existing succeeded/updated and canceled/deleted checks,
and ensure the cache purge runs when a previously recorded past_due_at indicates
recovery.
In `@supabase/functions/_backend/utils/cloudflare_cache_purge.ts`:
- Around line 105-111: Update the paginated lookup in listOrgAppIds to use
getPgClient() or getDrizzleClient() instead of supabaseAdmin(c), ensuring all
pages of the apps query use the database client consistently while preserving
the existing ordering, filtering, and pagination behavior.
- Around line 148-150: Update the organization purge flow around buildTag and
purgeByTags to partition tags into batches of at most 100, then await a separate
purgeByTags call for each batch so organizations with more than 100 apps are
fully purged. Add coverage for multiple batches, including verifying every batch
is submitted.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 81d10f37-c7a4-4b54-bc18-6b02ea1f71cd
📒 Files selected for processing (8)
cloudflare_workers/snippet/index.jssupabase/functions/_backend/plugin_runtime/plugins/stats.tssupabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.tssupabase/functions/_backend/triggers/stripe_event.tssupabase/functions/_backend/utils/cloudflare_cache_purge.tssupabase/functions/_backend/utils/rateLimitInfo.tstests/cloudflare-snippet.unit.test.tstests/stats.test.ts
🔗 Linked repositories identified
CodeRabbit considers these linked repositories for cross-repo context during reviews:
Cap-go/capacitor-updater(manual) → reviewed against open PR#845fix/latch-only-on-premiseinstead of the default branch
There was a problem hiding this comment.
All reported issues were addressed across 8 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
- Detect past_due recovery via past_due_at (status stays succeeded) - List org apps with getPgClient single SQL - Chunk Cloudflare tag purges to 100 tags per request - Drop stale Content-Length when rewriting cached 429 bodies Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_887615a5-7788-44b8-b925-e34fa08b6e55) |
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
Payment-recovery invalidation is not a plugin hot path — query apps on primary so replica lag cannot skip purge tags. Drop unreachable past_due status guard in the purge gate. Co-authored-by: Cursor <cursoragent@cursor.com>
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4f70fbb7-4ab4-4d6d-9dfc-22dd0e6ad995) |
|



Summary (AI generated)
onPremiseAppResponse()— setsRetry-After,X-RateLimit-Reset,Cache-Control, andmoreInfo(default 1h, overridable viaRATE_LIMIT_ON_PREMISE_RETRY_AFTER_SECONDS)/updates,/stats,/channel_self, files plan gate)on_premise_appbody butprivate, no-store+ real 15m reset (not public edge cache)Cache-ControlorRetry-After; persist absoluteX-RateLimit-Reset; refreshRetry-Afteron cache HIT/channel_self: op 10/s, same-set 1s/statson-prem → top-level 429 with backoff headersMotivation (AI generated)
Updated plugins honour
Retry-Afterinstead of latching until process kill. On-prem / cancelled need a server-driven backoff. Edge must retain those headers without letting IP-scoped enumeration blocks poison valid apps. Soft channel limits reduce accidental 429 → forever-latch on old plugins. Payment recovery must clear edge cache immediately.Business Impact (AI generated)
Less worker load for known on-prem / cancelled apps. Fewer false sticky latches for old plugins. Faster recovery after payment / misclassification.
Test Plan (AI generated)
bunx vitest run tests/cloudflare-snippet.unit.test.tsbunx vitest run tests/update-oracle-guard.unit.test.ts/updateson-prem → 429 withRetry-After~3600 +Cache-ControlCache-Control: private, no-store(not edge-cached for other clients)Generated with AI
Note
Medium Risk
Changes plugin hot paths and edge caching of 429 responses; mis-tuned TTL or enumeration vs on-prem cache semantics could affect client backoff or post-payment recovery, though enumeration explicitly avoids public cache poisoning.
Overview
Introduces
onPremiseAppResponse()so on-prem / cancelled plugin paths return a consistent 429 withRetry-After(default 1h, env override),X-RateLimit-Reset,publicCache-Control, andmoreInfobackoff fields. Plugin routes (/updates,/stats,/channel_self, files plan gate, etc.) switch from bare JSON 429s to this helper.The Cloudflare snippet now derives edge cache TTL from
Cache-ControlorRetry-After, stores absolute reset metadata when caching on-prem / plan-upgrade responses, and on cache HIT recomputesRetry-After,Cache-Control, and JSONmoreInfofromX-RateLimit-Reset. Batch/statsbodies can supplyapp_idfrom the first array element for cache keying.Update enumeration blocks still look like
on_premise_appbut useprivate, no-storeplus real reset headers so they are not stored in the app-keyed public edge cache./channel_selfdevice limits move to 10 ops/s and 1s duplicate-set window.On Stripe payment / plan recovery (paid, upgrade, past-due recovery, cancel→active, etc.), the backend purges on-prem and plan-upgrade edge cache tags for all org apps (PG app list, batched tag purge). Single-event
/statson-prem returns the new 429; batch keeps HTTP 200 with per-eventon_premise_apperrors for compatibility.Reviewed by Cursor Bugbot for commit 8b52ce0. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
Bug Fixes
Changes