Skip to content

fix(migration): preserve records with file-backed blobs during v4→v5 migration#870

Merged
kriszyp merged 3 commits into
mainfrom
kris/857-blob-migration-rootstore
May 30, 2026
Merged

fix(migration): preserve records with file-backed blobs during v4→v5 migration#870
kriszyp merged 3 commits into
mainfrom
kris/857-blob-migration-rootstore

Conversation

@kriszyp
Copy link
Copy Markdown
Member

@kriszyp kriszyp commented May 29, 2026

Summary

During the v4 (LMDB) → v5 (RocksDB) startup migration, copyDbToRocks opened each source LMDB primary dbi with a RecordEncoder but never set its rootStore. Decoding any record holding a file-backed blob reference then ran with no store context, so the Blob msgpackr extension threw No store specified, cannot load blob from storage. RecordEncoder.decode swallows that error and returns null, and copyDbToRocks skips null values — so every record containing a file-backed blob was silently dropped on upgrade (small/inlined blobs and blob-less records were unaffected).

Fix: set sourceDbi.encoder.rootStore = sourceRootStore for the primary source dbi so blob references resolve against the source store during migration (the runtime read path gets this via handleLocalTimeForGets; the migration opens the source dbi raw).

Resolves #857.

Changes

  • bin/copyDb.ts: set rootStore on the source primary encoder; export copyDbToRocks so the regression test can drive it directly.
  • unitTests/bin/blobMigration.test.js: new regression test (LMDB engine) — migrates a file-backed blob, an inlined blob, and a no-blob record, asserts all survive and the blob content is intact.
  • DESIGN.md: documents the rootStore requirement next to the existing source-dbi compression note.

Where to look

  • The one-line behavioral change is if (isPrimary && sourceDbi.encoder) sourceDbi.encoder.rootStore = sourceRootStore;. Confirm rootStore is the correct store for resolving source blob references (it mirrors what handleLocalTimeForGets assigns at runtime).
  • Scope check: only the primary dbi decodes records (and thus blobs); index dbis copy ordered-binary values and the LMDB→LMDB copyDb compaction copies raw bytes (decoder/encoder nulled), so neither needs this. The fix is intentionally guarded to isPrimary.

Notes

  • Generated by Claude (Opus 4.7). An outside-model review (Codex) found no code-level concerns.
  • Open item for the reviewer: the unit test verifies the migrated record survives and the on-disk blob file content matches (source and target share the <hdbBasePath>/blobs/<databaseName> path and the same fileId is re-emitted). It does not decode the migrated record back through a v5 RocksDB store to confirm the reference resolves end-to-end — that full read-through belongs in the integrationTests/upgrade path. Flagging in case you'd prefer a stronger unit assertion.

Copy link
Copy Markdown

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

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 addresses an issue during the LMDB to RocksDB migration where records containing file-backed blobs were silently dropped because the primary store's encoder lacked a reference to the root store. The fix explicitly assigns rootStore to the primary store's encoder during migration. A regression test has been added to verify this behavior. The review feedback highlights that createBlob is used in the new test file without being imported, which will cause a runtime error, and notes that redundant await keywords are used on synchronous createBlob calls.

Comment thread unitTests/bin/blobMigration.test.js
Comment thread unitTests/bin/blobMigration.test.js
@kriszyp kriszyp requested review from ldt1996 and removed request for kylebernhardy May 29, 2026 13:21
Comment thread unitTests/bin/blobMigration.test.js
… migration

copyDbToRocks opened the source LMDB primary dbi with a RecordEncoder but never
set its rootStore. Decoding any record holding a file-backed blob reference then
threw "No store specified, cannot load blob from storage"; the error was swallowed
(record decoded to null) and the record was silently skipped, dropping every
record with a file-backed blob on upgrade (#857).

Set sourceDbi.encoder.rootStore = sourceRootStore so blob references resolve
against the source store during migration. Adds a regression test and documents
the requirement in DESIGN.md alongside the existing source-dbi compression note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 29, 2026

Reviewed; no blockers found. Prior finding (regression test not wired into CI) addressed by commit e12e7d226.

Pre-existing repo-wide prettier drift (a long require line on main, from
d28a16f) that fails the PR-only Format Check on every new PR. `npm run
format:write` produces exactly this; unrelated to the migration fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kriszyp kriszyp force-pushed the kris/857-blob-migration-rootstore branch from 5536dce to e84cb1a Compare May 29, 2026 13:27
…xecutes

The blob-migration regression test (and copyDB.test.js) require an LMDB source
and self-skip unless HARPER_STORAGE_ENGINE=lmdb. test:unit:lmdb only covered
resources + apitests, so the bin suite never ran under lmdb and the new test
silently no-opped in CI. Append the bin suite to test:unit:lmdb (50 bin tests
pass under lmdb locally).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@kriszyp kriszyp marked this pull request as ready for review May 29, 2026 13:58
@kriszyp kriszyp added the patch label May 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 29, 2026

Patch cherry-pick: conflict

Cherry-pick onto v5.0 produced conflicts on commit(s): e84cb1a52029ea08096e4c15b32b4f517dd50e61 e12e7d226e0186171c42b4cc03c3753a2e2a260d

The conflict markers are committed on branch cherry-pick/v5.0/pr-870.
A pull request has been opened to land this patch: #887

@kriszyp kriszyp merged commit 68b576a into main May 30, 2026
45 of 47 checks passed
@kriszyp kriszyp deleted the kris/857-blob-migration-rootstore branch May 30, 2026 19:20
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.

No store specified, cannot load blob from storage errors at startup

1 participant