Skip to content

fix(plugin): on_premise Retry-After 1h, soften channel limits, edge retain - #2865

Open
riderx wants to merge 8 commits into
mainfrom
fix/on-premise-long-retry-after
Open

fix(plugin): on_premise Retry-After 1h, soften channel limits, edge retain#2865
riderx wants to merge 8 commits into
mainfrom
fix/on-premise-long-retry-after

Conversation

@riderx

@riderx riderx commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary (AI generated)

  • Add onPremiseAppResponse() — sets Retry-After, X-RateLimit-Reset, Cache-Control, and moreInfo (default 1h, overridable via RATE_LIMIT_ON_PREMISE_RETRY_AFTER_SECONDS)
  • Use it for true on-prem / cancelled plugin 429s (/updates, /stats, /channel_self, files plan gate)
  • Update enumeration keep on_premise_app body but private, no-store + real 15m reset (not public edge cache)
  • Cloudflare snippet: TTL from Cache-Control or Retry-After; persist absolute X-RateLimit-Reset; refresh Retry-After on cache HIT
  • Soften /channel_self: op 10/s, same-set 1s
  • Batch /stats on-prem → top-level 429 with backoff headers
  • Stripe plan recovery purges on-prem + plan edge cache tags for the org

Motivation (AI generated)

Updated plugins honour Retry-After instead 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.ts
  • bunx vitest run tests/update-oracle-guard.unit.test.ts
  • /updates on-prem → 429 with Retry-After ~3600 + Cache-Control
  • Enumeration-limited IP → 429 with Cache-Control: private, no-store (not edge-cached for other clients)
  • After Stripe payment success, edge on-prem cache for org apps is purged
  • Pair with capacitor-updater PR fix(updater): honour Retry-After for all 429s, queue stats while blocked capacitor-updater#845

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 with Retry-After (default 1h, env override), X-RateLimit-Reset, public Cache-Control, and moreInfo backoff 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-Control or Retry-After, stores absolute reset metadata when caching on-prem / plan-upgrade responses, and on cache HIT recomputes Retry-After, Cache-Control, and JSON moreInfo from X-RateLimit-Reset. Batch /stats bodies can supply app_id from the first array element for cache keying.

Update enumeration blocks still look like on_premise_app but use private, no-store plus real reset headers so they are not stored in the app-keyed public edge cache. /channel_self device 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 /stats on-prem returns the new 429; batch keeps HTTP 200 with per-event on_premise_app errors 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

    • Rate-limit responses now include clearer retry and reset details, with configurable retry timing for on-premise applications.
    • Cached rate-limit responses preserve and refresh countdown information.
    • Subscription updates now clear relevant cached data.
  • Bug Fixes

    • Standardized on-premise error responses across affected endpoints.
    • Improved rate-limit metadata for batch and enumeration responses.
  • Changes

    • Channel operations now allow up to 10 requests per second.
    • Repeated channel updates can be retried after 1 second.

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>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

On-premise rate-limit handling

Layer / File(s) Summary
Canonical response and reset-aware guards
supabase/functions/.../rateLimitInfo.ts, supabase/functions/.../updateOracleGuard.ts, supabase/functions/.../update.ts, tests/update-oracle-guard.unit.test.ts
Adds configurable retry durations, shared 429 responses, reset metadata, and private no-store headers for limited responses.
Shared response adoption
supabase/functions/.../files.ts, supabase/functions/.../plugins/channel_self.ts, supabase/functions/.../plugins/stats.ts, supabase/functions/.../utils/plugin_stats.ts, tests/stats.test.ts
Replaces inline on-premise 429 responses with onPremiseAppResponse(c). Batch stats requests retain HTTP 200 with per-event errors.
Channel-self operation limits
supabase/functions/.../channelSelfRateLimit.ts, supabase/functions/.../plugins/channel_self.ts, tests/channel-rate-limit.test.ts, tests/channel_self.test.ts
Raises operation limits from 5 to 10 requests per second and changes identical set windows from 5 seconds to 1 second.
Retry-aware Cloudflare caching
cloudflare_workers/snippet/index.js, supabase/functions/.../cloudflare_cache_purge.ts, supabase/functions/.../triggers/stripe_event.ts, tests/cloudflare-snippet.unit.test.ts
Derives cache TTLs from cache and retry metadata, refreshes cached headers, supports array request bodies, and purges organization caches after selected subscription updates.

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
Loading

Possibly related PRs

Suggested labels: codex

Suggested reviewers: dalanir

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies on-premise retry handling, softer channel limits, and edge-cache retention changes.
Description check ✅ Passed The description provides a detailed summary, motivation, impact, test plan, and backend-specific validation details.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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

@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@codspeed-hq

codspeed-hq Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing fix/on-premise-long-retry-after (8b52ce0) with main (bb59c86)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 11 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread supabase/functions/_backend/plugin_runtime/plugins/stats.ts
Comment thread supabase/functions/_backend/plugin_runtime/utils/update.ts
Comment thread supabase/functions/_backend/plugin_runtime/utils/updateOracleGuard.ts Outdated
Comment thread cloudflare_workers/snippet/index.js
Comment thread cloudflare_workers/snippet/index.js Outdated
Comment thread cloudflare_workers/snippet/index.js
Comment thread supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts
Comment thread supabase/functions/_backend/plugin_runtime/utils/updateOracleGuard.ts Outdated
Comment thread cloudflare_workers/snippet/index.js Outdated
Comment thread tests/cloudflare-snippet.unit.test.ts
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>
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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>
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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>
@cursor

cursor Bot commented Aug 4, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@riderx riderx changed the title fix(plugin): long Retry-After on on_premise_app + edge retain fix(plugin): on_premise Retry-After 1h, soften channel limits, edge retain Aug 4, 2026
- 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>
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@coderabbitai coderabbitai Bot added the codex label Aug 5, 2026

@coderabbitai coderabbitai Bot left a comment

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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between bb59c86 and de953e1.

📒 Files selected for processing (19)
  • cloudflare_workers/snippet/index.js
  • supabase/functions/_backend/files/files.ts
  • supabase/functions/_backend/plugin_runtime/plugins/channel_self.ts
  • supabase/functions/_backend/plugin_runtime/plugins/stats.ts
  • supabase/functions/_backend/plugin_runtime/utils/channelSelfRateLimit.ts
  • supabase/functions/_backend/plugin_runtime/utils/plugin_stats.ts
  • supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts
  • supabase/functions/_backend/plugin_runtime/utils/update.ts
  • supabase/functions/_backend/plugin_runtime/utils/updateOracleGuard.ts
  • supabase/functions/_backend/triggers/stripe_event.ts
  • supabase/functions/_backend/utils/channelSelfRateLimit.ts
  • supabase/functions/_backend/utils/cloudflare_cache_purge.ts
  • supabase/functions/_backend/utils/plugin_stats.ts
  • supabase/functions/_backend/utils/rateLimitInfo.ts
  • tests/channel-rate-limit.test.ts
  • tests/channel_self.test.ts
  • tests/cloudflare-snippet.unit.test.ts
  • tests/stats.test.ts
  • tests/update-oracle-guard.unit.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Comment thread cloudflare_workers/snippet/index.js Outdated
Comment thread supabase/functions/_backend/plugin_runtime/plugins/stats.ts
Comment thread supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts

@cubic-dev-ai cubic-dev-ai 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.

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

Comment thread supabase/functions/_backend/plugin_runtime/plugins/stats.ts Outdated
Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts Outdated
Comment thread supabase/functions/_backend/triggers/stripe_event.ts Outdated
Comment thread cloudflare_workers/snippet/index.js Outdated
Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts
- 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>
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@coderabbitai coderabbitai Bot left a comment

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.

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

📥 Commits

Reviewing files that changed from the base of the PR and between de953e1 and bee1f7c.

📒 Files selected for processing (8)
  • cloudflare_workers/snippet/index.js
  • supabase/functions/_backend/plugin_runtime/plugins/stats.ts
  • supabase/functions/_backend/plugin_runtime/utils/rateLimitInfo.ts
  • supabase/functions/_backend/triggers/stripe_event.ts
  • supabase/functions/_backend/utils/cloudflare_cache_purge.ts
  • supabase/functions/_backend/utils/rateLimitInfo.ts
  • tests/cloudflare-snippet.unit.test.ts
  • tests/stats.test.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

Comment thread supabase/functions/_backend/triggers/stripe_event.ts
Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts Outdated
Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts

@cubic-dev-ai cubic-dev-ai 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.

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

Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts
Comment thread cloudflare_workers/snippet/index.js
Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts
- 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>
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@cubic-dev-ai cubic-dev-ai 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.

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

Comment thread supabase/functions/_backend/utils/cloudflare_cache_purge.ts Outdated
Comment thread supabase/functions/_backend/triggers/stripe_event.ts Outdated
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>
@cursor

cursor Bot commented Aug 5, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@sonarqubecloud

sonarqubecloud Bot commented Aug 5, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant