Skip to content

test(http): gate idle-WebSocket CPU check on the quietest sample, not the last - #34576

Merged
Jarred-Sumner merged 1 commit into
mainfrom
farm/66b5ac34/ws-idle-cpu-fixture-quantization
Jul 22, 2026
Merged

test(http): gate idle-WebSocket CPU check on the quietest sample, not the last#34576
Jarred-Sumner merged 1 commit into
mainfrom
farm/66b5ac34/ws-idle-cpu-fixture-quantization

Conversation

@robobun

@robobun robobun commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

test/js/bun/http/bun-server.test.tsshould not use 100% CPU when websocket is idle has been going red on the Windows 11 aarch64 lane in roughly one build in six (e.g. 75052, 75150, 75179), always with the same shape:

CPU Usage: 0.00%
CPU Usage: 0.00%
CPU Usage: 6.20%
error: expect(received).toBe(expected)  Expected: 0  Received: 1

Cause

The fixture from #25475 samples process.cpuUsage() three times at 1-second intervals and exits nonzero if the third sample is >= 2%. On Windows, process.cpuUsage() is backed by GetProcessTimes, which accounts CPU time at timer-tick boundaries (the default tick is ~15.625ms). A sample therefore reads as a multiple of ~1.56%: one tick is ~1.56%, two ticks ~3.12%, and so on. A couple of background-thread wakeups (mimalloc scavenger, JSC timers, TLS) that happen to straddle a tick boundary during the third second are enough to cross 2% with the event loop properly idle.

Measured on a Windows 11 aarch64 box over 100 runs of the unmodified fixture: 15/100 had the third sample >= 2% (max 13.85%), while the minimum across the three samples was 0 in 97 runs and never exceeded 3.05%. The same assertion also fails under the local ASAN debug build on Linux (first sample ~28%, third ~3.7%) for the same overhead reason.

Fix

The #25475 regression is a busy-spinning event loop that holds every sample near 100%, so the minimum across the three samples is the right statistic to test "not busy-looping": an idle loop always has at least one quiet sample, a spinning loop has none. Gate on that minimum and use the same 50% bound that test/regression/issue/21654 already uses for its spin-loop-vs-idle check. The regression would still read ~100% on all three samples and fail; scheduler/tick noise cannot reach it.

Verification

  • Windows 11 aarch64, fixture run directly: 0/100 failures after (15/100 before).
  • Windows 11 aarch64, via the test runner: 0/20 failures.
  • bun bd test test/js/bun/http/bun-server.test.ts -t "should not use 100% CPU" on Linux now passes (was failing under ASAN).
  • Sanity check: a simulated busy loop (setImmediate chain) reports min ≈ 100% and the fixture exits 1.

Open PR #33956 bumps the same threshold to 15% for darwin-arm64 only; this change is platform-agnostic and subsumes that hunk.


no test proof · iteration 0 · docs-only change; test-proof not applicable

The 'should not use 100% CPU when websocket is idle' fixture takes three
1-second CPU% samples and exits nonzero when the last one is >= 2%.
That threshold is inside the noise floor on Windows, where
GetProcessTimes (and so process.cpuUsage) accounts CPU time in ~15.6ms
timer ticks: a couple of background-thread wakeups charged to the
process during the third second read as 3-14%, and the test goes red
with the event loop properly idle. On Windows 11 aarch64 CI this was
failing in about 15% of runs; it also failed under the local ASAN debug
build on Linux for the same reason.

The regression this guards against (#25475) is a busy-spinning event
loop that holds every sample near 100%, so gate on the minimum of the
three samples and use the same 50% bound as
test/regression/issue/21654. An idle loop always has at least one quiet
sample well under that; the spin-loop has none.
@robobun

robobun commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 1:59 AM PT - Jul 18th, 2026

@robobun, your commit a7f9de0 has 2 failures in Build #75187 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 34576

That installs a local version of the PR into your bun-34576 executable, so you can run:

bun-34576 --bun

@robobun

robobun commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator Author

Reproduced on Windows 11 aarch64 by running the fixture 100 times with the canary bun: 15/100 exited 1 with the third sample in the 3-14% range (all tick multiples of ~1.56%). After this change: 0/100.

CI build 75187: bun-server.test.ts is green on every lane including Windows 11 aarch64. The remaining failures are unrelated to this diff: test-worker-message-port-transfer-terminate.js (JSC assertion, debian-x64-asan) and no-orphans.test.ts (darwin-26-aarch64) are pre-existing on main; the rest passed on retry.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The WebSocket CPU fixture now records the minimum CPU usage observed across sampling intervals and uses that value, with a threshold of 50, to determine the process exit result.

Changes

WebSocket CPU sampling decision

Layer / File(s) Summary
Track minimum CPU usage and exit threshold
test/js/bun/http/bun-websocket-cpu-fixture.js
Initializes and updates a minimum CPU usage accumulator, then bases the exit decision on whether the quietest observed sample is below 50.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: using the quietest CPU sample instead of the last one for the idle-WebSocket test.
Description check ✅ Passed The description explains the change and verification well, though it does not use the repository's required template headings.

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — well-justified de-flake that keeps asserting the property the test protects.

What was reviewed:

  • Confirmed the min-of-3-samples + 50% bound still catches the #25475 busy-spin regression (all samples ~100%) while absorbing Windows GetProcessTimes tick granularity and ASAN overhead.
  • Verified the 50% precedent cited at test/regression/issue/21654/21654.test.ts:194 for the same spin-vs-idle distinction.
  • Checked the parent test in bun-server.test.ts — it only asserts on exit code, so no other call sites need updating.
Extended reasoning...

Overview

This PR touches a single test fixture, test/js/bun/http/bun-websocket-cpu-fixture.js, spawned by the "should not use 100% CPU when websocket is idle" test in bun-server.test.ts. The fixture samples process.cpuUsage() three times at 1s intervals; previously it exited nonzero if the third sample was ≥2%. The change tracks the minimum across all three samples and gates on min < 50% instead.

Security risks

None. Test-fixture-only change; no runtime, native, or user-facing code touched.

Level of scrutiny

Low. This is a targeted CI de-flake with a thorough root-cause analysis (Windows GetProcessTimes charges CPU in ~15.6ms tick multiples, so a single background wakeup can read as ~1.56–3.12%) and empirical before/after data (15/100 → 0/100 on the affected lane). The key REVIEW.md concern for de-flakes — "keep asserting the property the original assertion protected" — is satisfied: the #25475 regression is a busy-spinning event loop that pins every sample near 100%, so min < 50 still fails on it. The PR explicitly verified this with a setImmediate chain. The 50% bound matches existing precedent at test/regression/issue/21654/21654.test.ts:194 for the same spin-loop-vs-idle check.

Other factors

The threshold widening (2% → 50%) is significant but not silent: the in-code comment and PR description document the rationale, and the statistic change (last → min) is what actually eliminates the flake — the PR's own data shows the min never exceeded 3.05% across 100 runs. The parent test only inspects the fixture's exit code, so no coordinated changes elsewhere are needed. No prior reviews on the PR to address.

@Jarred-Sumner
Jarred-Sumner merged commit f0d2eaf into main Jul 22, 2026
77 of 79 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/66b5ac34/ws-idle-cpu-fixture-quantization branch July 22, 2026 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants