Skip to content

fix(api): scale pool usage snapshot limits by pool member count (summed budget) - #10253

Open
dpozimski wants to merge 7 commits into
diegosouzapw:release/v3.8.50from
dpozimski:fix/quota-pool-usage-summed-budget
Open

fix(api): scale pool usage snapshot limits by pool member count (summed budget)#10253
dpozimski wants to merge 7 commits into
diegosouzapw:release/v3.8.50from
dpozimski:fix/quota-pool-usage-summed-budget

Conversation

@dpozimski

Copy link
Copy Markdown

Problem

GET /api/quota/pools/[id]/usage passes the provider plan's per-account dimensions straight to poolUsageWithDimensions, while enforce.ts multiplies every dimension limit by the pool's member-connection count before deciding anything (the summed budget). For a multi-connection pool the snapshot therefore reports limits and fair shares N times smaller than what enforcement actually uses.

I hit this in production today: a 27-connection pool with a usd/daily plan limit of 66.67 (enforced budget 1800.09) had consumed 54.09. The Quota Share page showed 81% utilisation; real utilisation was 3%. The per-key borrowing flags have the same problem: they trip once a key crosses weight% of the per-account limit instead of weight% of the summed budget, so on that pool they fire 27x too early.

Fix

Scale the resolved plan dimensions by accountCount in the usage route before calling the store, using the same computation and legacy fallback as enforce.ts (connectionIds.length, falling back to 1 when the array is missing or empty). Snapshot limit, fairShare, deficit and borrowing then all describe the budget that is enforced. Single-connection and legacy pools are unchanged (N=1), and the { usage: snapshot } wrapper contract is untouched.

Scaling at the route level covers both store drivers, since sqlite and redis both receive their dimensions from this one caller.

Tests

Added tests/unit/quota-pool-usage-summed-budget.test.ts, following the pattern of quota-summed-budget.test.ts (which pins the same multiply on the enforcement side) and quota-pool-usage-shape.test.ts: structural assertions that the route applies the multiply with the right fallback, plus a logic-level check that the scaled limit corrects fairShare and borrowing for the 27-connection case above.

Ran locally:

node --import tsx/esm --test tests/unit/quota-pool-usage-summed-budget.test.ts \
  tests/unit/quota-pool-usage-shape.test.ts \
  tests/unit/quota-summed-budget.test.ts \
  tests/unit/quota-sharing-fixes.test.ts
# 23 pass, 0 fail

npm run lint (scoped to the two changed files) is clean.

diegosouzapw and others added 5 commits August 8, 2026 00:08
…ouzapw#189, diegosouzapw#190)

Bumps: nanoid ^3.3.17 (was transitive, now overridden), dompurify ^3.4.13
(with monaco-editor scoped override). Closes Dependabot diegosouzapw#189, diegosouzapw#190.

Remaining diegosouzapw#182-diegosouzapw#188 (js-yaml + mermaid) already closed by diegosouzapw#9651 merge —
awaiting Dependabot re-scan.

npm audit → 0 vulnerabilities.
…egosouzapw#190

Closes Dependabot diegosouzapw#189 (dompurify 3.4.13) and diegosouzapw#190 (nanoid 3.3.17). npm audit → 0.
_tasks is a SEPARATE nested git repo (gitignored). The pattern _tasks/ (trailing
slash) ignores only a directory, not a SYMLINK named _tasks. A self-referential
_tasks symlink can slip in via git add -A and, once pulled, checkout materializes
it over the real _tasks repo (destroying plans/specs/hands-off). Anchored /_tasks
ignores the symlink too, preventing re-capture.
…pw#10026)

Mirror the request-time exclusion rule (provider_specific_data.excludedModels)
in the unified catalog builder: a model is hidden when its provider has
connections but none of them is eligible for it. Applied across the
PROVIDER_MODELS, synced, custom, alias-backed, and managed-fallback loops
so ghost models no longer appear as available.

Co-authored-by: ritheshcn25 <ritheshcn25@users.noreply.github.com>
…osouzapw#10055)

* fix(models): memoize getModelsDevPricing for /v1/models catalog

resolveCatalogPricing called getModelsDevPricing once per model while
building GET /v1/models. Each call re-scanned models_dev_pricing and
JSON.parsed every row (~10k SQL scans + multi-GB parse work), pegging
the event loop so even /healthz timed out (diegosouzapw#9685, diegosouzapw#10052).

Memoize the parsed map until saveModelsDevPricing / clearModelsDevPricing
and add a unit test for invalidation.

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>

* fix(db): invalidate modelsDevPricing cache on DB reset (diegosouzapw#10055)

Copilot review fixes:
1. Register invalidateModelsDevPricingCache() with DB state reset system
   so resetDbInstance() clears the process-local memo, preventing stale
   pricing data from surviving across DB reset/restore operations.
2. Add test assertion verifying DB reset bypasses the memo (Copilot diegosouzapw#10055).

The process-local memo at modelsDevSync.ts:204 caches getModelsDevPricing()
results until saveModelsDevPricing()/clearModelsDevPricing() to avoid
re-scanning all pricing rows on every /v1/models request. Without this hook,
backup restore and test DB resets would serve stale cached data from the
previous connection.

Tests: npm run test:unit:serial -- tests/unit/modelsDevSync-extended.test.ts

---------

Signed-off-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Ravi Tharuma <RaviTharuma@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@dpozimski

Copy link
Copy Markdown
Author

hi @diegosouzapw , would be great if u can look on it , thanks!

@diegosouzapw
diegosouzapw changed the base branch from release/v3.8.49 to release/v3.8.50 August 13, 2026 13:49
@diegosouzapw

Copy link
Copy Markdown
Owner

Correct, production-reproduced bug fix — GET /api/quota/pools/[id]/usage now multiplies each plan dimension limit by accountCount. However, the PR targets the dead release/v3.8.49 and is BLOCKED. Please retarget to main and resolve the merge conflict — then it's good to merge.

@dpozimski
dpozimski force-pushed the fix/quota-pool-usage-summed-budget branch from 75d4ddd to 40c1204 Compare August 13, 2026 15:39
@dpozimski
dpozimski changed the base branch from release/v3.8.50 to main August 13, 2026 15:39
@dpozimski

Copy link
Copy Markdown
Author

Retargeted to main and resolved the conflict. The branch was originally cut from the release lineage, so rather than rebase 13 commits I rebuilt it from main and cherry-picked the fix commit, which applied cleanly. The conflict was all release-branch history, not the change itself. Re-verified against main: enforce.ts there still applies the same accountCount multiply, the 4 quota test files pass (23/23), and eslint is clean on both changed files. Should be good to merge now.

…dget)

Merge release/v3.8.50 into the contributor branch: resolve stale
base-evolution conflicts (.gitignore, package-lock.json, modelsDevSync)
to the release tip, keeping only the summed-budget pool usage fix and its
regression test.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@diegosouzapw
diegosouzapw changed the base branch from main to release/v3.8.50 August 15, 2026 11:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants