Skip to content

fix(self-host): stop tracing idle polls - #2252

Open
ntindle wants to merge 2 commits into
UsefulSoftwareCo:v2from
ntindle:fix/untraced-idle-polls
Open

ntindle wants to merge 2 commits into
UsefulSoftwareCo:v2from
ntindle:fix/untraced-idle-polls

Conversation

@ntindle

@ntindle ntindle commented Oct 10, 2026

Copy link
Copy Markdown

Part of #2194.

An idle self-host instance traces three polling loops: the scheduler's check for due runs (every second), self-host's check for queued provisioning jobs (every second), and the event-delivery check (every two seconds, about 15 spans a pass). On 2.0.0-beta.13 an idle instance stores 10,724 spans in 10 minutes, about 96% of them from these polls, so the bundled Motel store reaches its 1 GiB cap within hours. From then on Motel evicts on every export, which is the CPU and disk churn in #2194 (on our Unraid host, running beta.8, about 530 GB of writes a day).

Fix

Two commits, one per worker, each with its own changeset:

  1. Scheduler and provisioning. The scheduler's pass and the provisioning SELECT run with Effect.withTracerEnabled(false) (already used in packages/telemetry/src/isolate.ts). Each operation the scheduler finds still runs traced, in its own schedule.dispatch span, so oauth-reconnect-schedules still sees dispatcher roots. Provisioning jobs are traced as before. Sampling can't be used instead, because the telemetry layer forces sampled: true.
  2. Event delivery (packages/sdk/src/implementation/event-worker.ts, added after [bug] v2 self-host: idle container burns ~13% CPU and writes ~190 GiB/day once bundled Motel hits its 1 GiB cap #2194 was filed). The pass runs untraced; deliver turns tracing back on for each delivery attempt and each subscription it expires. Cloud's coordinator already runs with tracing on, so Cloud is unchanged.

Not in this PR

  • The profile-setup loop, workflow reconcile and apps.repository.recover still trace at idle (about 100 spans a minute). Existing scenarios assert their dispatch roots, so I left them.
  • Motel evicting on every export once it is at its cap: UsefulSoftwareCo/motel fix/size-bound-hysteresis (373a47e) addresses that.

Tested

  • bun run format changes nothing. The rest of bun run check passes on the branch as rebased onto v2 41d3982 (format check, lint, typecheck, protocol and contract checks), with no diagnostics in the changed lines. patches:check fails its own fixture self-test under git 2.43 on my build machine and passes under git 2.54; that is independent of this change.
  • E2E (on the branch before the rebase): oauth-reconnect-schedules and deploy-setup (member setup wakes profile setup) pass on self-host; the first fails if the per-operation dispatch span is removed. mcp-events on self-host and local-mcp-events on local pass.
  • Idle for 10 minutes on 2.0.0-beta.13, official vs. patched (both commits, plus the Self-host 2.0.0-beta.10: dashboard batch requests return 403 behind an HTTPS reverse proxy #2199 fix): stored spans 10,724 → 1,063, Motel store 45.4 → 9.6 MiB, CPU 2.21% → 1.23% of a core.
  • Running on our self-host since 2026-10-10.
oauth-reconnect-schedules.spec.ts
deploy-setup.spec.ts
hosted-schedules.spec.ts
scheduled-run-redeploy.spec.ts
oauth-scheduled-renewal.spec.ts
tool-runner-approvals.spec.ts
hosted-profile-scheduling.spec.ts
local-schedule-runs.spec.ts
schedule-restart.spec.ts
mcp-events.spec.ts
mcp-events-changes.spec.ts
mcp-events-accounts.spec.ts
mcp-events-grants.spec.ts
mcp-events-app-removal.spec.ts
local-mcp-events.spec.ts

🤖 Generated with Claude Code

ntindle and others added 2 commits October 10, 2026 01:36
Self-host checks for due scheduled runs and for queued provisioning jobs every
second, and traced every check: a schedule.dispatch span with its storage and
SQL spans, plus a root sql.execute span. An instance with no traffic exported
about 800 spans a minute into the bundled Motel store, which reaches its 1 GiB
bound within hours and then evicts on every export, writing to disk constantly.

A check that finds nothing now records nothing. Each scheduled operation a
check finds runs in its own schedule.dispatch trace, so the background work
under it keeps its dispatcher root, and each provisioning job is traced as
before. Local runs the same scheduler and stops tracing its idle checks too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-host and local look for due event deliveries every two seconds, and traced
every pass: an events.dispatch span with its delivery, subscription and
retention statements, about 15 spans each. On an instance with no traffic that
is about 450 spans a minute in the bundled Motel store, the largest idle source
since the event worker arrived.

A pass that finds nothing to deliver now records nothing. Each delivery attempt
and each subscription a pass expires is still traced, in its own trace. Cloud's
coordinator calls the same delivery code with tracing on and is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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