Skip to content

Commit 2727b4c

Browse files
fix(table): fire-and-forget run-column dispatch
Large fan-outs (thousands of rows) issue sequential trigger.dev HTTP calls inside scheduleRunsForRows.batchEnqueue. Awaiting that loop held the HTTP response (and the AI tool span) open for ~5 min on a 6k-row table — the user saw an 11-min "running" because the tool didn't return until every job had been enqueued. Run the dispatcher in the background and return immediately; contract response now reports `triggered: null` since the count isn't known synchronously.
1 parent b23ba1b commit 2727b4c

3 files changed

Lines changed: 23 additions & 6 deletions

File tree

apps/sim/app/api/table/[tableId]/columns/run/route.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createLogger } from '@sim/logger'
2+
import { toError } from '@sim/utils/errors'
23
import { type NextRequest, NextResponse } from 'next/server'
34
import { runColumnContract } from '@/lib/api/contracts/tables'
45
import { parseRequest } from '@/lib/api/server'
@@ -29,15 +30,21 @@ export const POST = withRouteHandler(async (request: NextRequest, { params }: Ro
2930
const access = await checkAccess(tableId, auth.userId, 'write')
3031
if (!access.ok) return accessError(access, requestId, tableId)
3132

32-
const { triggered } = await runWorkflowColumn({
33+
// Dispatch in the background — large fan-outs (thousands of rows) issue
34+
// sequential trigger.dev calls and would otherwise hold the HTTP response
35+
// open for minutes, blocking the AI/copilot tool span and the UI mutation.
36+
void runWorkflowColumn({
3337
tableId,
3438
workspaceId,
3539
groupIds,
3640
mode: runMode,
3741
rowIds,
3842
requestId,
43+
}).catch((err) => {
44+
logger.error(`[${requestId}] run-column dispatch failed:`, toError(err).message)
3945
})
40-
return NextResponse.json({ success: true, data: { triggered } })
46+
47+
return NextResponse.json({ success: true, data: { triggered: null } })
4148
} catch (error) {
4249
if (error instanceof Error && error.message === 'Invalid workspace ID') {
4350
return NextResponse.json({ error: 'Invalid workspace ID' }, { status: 400 })

apps/sim/lib/api/contracts/tables.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,12 @@ export const runColumnContract = defineRouteContract({
895895
body: runColumnBodySchema,
896896
response: {
897897
mode: 'json',
898-
schema: successResponseSchema(z.object({ triggered: z.number() })),
898+
/**
899+
* `triggered` is `null` when the dispatcher runs in the background — the
900+
* actual count is only known after a fan-out that may be tens of thousands
901+
* of rows, and we don't hold the HTTP response open for that long.
902+
*/
903+
schema: successResponseSchema(z.object({ triggered: z.number().nullable() })),
899904
},
900905
})
901906

apps/sim/lib/copilot/tools/server/table/user-table.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,19 +1415,24 @@ export const userTableServerTool: BaseServerTool<UserTableArgs, UserTableResult>
14151415
}
14161416
const requestId = generateId().slice(0, 8)
14171417
assertNotAborted()
1418-
const { triggered } = await runWorkflowColumn({
1418+
// Dispatch in the background — large fan-outs (thousands of rows)
1419+
// issue sequential trigger.dev calls and would otherwise hold the
1420+
// tool span open for minutes, blocking the chat connection.
1421+
void runWorkflowColumn({
14191422
tableId: args.tableId,
14201423
workspaceId,
14211424
groupIds,
14221425
mode: runMode,
14231426
rowIds,
14241427
requestId,
1428+
}).catch((err) => {
1429+
logger.error(`[${requestId}] run_column dispatch failed`, err)
14251430
})
14261431
const scopeLabel = rowIds ? `${rowIds.length} row(s) by id` : runMode
14271432
return {
14281433
success: true,
1429-
message: `Triggered ${triggered} row(s) across ${groupIds.length} column(s) (${scopeLabel})`,
1430-
data: { triggered },
1434+
message: `Started running ${groupIds.length} column(s) (${scopeLabel}). Cells will populate as workflows complete.`,
1435+
data: { triggered: null },
14311436
}
14321437
}
14331438

0 commit comments

Comments
 (0)