fix(cron): bound the desktop run-history query to one job (#41088)
The cron run-history endpoint (GET /api/cron/jobs/{id}/runs, added in
#40684) reused list_sessions_rich's order_by_last_active path with a
leading-wildcard id_query. That routes through the recursive
compression-chain CTE, which seeds from EVERY source='cron' row in the DB
and runs per-row preview/last_active subqueries before filtering to one
job and applying LIMIT. Work scaled with the total cron history, so a
large pile made the run-history load time out before eventually
populating.
Cron runs are flat, never-compressed sessions with ids of the form
cron_{job_id}_{ts}, so the chain machinery is pure overhead and the
job binding is a true prefix, not a substring.
- New SessionDB.list_cron_job_runs(): bounded [prefix, hi) id-range scan
on source='cron', ordered by started_at DESC, with the same
preview/last_active enrichment. No CTE, no leading-wildcard LIKE.
- Add idx_sessions_source(source, id) so the range is an index scan;
bump SCHEMA_VERSION 14 -> 15 (index reconciles onto existing DBs via
CREATE INDEX IF NOT EXISTS on startup).
- Point the endpoint at the new method.
Measured on a real SessionDB with 30k cron rows: 5ms vs 85ms for the old
path (16x), and the new path stays flat as the pile grows while the old
one scaled with it. Verified the query plan uses idx_sessions_source_id
(range scan, no full table scan), runs are correctly scoped (substring
collisions like cron_xalpha_ excluded), newest-first, and paged.
This commit is contained in:
parent
5a3092b601
commit
ed81cfe3de
3 changed files with 178 additions and 9 deletions
|
|
@ -5657,9 +5657,14 @@ async def list_cron_job_runs(job_id: str, profile: Optional[str] = None, limit:
|
|||
Cron runs are stored as ordinary sessions whose id is
|
||||
``cron_{job_id}_{timestamp}`` (see cron/scheduler.run_job). A job's history
|
||||
is therefore every session whose id carries that prefix; ``source='cron'``
|
||||
narrows it and the id substring binds it to this job. Powers the run-history
|
||||
narrows it and the id prefix binds it to this job. Powers the run-history
|
||||
list under each job in the desktop cron detail. Same row shape as
|
||||
``/api/sessions`` so the frontend can reuse SessionInfo.
|
||||
|
||||
Backed by ``SessionDB.list_cron_job_runs`` — a bounded ``[prefix, hi)``
|
||||
id-range scan, not the compression-chain CTE used for the recents list,
|
||||
so the cost scales with the requested window and not the (unbounded) total
|
||||
cron history.
|
||||
"""
|
||||
selected = profile or _find_cron_job_profile(job_id)
|
||||
# job_id may be a human name; resolve to the canonical id used in run-session ids.
|
||||
|
|
@ -5676,13 +5681,7 @@ async def list_cron_job_runs(job_id: str, profile: Optional[str] = None, limit:
|
|||
|
||||
db = _open_session_db_for_profile(selected)
|
||||
try:
|
||||
runs = db.list_sessions_rich(
|
||||
source="cron",
|
||||
id_query=f"cron_{canonical}_",
|
||||
limit=limit_n,
|
||||
offset=0,
|
||||
order_by_last_active=True,
|
||||
)
|
||||
runs = db.list_cron_job_runs(canonical, limit=limit_n, offset=0)
|
||||
now = time.time()
|
||||
for s in runs:
|
||||
s["is_active"] = (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue