Skip to content

test(cluster): poll for seed materialization in copyModeBlobDeadlock before-hook#549

Merged
kriszyp merged 2 commits into
mainfrom
kris/fix-blob-deadlock-setup-race
Jul 8, 2026
Merged

test(cluster): poll for seed materialization in copyModeBlobDeadlock before-hook#549
kriszyp merged 2 commits into
mainfrom
kris/fix-blob-deadlock-setup-race

Conversation

@kriszyp

@kriszyp kriszyp commented Jul 8, 2026

Copy link
Copy Markdown
Member

Summary

copyModeBlobDeadlock.test.mjs's before() hook seeds 80 file-backed blobs on the source node via concurrent GETs, then asserts describe_table.record_count >= 80 immediately after Promise.all(seedPromises) resolves. That assertion races: a sourcedFrom GET resolves the HTTP response as soon as source.get() returns, while the cache-populating write (and record commit) happens in the background afterward — the fetches completing never actually guaranteed the records had landed.

harper#1641 (blob commit-gating: local writes now wait on the blob's durability/fsync before the record commits — a correct, intentional fix for an orphan-blob-reference bug) added real fsync-bound latency to that background commit path, widening this pre-existing race enough to flake under 80 concurrent 512KB blob writes on a loaded CI runner. This surfaced as AssertionError: source did not materialize blobs: holds 73/80 on #546 and #547 (both core-submodule-bump PRs whose new core commits include #1641), and per the coordinator, also on #512 and #504 after their main-merge conflict resolution — i.e. any PR touching current main/core now hits it.

This PR replaces the immediate post-Promise.all assertion with a poll loop that waits for the actual condition (record_count >= RECORDS, bounded by a 20s timeout), matching the pattern already used later in this same file and in receiveBacklogMemory.test.mjs.

Verification

Where to look

Test-only change, low risk. No core code touched (this is harper-pro's own test-timing assumption, not a core regression) — see diagnosis above for the full trace.

Kris, via Claude Sonnet 5

🤖 Generated with Claude Code

…w assumption

copyModeBlobDeadlock's before() hook asserted describe_table.record_count
immediately after Promise.all(seedPromises) resolved. A sourcedFrom GET
resolves the HTTP response as soon as source.get() returns; the
cache-populating write commits in the background afterward, so the fetches
completing never guaranteed the records had landed. harper#1641 (blob
commit-gating: local writes now wait on blob durability/fsync before
committing) widened that pre-existing race enough to make it flake under
concurrent 512KB blob writes, most recently on harper-pro#546/#547's core
bumps ("source did not materialize blobs: holds 73/80").

Poll describe_table for the actual condition instead, matching the pattern
already used in this file's second test and in receiveBacklogMemory.test.mjs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kriszyp kriszyp requested a review from kylebernhardy July 8, 2026 17:37

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

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 updates the copyModeBlobDeadlock.test.mjs integration test to poll for the expected record count instead of asserting immediately, ensuring background cache-populating writes and fsyncs have completed. The reviewer suggested enhancing the polling loop with a time-based deadline and transient error handling to prevent test flakiness under heavy concurrent disk I/O.

Comment thread integrationTests/cluster/copyModeBlobDeadlock.test.mjs
@claude

claude Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

@kriszyp kriszyp marked this pull request as ready for review July 8, 2026 17:55
@kriszyp kriszyp requested a review from a team as a code owner July 8, 2026 17:55
… seed poll

Per Gemini review on #549: align the new seed-materialization poll with the
time-based deadline + .catch(() => null) pattern already used a few lines
down in this file, instead of an iteration-count loop that would abort the
whole hook on a single transient sendOperation failure under the heavy
concurrent disk I/O this test drives (80x 512KB blob writes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@kriszyp kriszyp merged commit 5b774c5 into main Jul 8, 2026
45 of 47 checks passed
@kriszyp kriszyp deleted the kris/fix-blob-deadlock-setup-race branch July 8, 2026 18:09
@kriszyp kriszyp added the patch label Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Patch cherry-pick: merged

Cherry-picked onto v5.1.

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.

1 participant