improvement(billing): move overage calculations out of txes #4595
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Adds usage snapshot + recheck logic (with a lock timeout) so threshold billing skips and retries later if relevant usage inputs, membership/owner, or billed trackers change while locked—preventing stale calculations from enqueueing invoices. Updates org-scoped Reviewed by Cursor Bugbot for commit 390eef5. Configure here. |
Greptile SummaryThis PR moves the expensive overage calculations (
Confidence Score: 4/5Safe to merge after fixing the lock-ordering inversion in captureDepartedUsage; all other changes are well-structured. The billing and invoice paths are carefully reworked with consistent lock ordering and timeout guards. The one concrete defect is in membership.ts: captureDepartedUsage locks the org row before the departing user's userStats row, while threshold billing and resetUsageForSubscription lock userStats before org. When the org owner is removed while threshold billing is running, Postgres will detect the AB/BA cycle and abort one of the transactions. apps/sim/lib/billing/organizations/membership.ts — lock ordering in captureDepartedUsage needs to be inverted (userStats first, then org) to match the ordering used in threshold-billing.ts and invoices.ts. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller
participant ThresholdBilling
participant DB
Caller->>ThresholdBilling: checkAndBillOverageThreshold(userId)
ThresholdBilling->>DB: SELECT userStats snapshot (no lock)
ThresholdBilling->>DB: calculateSubscriptionOverage() [outside tx]
alt "overage < threshold"
ThresholdBilling-->>Caller: return (no tx opened)
else "overage >= threshold"
ThresholdBilling->>DB: "BEGIN tx + SET LOCAL lock_timeout=5s"
ThresholdBilling->>DB: SELECT member FOR UPDATE (owner)
ThresholdBilling->>DB: SELECT userStats FOR UPDATE (owner)
ThresholdBilling->>DB: SELECT organization FOR UPDATE
ThresholdBilling->>DB: SELECT member+userStats+org (locked read)
alt snapshot changed
ThresholdBilling-->>Caller: return (retry next cycle)
else snapshot stable
ThresholdBilling->>DB: UPDATE userStats billedOverageThisPeriod
ThresholdBilling->>DB: enqueueOutboxEvent (Stripe invoice)
ThresholdBilling->>DB: COMMIT
end
end
Reviews (3): Last reviewed commit: "share timeout const" | Re-trigger Greptile |
|
@greptile |
|
@greptile |
Summary
Will catch missed overages in next check.
Type of Change
Testing
Tested manually
Checklist