Skip to content

Fail fast when the concurrent PATCH route is not ready - #2204

Draft
kriszyp wants to merge 1 commit into
mainfrom
test/concurrent-patch-readiness
Draft

Fail fast when the concurrent PATCH route is not ready#2204
kriszyp wants to merge 1 commit into
mainfrom
test/concurrent-patch-readiness

Conversation

@kriszyp

@kriszyp kriszyp commented Aug 18, 2026

Copy link
Copy Markdown
Member

Summary

  • Require the GET /CollabDoc/ readiness probe to return a successful response before QA-328 starts.
  • Close each probe response body and retain the last HTTP status or fetch error.
  • Throw after the existing 30-second deadline with the full endpoint, deadline, and last observation instead of silently entering the concurrency tests.

CI root cause

The failing Node 24 shard never returned from setupHarperWithFixture; the later route poll did not run. The harness eventually reported Harper produced no startup output for 150000ms before reporting ready.

The server artifact shows initial HTTP thread IDs 1-4, then replacement IDs 5-7, with no Harper successfully started message. manageThreads automatically replaces a worker that exits before readiness, but socketRouter on main waits on promises owned by the original Worker objects. A replacement creates a different promise, so the original startup barrier can never settle. The exit codes/triggers were not retained in that artifact, but the abandoned readiness barrier explains the 150-second hang after the exits.

Fix HTTP startup after pre-ready worker restart #2129 already fixes that product-level lifecycle defect by owning readiness per logical worker slot and includes an end-to-end crash/replacement regression. Its CI passes across the Node, Windows, uWS, and integration matrices. This PR does not duplicate that open fix; the two changes are complementary.

Testing

  • npm run build
  • npm run lint:required
  • Prettier check and git diff --check
  • QA-328 focused RocksDB suite on Node 22.23.1, 24.19.0, and 26.2.0: 7 passed, 0 failed, 0 cancelled on each
  • QA-328 focused LMDB suite on Node 26.2.0: 7 passed, 0 failed, 0 cancelled

Independent review: Gemini + Cursor Composer + Harper-domain. Claude was attempted but returned provider HTTP 529 after its retry budget.

Human-Review-Need: 3 (decisions: inline-poll-vs-shared-helper, readiness-predicate-2xx-vs-non-404, duplicate-qa328-suites, fail-fast-vs-fixing-the-flake) @ 10d7283

Co-Authored-By: GPT-5 Codex <noreply@openai.com>

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request improves the readiness polling logic in the concurrentPatchMerge integration test. It introduces robust error handling, tracks the last observed status or error, cancels the response body to prevent resource leaks, and throws a detailed error if the service fails to become ready within the 30-second deadline. There are no review comments, and I have no feedback to provide.

Comment on lines +65 to +90
const readinessURL = `${httpURL}/CollabDoc/`;
const readinessDeadlineMs = 30_000;
const deadline = Date.now() + readinessDeadlineMs;
let ready = false;
let lastObserved = 'no response';
while (Date.now() < deadline) {
try {
const probe = await fetch(`${httpURL}/CollabDoc/`, {
const probe = await fetch(readinessURL, {
headers: { Authorization: auth },
signal: AbortSignal.timeout(3_000),
});
if (probe.status !== 404) break;
} catch {
/* not ready yet */
lastObserved = `HTTP ${probe.status}`;
await probe.body?.cancel();
if (probe.ok) {
ready = true;
break;
}
} catch (error) {
lastObserved = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
}
await sleep(250);
}
if (!ready)
throw new Error(
`Harper did not become ready at ${readinessURL} within ${readinessDeadlineMs}ms; last observed ${lastObserved}`
);

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.

Suggestion (non-blocking): This re-implements the same poll/timeout/last-observed-error pattern as waitForRouteReady(client, probePath, timeoutMs) in integrationTests/apiTests/utils/lifecycle.mjs (extracted the same day in #1904), and client here (from createApiClient) already exposes the compatible reqRest() method. Reusing it would drop ~20 duplicated lines. The one real difference is the readiness predicate — the helper treats any non-404 as ready, while this test requires a true 2xx (probe.ok) — which the PR body already flags as an open decision. If that stricter predicate is needed here, consider adding an optional predicate parameter to the shared helper instead of a second bespoke implementation with different semantics.

@claude

claude Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found. Left one non-blocking suggestion inline about reusing the existing waitForRouteReady helper.

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