Skip to content

test(harness): de-collide e2e ports — per-fork ranges + AddrInUse respawn - #713

Merged
jarvis9443 merged 1 commit into
mainfrom
test/e2e-spawn-port-toctou
Jul 3, 2026
Merged

test(harness): de-collide e2e ports — per-fork ranges + AddrInUse respawn#713
jarvis9443 merged 1 commit into
mainfrom
test/e2e-spawn-port-toctou

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Problem

Recurring e2e startup flake (twice in two days: sls-content-capture-responses-completions on #710's run, datadog-exporter on main run 28595570164):

timed out waiting for http://127.0.0.1:46453/metrics after 99 attempts (lastStatus=404): connect ECONNREFUSED
  binary state: aisix exited early with code=1 signal=null

pickFreePort binds :0, reads the assignment, closes the socket, and hands the port to aisix to re-bind a process spawn later. Under vitest's two parallel forks that check-then-use window races: the kernel can re-issue the port to the other fork (or a mock server) first. The lastStatus=404-then-ECONNREFUSED probe shape is the tell — the probe was answered by a different instance that had stolen the port, while our aisix died to AddrInUse.

Fix

  • ports.ts: allocate from a disjoint per-fork port range (VITEST_POOL_ID, pid fallback outside vitest) with a monotonic in-process cursor and a bind-verify probe. Ranges sit below the kernel's ephemeral range (32768+), so :0 assignments can never overlap them. No call-site changes.
  • app.ts: spawnApp respawns with fresh ports (max 3 attempts) only when aisix exited at startup with AddrInUse — the residual race vs unrelated external processes. Any other startup failure still fails the test immediately.
  • app.ts diagnostics: keep the stderr head in the failure excerpt. anyhow prints Error: … before its backtrace; the old tail-only slice cut exactly the line naming the failure, which is why the CI flake logs only showed backtrace frames.

Tests

  • src/harness/ports.test.ts: allocator range/uniqueness/bindability, occupied-port skip, and the AddrInUse failure classifier (positive + negative).
  • The respawn path's race itself can't be triggered deterministically (concurrency timing) — classifier is unit-tested instead.
  • Full local e2e suite with the new allocator: 117 files / 225 tests, all green.

…pawn

The harness picked ports by binding :0, reading the assignment, and
closing the socket, then handed the port to aisix to re-bind a spawn
later. Under vitest's two parallel forks that check-then-use window
races: a port picked in one fork can be re-issued to the other (or
grabbed by a mock server) first. Observed twice in two days as e2e
startup flakes: aisix exits 1 with AddrInUse while the readiness probe
sees a different instance answer 404 on the stolen port, then
ECONNREFUSED (sls-content-capture on PR #710, datadog-exporter on main).

- ports.ts: allocate from a disjoint per-fork range (VITEST_POOL_ID,
  pid fallback) with a monotonic cursor + bind-verify, below the
  kernel's ephemeral range so :0 assignments never overlap it. Call
  sites unchanged.
- app.ts: retry spawnApp with fresh ports (max 3 attempts) when aisix
  died at startup to AddrInUse — the residual race vs external
  processes; any other startup failure still surfaces immediately.
- app.ts: keep the stderr HEAD in the failure excerpt — anyhow prints
  the error line before its backtrace, and the old tail-only slice cut
  exactly the line naming the failure, which is why the CI flake was
  undiagnosable from the job log.

The respawn race itself is not deterministically testable (concurrency
timing); the allocator and the failure classifier are unit-tested in
src/harness/ports.test.ts, and the full 117-file suite passes with the
new allocator.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f1f15a29-ab97-47c5-8e94-d48aea193648

📥 Commits

Reviewing files that changed from the base of the PR and between bba94b5 and 1a6ae6c.

📒 Files selected for processing (3)
  • tests/e2e/src/harness/app.ts
  • tests/e2e/src/harness/ports.test.ts
  • tests/e2e/src/harness/ports.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/e2e-spawn-port-toctou

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

@jarvis9443
jarvis9443 merged commit bae570f into main Jul 3, 2026
9 checks passed
@jarvis9443
jarvis9443 deleted the test/e2e-spawn-port-toctou branch July 3, 2026 06:27
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