improvement(billing): self-heal null usage limits and debounce api-key last-used writes#5000
Conversation
…y last-used writes
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview
New unit tests cover heal paths (stored limit, missing row, free/paid fallbacks, concurrency, failed write) and API key last-used behavior (guarded predicate, error swallow). Reviewed by Cursor Bugbot for commit 1814df5. Configure here. |
|
@greptile |
|
@cursor review |
Greptile SummaryThis PR fixes two independent resiliency issues: null
Confidence Score: 5/5Both changes are safe to merge: the self-heal is fully guarded against concurrent writers and transient DB failures, and the debounced lastUsed write is best-effort on a display-only field. The null-limit self-heal correctly handles all failure modes (write fails → returns fallback, concurrent writer wins → returns concurrent value, no row → throws as before). The staleness-guarded lastUsed update is a net improvement with no regression risk. All new code is covered by unit tests. No files require special attention; the minor log-message accuracy issue in usage.ts is non-blocking. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getUserUsageLimit called] --> B{isOrgScoped?}
B -- yes --> C[return org limit]
B -- no --> D[SELECT currentUsageLimit FROM userStats]
D --> E{row found?}
E -- no --> F[throw: No user stats record]
E -- yes --> G{currentUsageLimit IS NULL?}
G -- no --> H[return stored limit]
G -- yes --> I[compute fallbackLimit\npaid to perUserMinimum\nfree to freeTierLimit]
I --> J[UPDATE userStats SET limit=fallback\nWHERE userId=X AND limit IS NULL RETURNING]
J -- try throws --> K[logger.error / return fallbackLimit]
J -- healed.length > 0 --> L[logger.warn / return fallbackLimit]
J -- healed.length == 0 --> M[re-read concurrent value]
M -- found --> N[return concurrent value]
M -- not found --> O[logger.warn / return fallbackLimit]
Reviews (4): Last reviewed commit: "improvement(api-key): widen last-used st..." | Re-trigger Greptile |
Greptile SummaryThis PR fixes two independent billing and API-key issues:
Confidence Score: 3/5The debounce change is safe to merge as-is; the self-heal in usage.ts has a gap where a transient DB error during the heal write still propagates to the caller, re-introducing the fail-closed behaviour the PR aims to eliminate. The self-heal write ( apps/sim/lib/billing/core/usage.ts — the null-heal path at lines 550-564 needs a try-catch around the Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[getUserUsageLimit] --> B{isOrgScoped?}
B -- yes --> C[return orgLimit]
B -- no --> D[query userStats.currentUsageLimit]
D --> E{row exists?}
E -- no --> F[throw: No user stats record found]
E -- yes --> G{limit is null?}
G -- no --> H[return stored limit]
G -- yes --> I[compute fallbackLimit\npaid? getPerUserMinimumLimit\nfree? getFreeTierLimit]
I --> J[db.update userStats\nset currentUsageLimit\nWHERE userId AND isNull]
J -- success --> K[logger.warn + return fallbackLimit]
J -- throws ❌ --> L[exception propagates\nfail-closed again]
style L fill:#ff4444,color:#fff
style F fill:#ffaa00,color:#fff
Reviews (1): Last reviewed commit: "improvement(billing): self-heal null usa..." | Re-trigger Greptile |
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit eb7bf1d. Configure here.
|
@greptile |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1814df5. Configure here.
Summary
getUserUsageLimitnow self-heals a nullcurrentUsageLimitinstead of throwing. Org-scoped members carry a null per-user limit by design, so a user whose subscription stops being org-scoped without a resync was left null and failed closed (Cannot determine usage status - blocking execution) on every run. The limit now falls back to the same plan defaultsyncUsageLimitsFromSubscriptionwould set (plan minimum for paid, free tier otherwise), writes it back guarded oncurrentUsageLimit IS NULL, and logs a warningupdateApiKeyLastUsedis debounced: the write only fires when the storedlastUsedis missing or older than 60s. The column is display-only, and unconditionally rewriting the same row on every authenticated request serializes concurrent requests for the same key behind row locksType of Change
Testing
lib/billing+lib/api-keysuites pass (186 tests), typecheck cleanChecklist