perf(core): continue partial run_started preloads from cursor - #3124
perf(core): continue partial run_started preloads from cursor#3124NathanColosimo wants to merge 6 commits into
Conversation
🦋 Changeset detectedLatest commit: 5e704ff The changes in this PR will be included in the next version bump. This PR includes changesets to release 20 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📊 Workflow Benchmarkscommit Backend:
📜 Previous results (5)c3b400bMon, 27 Jul 2026 23:32:01 GMT · run logs
a82f28cMon, 27 Jul 2026 22:25:01 GMT · run logs
ef184e4Mon, 27 Jul 2026 22:01:04 GMT · run logs
ef184e4Mon, 27 Jul 2026 21:02:39 GMT · run logs
2f2e9e9Mon, 27 Jul 2026 19:19:43 GMT · run logs
ℹ️ Metric definitions & methodologyBest/P75/P90/P99 deltas compare against the most recent benchmark run on Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) · SO: stream overhead (end-to-end write+consume time beyond the modelled generation window) Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) · stream overhead (text): writer streams 300 variable-length text token deltas paced at 100/s for 3s (a haiku-size LLM's token throughput) while a parallel reader drains the whole stream; SO is the end-to-end write+consume time beyond the 3s generation window (overhead/backpressure) · stream overhead (structured): same workload as stream overhead (text), but each delta is an AI-SDK-style structured object ({ type: 'text-delta', id, text }) instead of a raw string, so the SO gap vs the text scenario is the added serialization cost 🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · SO 250/500/1000 · STSO (1-20) 20/30/60 · STSO (101-120) 30/45/90 · STSO (1001-1020) 40/60/120 All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor ( Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the |
🧪 E2E Test Results❌ Some tests failed ❌ Failed E2E Tests▲ Vercel Production (1 failed)astro (1 failed):
E2E Test SummarySummary
Details by Category❌ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 📋 Other
✅ vercel-multi-region
|
Summary
MutableEventLogsnapshot instead of parallel optional valuesrun_startedand continue strictly after their cursorevents.createresults from the request event type, making required run/step entities and theirstartedAttimestamps non-optionalrun_created,run_started, andstep_startedWhy
A fresh workflow invocation must replay its durable event log. The server already reads
run_createdandrun_startedto recover an idempotent duplicate start, so the SDK should keep those events and continue after their cursor instead of loading the same prefix again.The World interface also previously collapsed every event response into one broad
EventResultbag whererun,step, and their start timestamps were optional. The implementations guarantee those values for runtime setup events, so the public type now expresses that contract and callers use the returned values directly.Flow
run_startedreturns its materialized run and may include a resolved lifecycle prefix pluscursor/hasMore.loadWorkflowRunEvents(runId, cursor).{ events, cursor }snapshot for replay.EventResultFor<T>narrows the materialized entity forrun_created,run_started, andstep_started, so runtime code does not re-check interface guarantees.If an older World omits the cursor, the SDK performs the prior full load. If a World rejects cursor pagination with HTTP 400 and the loader retries from the beginning, the merge removes prefix IDs once.
Verification
pnpm --filter @workflow/core test— 1,505 passed, 3 expected failurespnpm --filter @workflow/world-local test— 478 passedpnpm --filter @workflow/world-vercel test— 296 passedpnpm --filter @workflow/world-postgres test— 162 passedpnpm typecheck— passedpnpm build— 27/27 taskspnpm lintremains blocked by existing generated docs/workbench diagnostics unrelated to this PR