Skip to content

fix(indexing): treat shutdown-closed store during backfill as benign; de-flake reindex test - #1476

Merged
kriszyp merged 2 commits into
mainfrom
kris/vecindex-shutdown-stuck
Jul 4, 2026
Merged

fix(indexing): treat shutdown-closed store during backfill as benign; de-flake reindex test#1476
kriszyp merged 2 commits into
mainfrom
kris/vecindex-shutdown-stuck

Conversation

@kriszyp

@kriszyp kriszyp commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

Fixes the ~50% CI flake on integrationTests/server/vector-index-integrity.test.ts ("reindex backfill") and removes misleading error noise during worker shutdown.

Root cause (investigated with instrumentation + local repro)

When restart_service http_workers tears a worker down mid-backfill, runIndexing's range scan/puts throw against the closing store (Database not open). The new worker generation re-runs the backfill via the existing crash-recovery trigger (indexingPID !== process.pid / restartNumber < currentRestartGeneration). During that recovery-reindex window the index legitimately reports isIndexing=true, so searches transiently return 503 INDEX_REBUILDING. The test's step-4 gate waited for a single successful search, then step 5 fired searches with no retry — a transient 503 in that window failed the test.

Instrumented/amplified local runs confirmed the index always recovers (no permanent stuck state); the failure is the transient 503 plus benign-but-misleading teardown logging (Error in indexing + Failed to persist indexing failure state).

Changes

  • resources/databases.ts — in runIndexing's outer catch and per-attribute error catch, treat a store closed by shutdown (primaryStore.rootStore?.status === 'closed') as a benign interruption: early-return without the doomed indexingFailed persist, log at debug. Recovery behavior is unchanged (handled by the pid/restartNumber trigger).
  • integrationTests/server/vector-index-integrity.test.tsvectorSearchStable() retries a transient INDEX_REBUILDING (503) up to 30s; used in steps 5/6. A permanently-stuck index (never clears) still surfaces the 503 and fails.
  • unitTests/resources/indexRestartNumber.test.js — new test for the shutdown-interruption path (runIndexing resolves; indexingFailed not set).

Where to look / what to verify

  • The status === 'closed' early-return is gated so it only skips a persist that could not have succeeded anyway (closed store). On LMDB, dbisDB shares the env with primaryStore, so the prior dbisDB.put already failed there too — no recovery regression. On RocksDB (CI), resetDatabases() never closes the store, so closed only occurs on real worker teardown → recovery via a new generation.

Cross-model review (DLC step 10 — thorough)

Codex + Gemini + Harper domain pass. No open blockers. Two findings surfaced, both confirmed pre-existing / out of scope, not introduced here:

  • LMDB same-process resetDatabases() mid-backfill can leave an index stuck — this predates the change (the old persist already failed against the same closed env; recovery there doesn't depend on indexingFailed). LMDB is not the indexing/replication target path. Flagged for awareness, not fixed here.
  • Non-last-put unhandled rejection for multi-value attributes — already documented in-code as pre-existing and out of scope.

Generated by an LLM (Claude Opus 4.8).

@kriszyp
kriszyp requested a review from kylebernhardy June 24, 2026 13:34

@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 resilience of vector indexing during worker shutdowns by treating closed stores as benign interruptions rather than errors, and introduces a robust vectorSearchStable helper to handle transient indexing states in integration tests. The review feedback suggests minor improvements: simplifying the sleep utility in tests by using an existing import, and preserving error context in debug logs when logging interrupted indexing operations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread integrationTests/server/vector-index-integrity.test.ts Outdated
Comment thread resources/databases.ts Outdated
Comment thread resources/databases.ts
@claude

claude Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

@kriszyp
kriszyp marked this pull request as ready for review June 24, 2026 14:18
Kris Zyp and others added 2 commits July 4, 2026 05:56
… de-flake reindex test

When `restart_service http_workers` tears a worker down mid-backfill, runIndexing's
range scan/puts throw against the closing store ("Database not open"). The old catch
logged a misleading error and then tried to persist `indexingFailed` against the
already-closed store, which also failed ("Failed to persist indexing failure state").
Treat a store closed by shutdown (`primaryStore.rootStore.status === 'closed'`) as a
benign interruption: skip the doomed persist and log at debug. Recovery is unchanged —
the next worker generation re-runs the backfill via the existing crash-recovery trigger
(indexingPID !== process.pid / restartNumber < current generation).

Also de-flakes the reindex integration test: a recovery-reindex window after the
restart-interrupted first backfill legitimately returns a transient INDEX_REBUILDING
(503), which the test's single-success gate didn't tolerate. A new vectorSearchStable()
polls past the transient window; a permanently-stuck index (never recovers) still fails.

Adds a unit test for the shutdown-interruption path (runIndexing resolves, no indexingFailed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… in test

Addresses Gemini review: pass the underlying error to logger.debug on the
shutdown-interruption paths so the root cause (e.g. "Database not open") is captured;
use the already-imported sleep() helper in vectorSearchStable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kriszyp
kriszyp force-pushed the kris/vecindex-shutdown-stuck branch from c6e6baa to 3671b81 Compare July 4, 2026 11:56
@kriszyp
kriszyp merged commit 228eacc into main Jul 4, 2026
49 of 50 checks passed
@kriszyp
kriszyp deleted the kris/vecindex-shutdown-stuck branch July 4, 2026 12:42
kriszyp added a commit that referenced this pull request Jul 6, 2026
…durable-session test

`subscribe with QoS=1 and reconnect with non-clean session` sequenced connect/
disconnect/publish steps with hard-coded `delay(10)`/`delay(50)` calls instead
of waiting for the actual server-side state to settle. `client.endAsync()`
only resolves once the local socket closes — it does not wait for the broker
to finish tearing down the durable session — so reconnecting with the same
clientId immediately after could race the broker (issue #1138 flagged this
file as the #2-worst offender for this pattern). Under CI load the delays
were occasionally too short, causing the intermittent "got 0 messages"/mocha
timeout flakes seen on #410 and #1476.

Replace the disconnect-then-reconnect delays with a wait on the broker's own
`disconnected` event (already exposed via `server.mqtt.events`) for the
matching clientId, and replace the pre-disconnect delay with a wait for the
client's outgoing PUBACK. Also await the first offline publish (it was
fire-and-forget while the other two were awaited), so all three publishes are
durably committed before the reconnecting client asserts on redelivery.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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