perf(convex): bound unbounded owner-wide collects on hot paths

This commit is contained in:
Gabriel Brown
2026-07-11 14:41:31 -04:00
parent e4a7f96633
commit 9067d16734
3 changed files with 300 additions and 10 deletions
+7 -2
View File
@@ -234,6 +234,11 @@ const normalizeWorkspacePaths = (values: string[] | undefined, max: number) =>
.filter((value, index, all) => all.indexOf(value) === index)
.slice(0, max);
// Owner-wide scans for old-workspace cleanup are bounded to this many rows so
// the query cost cannot grow without limit. The count is therefore reported as
// "up to N"; delete is per-call bounded and re-runnable to drain the rest.
const OLD_WORKSPACE_SCAN_LIMIT = 500;
const isDeletableWorkspace = (job: Doc<'agentJobs'>) =>
['failed', 'cancelled', 'timed_out'].includes(job.status) ||
['stopped', 'expired', 'failed'].includes(job.workspaceStatus ?? '');
@@ -1069,7 +1074,7 @@ export const countOldWorkspaces = query({
const jobs = await ctx.db
.query('agentJobs')
.withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
.collect();
.take(OLD_WORKSPACE_SCAN_LIMIT);
return jobs.filter(
(job) => isDeletableWorkspace(job) && job.updatedAt <= cutoff,
).length;
@@ -1091,7 +1096,7 @@ export const deleteOldWorkspaces = mutation({
const jobs = await ctx.db
.query('agentJobs')
.withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
.collect();
.take(OLD_WORKSPACE_SCAN_LIMIT);
const deletable = jobs
.filter((job) => isDeletableWorkspace(job) && job.updatedAt <= cutoff)
.sort((a, b) => a.updatedAt - b.updatedAt)