Skip to content

fix(table): dispatcher cold-start, live run counter, smooth typewriter#4675

Merged
TheodoreSpeaks merged 5 commits into
stagingfrom
fix/table-dispatcher-coldstart
May 20, 2026
Merged

fix(table): dispatcher cold-start, live run counter, smooth typewriter#4675
TheodoreSpeaks merged 5 commits into
stagingfrom
fix/table-dispatcher-coldstart

Conversation

@TheodoreSpeaks
Copy link
Copy Markdown
Collaborator

Summary

Three follow-up fixes after the chunked-dispatcher merge (#4672), each in its own commit.

  • Dispatcher cold-start (~6s → <2s queued→running): table-run-dispatcher imported lib/table/service for getTableById, which eagerly pulled lib/table/trigger@/lib/webhooks/processor → the whole workflow-executor stack into the dispatcher container. trigger.ts now lazy-imports the webhook processor inside fireTableTrigger, and buildEnqueueItems only imports the cell job (for the inline runner) on the database backend — the trigger.dev backend triggers by task id and ignores runner.
  • Live "X running" counter / gutter Stop button: useRunColumn optimistically stamped cells pending in the rows cache but never bumped runningCellCount/runningByRowId, so the dispatcher's real pending SSE saw no wasInFlight transition and the counter stayed at 0 until a refetch ("shows up on refresh"). onMutate now bumps the counter for the cells it stamps (with rollback on error), and onSuccess seeds the dispatch into the overlay from the response instead of refetching (which reset the counter to the server's still-zero count).
  • Typewriter degrades with many concurrent reveals: per-cell setInterval callbacks fired uncoordinated → O(cells) independent reflows over an un-virtualized grid. Switched to requestAnimationFrame (frame-batched, elapsed-time based) so all reveals render+paint once per frame.

Type of Change

  • Bug fix

Testing

Tested manually in staging (issues observed there); tsc, vitest (lib/table + hooks/queries, 202 passing), lint, and check:api-validation:strict all pass. Cold-start improvement diagnosed via trigger.dev run traces (dispatcher init dropped from ~8s).

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…ains

The trigger.dev table-run-dispatcher spent ~6s in module init before its
first batchTriggerAndWait — it imports lib/table/service for getTableById,
which eagerly imported lib/table/trigger → @/lib/webhooks/processor →
webhook-execution + executor, dragging the entire workflow-execution stack
into the dispatcher container even though it never fires a trigger.

- trigger.ts lazy-imports the webhook processor + polling utils inside
  fireTableTrigger (the only consumer), so importing service no longer pulls
  the executor.
- buildEnqueueItems only imports the cell job (for the inline `runner`) on the
  database backend; the trigger.dev backend triggers by task id and ignores
  runner.
The "X running" badge, per-row gutter Stop button, and runningByRowId map
stayed at zero after clicking Run until a manual refetch. useRunColumn
optimistically stamped cells pending in the rows cache but never bumped the
activeDispatches counter — so when the dispatcher's real pending SSE arrived,
applyCell saw the cell was already in-flight (wasInFlight === isInFlight) and
skipped the counter delta. The optimistic stamp ate the transition.

- onMutate now bumps runningCellCount / runningByRowId by the cells it stamps,
  snapshotting prior run-state for rollback on error.
- onSuccess seeds the dispatch into the overlay list from the response instead
  of invalidating activeDispatches (a refetch would reset the optimistic
  counter to the server's still-zero count before the dispatcher stamps).
… smooth

The character-by-character reveal used a per-cell setInterval. When many cells
reveal at once (a Run-all completing in waves), the independent interval
callbacks fire at uncoordinated times and each forces its own render +
layout/paint — O(cells) reflows over an un-virtualized grid, so it degrades as
more cells fill. Switch to requestAnimationFrame: all cells' callbacks run
before one paint, so React batches them into a single render + paint per frame
regardless of cell count. Reveal length is derived from elapsed time, so a
dropped frame catches up instead of slowing the animation.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 20, 2026 7:04pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 20, 2026

PR Summary

Medium Risk
Medium risk because it changes optimistic run-state accounting and dispatch overlay seeding in React Query, which can affect live run counters and cancellation UI if mismatched with SSE/server state. Server-side changes are mostly lazy-loading/perf optimizations but touch trigger firing and job enqueue options.

Overview
Improves table workflow run responsiveness and UI correctness by reducing cold-start costs, fixing live running counters, and smoothing cell reveal animations.

On the client, useRunColumn now tracks how many cells are optimistically stamped pending, bumps runningCellCount/runningByRowId in useTableRunState to match, rolls back on error, and seeds the new dispatch into the active-dispatch overlay from the mutation response (avoiding a refetch that would zero out the optimistic count).

On the server, fireTableTrigger lazily imports webhook utilities/processor, and buildEnqueueItems only imports/provides the inline runner when not using trigger.dev. In the grid, the typewriter effect switches from per-cell setInterval to elapsed-time requestAnimationFrame updates to batch renders across many cells.

Reviewed by Cursor Bugbot for commit 745a5a3. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

Three targeted bug fixes following the chunked-dispatcher merge: a dispatcher cold-start reduction via lazy imports, an optimistic live-counter fix for the "X running" badge, and a requestAnimationFrame-based typewriter to batch concurrent cell reveals into a single paint per frame.

  • trigger.ts / workflow-columns.ts: fetchActiveWebhooks and processPolledWebhookEvent are now lazy-imported inside fireTableTrigger, and buildEnqueueItems skips the executor import entirely on trigger.dev — both changes prevent the workflow-executor stack from loading in the dispatcher container at startup.
  • tables.ts: onMutate in useRunColumn now bumps runningCellCount/runningByRowId optimistically (with rollback on error), and onSuccess seeds the dispatch directly from the response rather than calling invalidateQueries (which would have reset the counter to the server's still-zero value).
  • cell-render.tsx: Typewriter switches from setInterval to an elapsed-time requestAnimationFrame loop so all concurrent cell reveals share one render+paint cycle per frame.

Confidence Score: 3/5

Safe to merge for the cold-start and typewriter fixes; the live-counter change has a gap where the counter can become permanently stuck if the server returns success without a dispatchId.

The onSuccess handler replaces invalidateQueries with a targeted cache update, but returns early without any counter cleanup when dispatchId is undefined. In that scenario the optimistic runningCellCount bump from onMutate has no rollback path — onError won't fire (the mutation succeeded) and no SSE will arrive for a dispatch that was never created, leaving the badge stuck. The lazy-import and rAF changes are straightforward and carry no meaningful risk.

apps/sim/hooks/queries/tables.ts — specifically the onSuccess handler in useRunColumn

Important Files Changed

Filename Overview
apps/sim/hooks/queries/tables.ts Adds optimistic runningCellCount/runningByRowId bump in onMutate with rollback on error, and replaces invalidateQueries on success with a direct cache upsert; missing rollback path when the server returns success without a dispatchId.
apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render.tsx Replaces setInterval-based typewriter with an elapsed-time requestAnimationFrame loop; cleanup and timing logic are correct.
apps/sim/lib/table/trigger.ts Moves fetchActiveWebhooks and processPolledWebhookEvent to lazy inline imports to avoid pulling the entire executor stack into consumers of lib/table/service; logic is unchanged.
apps/sim/lib/table/workflow-columns.ts Skips the workflow-column-execution dynamic import and omits runner from enqueue options when trigger.dev is enabled, preventing cold-start from loading the executor stack in the dispatcher container.

Sequence Diagram

sequenceDiagram
    participant UI as UI (useRunColumn)
    participant QC as QueryClient Cache
    participant API as Run Column API
    participant SSE as SSE Stream

    UI->>QC: onMutate: stamp cells pending, bump runningCellCount optimistically
    UI->>API: POST /run-column
    API-->>UI: "200 { dispatchId }"
    alt dispatchId present
        UI->>QC: onSuccess: upsert ActiveDispatch into cache (counter preserved)
    else dispatchId absent
        UI->>UI: return early — counter NOT rolled back
    end
    SSE-->>QC: cell events update counter as dispatcher processes rows
    Note over UI,SSE: onError rolls back both row snapshots and counter bump
Loading

Reviews (1): Last reviewed commit: "fix(table): drive cell typewriter with r..." | Re-trigger Greptile

Comment thread apps/sim/hooks/queries/tables.ts
useRunColumn.onSuccess returned early on a null dispatchId (no matching
groups / eligible rows) without undoing the onMutate counter bump — and no
SSE would arrive to correct it, leaving the counter permanently inflated.
Restore the pre-mutation run-state on that path, mirroring onError.
@TheodoreSpeaks TheodoreSpeaks force-pushed the fix/table-dispatcher-coldstart branch from fa34ae7 to 745a5a3 Compare May 20, 2026 19:04
@TheodoreSpeaks TheodoreSpeaks merged commit af8025c into staging May 20, 2026
14 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/table-dispatcher-coldstart branch May 20, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant