perf: eliminate wasted API call, afterUpdate overhead, and sequential billing checks#3026
Conversation
… billing checks - Remove unused projects.list() call fired on every console load (result was never consumed) - Replace afterUpdate with reactive headerAlert subscription to avoid per-render overhead - Parallelize billing checks with Promise.all instead of sequential awaits - Fix database.subscribe() memory leak by wrapping in onMount for proper cleanup
Greptile SummaryThis PR removes a never-consumed
Confidence Score: 4/5Safe to merge once the duplicate checkForNewDevUpgradePro call in +layout.svelte is removed. The +layout.ts change is straightforward and correct. In +layout.svelte, the database.subscribe fix and reactive-statement replacement look good, but the billing-check refactor left checkForNewDevUpgradePro executing twice per organization load — once explicitly awaited and once inside Promise.all — causing two redundant backend round-trips every time the org changes. src/routes/(console)/+layout.svelte — the billing check section around the billingChecks array Important Files Changed
Reviews (2): Last reviewed commit: "Merge branch 'main' into perf-console-op..." | Re-trigger Greptile |
| afterUpdate(() => { | ||
| $activeHeaderAlert = headerAlert.getExcluding('impersonation'); | ||
| }); | ||
| $: (void $headerAlert, ($activeHeaderAlert = headerAlert.getExcluding('impersonation'))); |
There was a problem hiding this comment.
Unusual comma-expression reactive pattern
The comma expression (void $headerAlert, ($activeHeaderAlert = ...)) is a non-obvious Svelte pattern. Reading $headerAlert via void purely to register it as a reactive dependency, while performing the actual side-effect in the right-hand operand, is hard to parse at a glance and not idiomatic Svelte. A named reactive block or a dedicated $: if ($headerAlert) form would communicate intent more clearly. There is also a subtle semantic shift from afterUpdate: this reactive declaration fires on component initialization (before any update), whereas afterUpdate only fired after re-renders. On first mount $headerAlert is empty, so getExcluding returns a no-op result — that is harmless today, but the distinction is worth noting for future maintainers.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| onMount(() => { | ||
| return database.subscribe(async (database) => { | ||
| if (!database || !page.params.region || !page.params.project) return; | ||
| // the component checks `isCloud` internally. | ||
| await checkForDatabaseBackupPolicies(page.params.region, page.params.project, database); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Two separate
onMount blocks in the same component
There are now two onMount calls in this component (lines 253 and 283). While Svelte allows multiple onMount registrations, having two separate blocks for the same component lifecycle event makes the initialization flow harder to follow. The database.subscribe setup could be placed inside the existing onMount block at line 253 to keep all mounting logic in one place.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| await checkForNewDevUpgradePro(org); | ||
|
|
||
| if (org?.billingPlanDetails.requiresPaymentMethod) { | ||
| await paymentExpired(org); | ||
| await checkPaymentAuthorizationRequired(org); | ||
| const billingChecks: Promise<unknown>[] = [ | ||
| checkForUsageLimit(org), | ||
| checkForNewDevUpgradePro(org) | ||
| ]; |
There was a problem hiding this comment.
checkForNewDevUpgradePro is called twice: once on line 301 with an explicit await, and again inside billingChecks on line 305. The first call is awaited before the array is even constructed, so both invocations will hit the backend (two organizations.list() calls and two getCoupon() calls per load). The localStorage.getItem('newDevUpgradePro') guard is only set externally (on user dismissal), so it won't short-circuit the second run. The function should be in billingChecks only — remove the standalone await call.
| await checkForNewDevUpgradePro(org); | |
| if (org?.billingPlanDetails.requiresPaymentMethod) { | |
| await paymentExpired(org); | |
| await checkPaymentAuthorizationRequired(org); | |
| const billingChecks: Promise<unknown>[] = [ | |
| checkForUsageLimit(org), | |
| checkForNewDevUpgradePro(org) | |
| ]; | |
| const billingChecks: Promise<unknown>[] = [ | |
| checkForUsageLimit(org), | |
| checkForNewDevUpgradePro(org) | |
| ]; |
… billing checks
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)