From f00b5c8ede1248a2318ecfe6a4698aa166152252 Mon Sep 17 00:00:00 2001 From: Alyx-ML <256263590+Alyx-ML@users.noreply.github.com> Date: Sun, 18 Oct 2026 13:51:23 +0100 Subject: [PATCH] fix(server): usage-limit sweep stops scanning every run each tick The usage-limit recovery sweep runs every scheduler tick (5 s). Its latest-run lookup sorted every run of every thread, about 40 ms per tick with one long thread, which was most of the server's idle CPU. Only threads with a usage-limit failure can qualify, so filter to those first. Same rows, 20.9 ms to 0.004 ms on a real database copy. Done with Claude Opus 5.5 in Claude Code. Co-Authored-By: Claude Opus 5.5 --- apps/server/src/orchestration-v2/ProjectionStore.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/server/src/orchestration-v2/ProjectionStore.ts b/apps/server/src/orchestration-v2/ProjectionStore.ts index e222b0d30a1e..144d3f135380 100644 --- a/apps/server/src/orchestration-v2/ProjectionStore.ts +++ b/apps/server/src/orchestration-v2/ProjectionStore.ts @@ -3580,6 +3580,14 @@ export const layer: Layer.Layer = LIMIT 1 ) WHERE t.deleted_at IS NULL + -- The sweep runs every scheduler tick. Without this list the latest-run + -- lookup above sorts every run of every thread, tens of milliseconds + -- for one long thread; only threads with a usage-limit failure qualify. + AND t.thread_id IN ( + SELECT failure.thread_id FROM orchestration_v2_projection_turn_items failure + WHERE failure.type = 'error' AND failure.status = 'failed' + AND json_extract(failure.payload_json, '$.failure.class') = 'usage_limit' + ) AND json_extract(t.payload_json, '$.archivedAt') IS NULL AND json_extract(t.payload_json, '$.settledOverride') IS NOT 'settled' AND json_extract(item.payload_json, '$.failure.class') = 'usage_limit'