Skip to main content
← Back to list
01Issue
BugClosedSwamp CLI
Assigneesstack72

Relationships

Blocked

#1285 serve: main JS thread holds ~40–70% of a core while idle (no clients, no runs, --no-schedule)

Opened by ehazlett · 7/20/2026

Summary

A serve process with no client connected, no active workflow runs, and --no-schedule set still holds a large, steady fraction of one core. The load is on the main event-loop thread, which points at a hot timer/poll rather than request work. Besides wasting a core, it starves request handling and produces multi-second tail latencies on otherwise sub-second reads.

Environment

  • swamp 20260718.081020.0-sha.fc14abbd
  • serve with token auth, --no-schedule, single repo (~581 data records; datastore has accumulated a sizeable WAL). Run history: 30 succeeded / 4 failed / a few suspended.

Measurements

Sampling /proc/<pid>/task/*/stat (utime + stime deltas) over several-second windows with no client connected:

  • Whole process: ~40–70% of one core, sustained, across repeated windows.
  • Per-thread over a 3s window:
    • main event-loop thread: ~38%
    • four V8 worker threads: ~1.7% each
    • tokio worker threads: ~0.3% each

Under an added read load the same process rose to ~112% and individual data.query calls slowed (~570ms to ~1000ms), i.e. the idle work contends with request handling.

Impact

  • One core consumed continuously at rest.
  • Tail latencies: an otherwise ~500ms read occasionally spikes to ~3.4s, consistent with the event loop being busy when the request arrives.

Suggested direction

  • Profile the main event-loop thread at rest (no clients) to find the hot timer/poll.
  • Confirm whether any internal loop ignores --no-schedule, or polls the datastore / WAL on a tight interval.

Repro

  1. Start serve with --no-schedule and no clients.
  2. Sample the process's CPU (e.g. /proc/<pid>/stat, or per-thread via /proc/<pid>/task/*/stat) over several seconds.
  3. Observe sustained main-thread CPU with zero external traffic.

The scan-cost issue #1284 (https://swamp-club.com/lab/1284) sets the ~500ms baseline for reads; this issue explains the multi-second tail on top of it.

02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED+ 1 MOREASSIGNED+ 5 MOREREVIEW

Closed

7/26/2026, 11:14:07 PM

No activity in this phase yet.

03Sludge Pulse
stack72 assigned stack727/24/2026, 12:03:48 AM
stack72 marked as blocked7/24/2026, 3:42:41 PM
Editable. Press Enter to edit.

stack72 commented 7/24/2026, 3:43:23 PM

@ehazlett We dug into this in detail — walked every timer, setInterval, for await loop, and event subscription in the serve path. With --no-schedule, the scheduler, workflow watcher, and cron subsystems are completely disabled. The only things alive at idle are the Deno.serve listener, signal handlers, and two open SQLite connections. None of them poll. The daemon mode is a plain systemd unit wrapper — no watchdog or health-check timer.

We tried reproducing across 4 configurations (empty repo, 600 catalog rows + 38 tracked runs with 3.9MB WAL, Deno.serve + SQLite combined) and got 0% idle CPU every time. We're on macOS though, so this could be Linux-specific.

Could you grab one of these while serve is idle?

Syscall summary (shows if something is polling):

sudo strace -cp $(pgrep -f 'swamp serve') &
sleep 10 && kill -INT $!

CPU hotspot snapshot (shows which function is burning CPU):

sudo perf top -p $(pgrep -f 'swamp serve')

Either one would point us directly at the cause.

ehazlett commented 7/24/2026, 5:41:12 PM

ok thanks for checking! let me try to get a reproducer and if i can't i'll close. thanks for digging!

ehazlett commented 7/26/2026, 2:53:55 PM

Closing this as environmental. I'll submit a new one if I can get a good repro case. Thanks!

stack72 commented 7/26/2026, 11:14:18 PM

Thanks @ehazlett - let me know if something shows up again!

Sign in to post a ripple.