fix(sessions): ration the compression-heavy widening to one pass budget

Greptile P1 on #6659: the retry that follows a binding scoped raw window jumped
straight to the global `query_limit`, and every starved project got it in turn.
`remaining` bounds a project's FIRST scoped query, but the widening was not
bounded by the pass at all, so a build scanned len(starved) * query_limit —
an order of magnitude past PROJECT_ASSIGNED_CLI_SCAN_CEILING on a profile with
ten compression-heavy projects, and growing with the number of them.

The widening now comes out of a single pass-level pool of `query_limit`, charged
the full width of each retry, so the scoped reading of a build is capped at
2 * query_limit however many projects are starved. When the pool cannot widen a
retry past the window that just bound it, the loop stops instead of paying for a
query that cannot deliver.

Completeness is deliberately untouched: only the WIDENING is rationed. Every
starved project still pays its own first scoped query, which is the guarantee
the fixed per-build project cap used to break (greptile P1 on #6659). The
constants comment above PROJECT_ASSIGNED_CLI_SCAN_CEILING carried the same
overclaim ("one small project-scoped query per starved project") and now states
the widening budget.

Verified: 88 tests pass across
tests/test_project_assigned_cli_session_limit.py (75),
tests/test_issue2628_cli_sessions_perf.py and
tests/test_issue4842_cron_projection_perf.py. The new regression test
`test_compression_heavy_widening_is_one_pass_budget_not_per_project` is
parametrized over 1 and 3 compression-heavy starved projects and fails on the
old one-liner by showing three full-width retries (16, 16, 16) where the pool
funds one (16).
This commit is contained in:
rodrigogs authored and Rodrigo Gomes da Silva committed 2026-09-24 11:03:47 -03:00
1 parent 7e56aaae07
commit 634f66774b
2 files changed
+126 -6

No files matched your search

+33 -6
View File
@@ -79,7 +79,11 @@ PROJECT_ASSIGNED_CLI_SCAN_CEILING = 2000
# one project's remaining budget; and the sum of those budgets across every
# starved project is at most effective_limit * len(projects), which the scan
# ceiling above already caps. Worst case per build: 1 global query + 1 GROUP BY
# probe + one small project-scoped query per starved project.
# probe + one small project-scoped query per starved project, plus the pass's
# single widening budget (query_limit, see `widening_budget`) shared by the
# compression-heavy retries — a retry is NOT granted the global allowance per
# project, which would make a build scale with the number of starved projects
# (greptile P1 on #6659).
#
# --- Bounds for the UNASSIGNED refill pass (see _load_cli_sessions_uncached).
# The SQL 'unassigned' filter reads state.db.project_id, but a WebUI-side move
@@ -8754,6 +8758,21 @@ def _load_cli_sessions_uncached(
# cost stays bounded because each query is limited to one
# project's remaining budget (sum <= the scan ceiling) and
# only fires when the global window was saturated.
#
# The WIDENING below is rationed per PASS instead (greptile
# P1 on #6659): `remaining` bounds each project's FIRST
# query, but a retry that jumps to `query_limit` is not
# bounded by the project's own share at all, so granting it
# to every starved project made a build scan
# len(starved) * query_limit — an order of magnitude past
# the ceiling on a profile with ten compression-heavy
# projects. One aggregate pool, spent at the full width of
# each retry, caps the pass at 2 * query_limit of scoped
# reading regardless of how many projects are starved.
# Only the widening is rationed; the first query of every
# starved project is never skipped, which is what keeps the
# completeness guarantee above intact.
widening_budget = query_limit
for _kept, project_id in starved:
remaining = effective_limit - kept_per_project.get(project_id, 0)
if remaining <= 0:
@@ -8768,10 +8787,10 @@ def _load_cli_sessions_uncached(
# the loop would advance to the next project and leave
# this one under-delivered on every rebuild — the same
# failure the global query already guards against
# (greptile P1 on #6659). Re-query once at the widest
# limit this pass is allowed to scan, so the retry is
# bounded to one extra query per starved project and
# cannot out-read the pass's own scan budget.
# (greptile P1 on #6659). Re-query once, widened to
# whatever the pass's widening budget has left, which is
# what bounds the retry at the PASS level instead of per
# project (greptile P1 on #6659).
scoped_limit = remaining
while True:
scoped_rows, scoped_window_exhausted = cast(
@@ -8797,7 +8816,15 @@ def _load_cli_sessions_uncached(
or scoped_limit >= query_limit
):
break
scoped_limit = query_limit
widened = min(query_limit, widening_budget)
if widened <= scoped_limit:
# The pass has spent its widening budget. A
# retry now could only re-read a window this
# query already consumed, so stop instead of
# paying for a read that cannot deliver.
break
widening_budget -= widened
scoped_limit = widened
except Exception:
logger.debug("Project-assigned CLI recovery pass failed", exc_info=True)