Skip to content

TTLS: fan-out completion checks are O(N²) event-log loads — return a pending-step count on step-terminal writes #3455

Description

@TooTallNate

Problem

At high fan-out, TTLS (time to last step) falls off a cliff: durabench parallel sweeps measure ~15–20s TTLS at 256 branches vs ~1.3s at 64, on every SDK version (node engine, iad1). Trace decomposition of a representative run (wrun_41KZR0MW890GWZK34RD4Y1JBDT) shows the dominant cause is O(N²) event-log read traffic:

  • Every queued step's consumer, after completing its step, loads the run's full event log to answer "are all parallel steps done?" (the last completer replays inline). At N branches that is N loads of an ~4N-event log.
  • Measured server-side: GET /api/v4/runs/:runId/events p90 = 7.9s during the burst; the saturation drags the consumer prologue's runs.get to p90 = 5.1s, smearing branch starts over ~10–15s.
  • Not throttling: zero 425/429 on the run.

Proposal

Let the step-terminal write answer the question the events.list is asking. The World's events.create for step_completed / step_failed returns an optional count of the run's non-terminal steps after the write (or a boolean "none pending"); the runtime then:

  • skips the events.list entirely when pending > 0 (return, another handler finishes later);
  • only the true last completer (pending === 0) proceeds — and only IT loads the log for the inline replay.

That converts O(N) full-log loads per fan-out into exactly one.

World interface impact

Additive and backward-compatible, following the sinceCursor / inline-delta precedent:

  • CreateEventParams: opt-in request flag (e.g. pendingStepCount: true) so backends that must do extra work only do it when asked.
  • EventResult: optional pendingStepCount?: number on step-terminal writes. Absent → runtime falls back to today's events.list check, so old/unsupporting Worlds are unaffected.
  • world-local / world-postgres: cheap direct count. The Vercel backend can maintain a pending counter on the run row (it already patches the step row in the same write) — tracked separately server-side.

Measurement

Baseline and after: durabench parallel sweep at branches {64, 256, 1024}, plus the pnpm phases decomposition (event-log-loads phase should collapse). Expected: the 256-branch TTLS cliff drops toward the queue-transit floor.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions