Skip to content

Commit b00dbfa

Browse files
fix(tables): dedupe usage-limit event + release rate-limited cells on cancel
Addresses PR review: - Usage limit: only the cell that transitions the dispatch active→complete (via completeDispatchIfActive) emits usageLimitReached, so concurrent cells don't fire up to 20 identical "upgrade" toasts. - Rate-limit retry: re-check the cancelled tombstone after each sleep so a Stop All mid-wait releases the concurrency slot promptly (signal never fires on the trigger.dev backend).
1 parent aca010b commit b00dbfa

2 files changed

Lines changed: 41 additions & 10 deletions

File tree

apps/sim/background/workflow-column-execution.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,18 +429,25 @@ async function runWorkflowAndWriteTerminal(
429429
logger.warn(
430430
`Usage limit reached — halting dispatch (table=${tableId} row=${rowId} group=${groupId})`
431431
)
432+
// With up to 20 concurrent cells all hitting the limit at once, only
433+
// the cell that actually transitions the dispatch active→complete
434+
// emits the event — otherwise the user sees a toast per in-flight
435+
// cell. Cells with no owning dispatch (auto-fire) always emit.
436+
let shouldEmit = true
432437
if (dispatchId) {
433-
const { markDispatchComplete } = await import('@/lib/table/dispatcher')
434-
await markDispatchComplete(dispatchId)
438+
const { completeDispatchIfActive } = await import('@/lib/table/dispatcher')
439+
shouldEmit = await completeDispatchIfActive(dispatchId)
440+
}
441+
if (shouldEmit) {
442+
await appendTableEvent({
443+
kind: 'usageLimitReached',
444+
tableId,
445+
dispatchId: dispatchId ?? '',
446+
message:
447+
preprocess.error?.message ??
448+
'Usage limit exceeded. Please upgrade your plan to continue.',
449+
})
435450
}
436-
await appendTableEvent({
437-
kind: 'usageLimitReached',
438-
tableId,
439-
dispatchId: dispatchId ?? '',
440-
message:
441-
preprocess.error?.message ??
442-
'Usage limit exceeded. Please upgrade your plan to continue.',
443-
})
444451
return 'blocked'
445452
}
446453
await writeState({
@@ -486,6 +493,12 @@ async function runWorkflowAndWriteTerminal(
486493
`Rate limited — waiting ${Math.round(waitMs)}ms before retry ${attempt + 1} (table=${tableId} row=${rowId} group=${groupId})`
487494
)
488495
await sleep(waitMs)
496+
// Stop All can land mid-wait. On the trigger.dev backend `signal` never
497+
// fires (cancelByKey is a no-op there), so re-check the DB tombstone and
498+
// release this concurrency slot promptly instead of sleeping out the
499+
// full retry budget.
500+
const refreshed = await getRowById(tableId, rowId, workspaceId)
501+
if (!refreshed || cancelledBeforeRun(refreshed.executions?.[groupId])) return 'error'
489502
}
490503

491504
// SQL guard also rejects if a stop click stamped `cancelled` between this

apps/sim/lib/table/dispatcher.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,24 @@ export async function markDispatchComplete(dispatchId: string): Promise<void> {
646646
.where(eq(tableRunDispatches.id, dispatchId))
647647
}
648648

649+
/** Complete a dispatch only if it's still active, returning whether THIS call
650+
* performed the transition. Lets concurrent cells that all hit a hard stop
651+
* (e.g. usage limit) elect a single owner — only the winner emits the
652+
* user-facing event, instead of one toast per in-flight cell. */
653+
export async function completeDispatchIfActive(dispatchId: string): Promise<boolean> {
654+
const transitioned = await db
655+
.update(tableRunDispatches)
656+
.set({ status: 'complete', completedAt: new Date() })
657+
.where(
658+
and(
659+
eq(tableRunDispatches.id, dispatchId),
660+
inArray(tableRunDispatches.status, [...ACTIVE_DISPATCH_STATUSES])
661+
)
662+
)
663+
.returning({ id: tableRunDispatches.id })
664+
return transitioned.length > 0
665+
}
666+
649667
export async function markDispatchCancelled(dispatchId: string): Promise<void> {
650668
await db
651669
.update(tableRunDispatches)

0 commit comments

Comments
 (0)