Skip to content

Fix segfault when SETting an indexed embedding while a live HNSW index exists (#896) - #78

Merged
adsharma merged 1 commit into
mainfrom
fix-896-hnsw-set-null
Sep 2, 2026
Merged

Fix segfault when SETting an indexed embedding while a live HNSW index exists (#896)#78
adsharma merged 1 commit into
mainfrom
fix-896-hnsw-set-null

Conversation

@adsharma

@adsharma adsharma commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes the vector-extension half of LadybugDB/ladybug#896.

Problem

SET n.embedding = $vec on a node whose embedding is NULL, under a live HNSW index, segfaults nondeterministically inside shrinkForNode() -> computeDistance(). shrinkForNode() read the node's embedding from the node table and could get a NULL (null EmbeddingHandle) value, then passed getPtr() straight to the distance function — reading through a garbage pointer in release builds. Depending on memory layout, this either segfaults or silently corrupts search distances. A crash also leaves the WAL unreplayable.

Changes

  • shrinkForNode(): return early when the scanned embedding is a null handle (the node has no embedding, e.g. its embedding was SET to NULL while still referenced in the graph) instead of asserting in debug / crashing in release.
  • Document that OnDiskHNSWIndex::update() runs after the node table row was updated (paired main-repo change), so embeddings scanned during re-insertion are the new value.
  • Regression tests in update.test:
    • SET on a row that was NULL at index build (+ nearest-neighbor check),
    • SET to NULL and back (+ nearest-neighbor check),
    • NULL-row SET surviving CHECKPOINT + reopen (on-disk only).

Testing

  • All 87 vector extension e2e tests pass.
  • Issue-scale repro (2000x768-d vectors + 1000 NULL rows, cosine/mu 30/ml 60/efc 200): 50 SETs on NULL rows + checkpoint + reopen, 5/5 runs clean, WAL replays without corruption.

Paired with the main-repo PR (ladybugdb/ladybug) which reorders NodeTable::update() to apply the table update before index maintenance.

- Guard shrinkForNode() against a null EmbeddingHandle when the node's
  embedding scanned from the node table is NULL (e.g. its embedding was
  SET to NULL while still referenced in the graph). Previously this
  dereferenced a null handle and segfaulted in the distance function.
- Document that OnDiskHNSWIndex::update() runs after the node table has
  been updated, so scanned embeddings are the new value.
- Add regression tests: SET on a row that was NULL at index build,
  SET to NULL and back, and NULL-row SET surviving checkpoint + reopen.

Fixes LadybugDB/ladybug#896 (together with the NodeTable::update ordering
fix in the main repo).
adsharma added a commit to LadybugDB/ladybug that referenced this pull request Sep 2, 2026
Bumps ladybugdb/extensions to the fix-896-hnsw-set-null branch
(pr LadybugDB/extensions#78): guards shrinkForNode() against a NULL
embedding scanned from the node table and adds regression tests.
adsharma added a commit to LadybugDB/ladybug that referenced this pull request Sep 2, 2026
Bumps ladybugdb/extensions to the fix-896-hnsw-set-null branch
(pr LadybugDB/extensions#78): guards shrinkForNode() against a NULL
embedding scanned from the node table and adds regression tests.
@adsharma
adsharma merged commit 0b4f40d into main Sep 2, 2026
2 checks passed
@adsharma
adsharma deleted the fix-896-hnsw-set-null branch September 2, 2026 17:45
adsharma added a commit to LadybugDB/ladybug that referenced this pull request Sep 2, 2026
Bumps ladybugdb/extensions to the merged fix (0b4f40d7f, PR
LadybugDB/extensions#78): guards shrinkForNode() against a NULL embedding
scanned from the node table and adds regression tests.
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