Skip to content

Apply sequential read advice during vector merges - #153423

Merged
jimczi merged 8 commits into
mainfrom
jimczi/diskbbq-merge-sequential-advice
Aug 8, 2026
Merged

Apply sequential read advice during vector merges#153423
jimczi merged 8 commits into
mainfrom
jimczi/diskbbq-merge-sequential-advice

Conversation

@jimczi

@jimczi jimczi commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Lucene brackets a merge with getMergeInstance() and finishMerge() on the reader it is
handed: the first lets a reader prepare for merging (the flat vector readers use it to switch
their input from the DataAccessHint.RANDOM advice used for search to SEQUENTIAL), the
second restores the search access pattern afterwards. Both calls have to travel the whole
reader chain to reach the reader that owns the raw vector input. Three links in that chain
did not pass them on.

IVFVectorsReader (bbq_disk) overrode neither, so the flat vector reader kept RANDOM
advice for the entire merge, disabling read-ahead on the sequential scans a merge performs
(checkIntegrity() and the vector copy). getMergeInstance() now returns a copy of the
reader — via a copy constructor — backed by the flat readers' merge instances, and
finishMerge() delegates to them. Only the flat vectors are affected: the centroid and
posting list inputs (ivfCentroids / ivfClusters) are opened with the default context and
already use read-ahead advice, so they are left unchanged.

MergeReaderWrapper returned its merge reader without calling getMergeInstance() on it,
and did not implement finishMerge() at all, so both calls stopped at the wrapper. This is
the reader that sits in front of the raw vectors whenever direct I/O is enabled for them
(on_disk_rescore: true), which means the fix above had no effect at all in that
configuration — the call reached the wrapper and went no further. Both are now delegated.

ES818BinaryQuantizedVectorsReader (bbq_hnsw) had the opposite half of the problem: it
built a merge instance from the raw reader but never told that reader the merge had finished.
Since the flat reader mutates shared state and relies on finishMerge() to revert it, the raw
vector input stayed on sequential advice after the first merge — the wrong access pattern for
the rescoring path that reads it next. finishMerge() now delegates.

MergeReaderWrapperTests covers the wrapper's half of the contract with recording readers:
that both calls reach the merge reader, and that the search reader is never asked for a merge
instance nor told to finish one. The test fails on both counts without the production change.

Known gap left in place: Lucene104ScalarQuantizedVectorsReader (int8_hnsw / int4_hnsw)
overrides neither method, so nothing below it is reached for those field types. It keeps its
rawVectorsReader private with no copy constructor, so the equivalent fix cannot be made from
the Elasticsearch subclass and belongs upstream.

@elasticsearchmachine elasticsearchmachine added needs:triage Requires assignment of a team area label v9.6.0 labels Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

🔍 Preview links for changed docs

⏳ Building and deploying preview... View progress

This comment will be updated with preview links when the build is complete.

@jimczi
jimczi force-pushed the jimczi/diskbbq-merge-sequential-advice branch from 3422179 to e71e32b Compare July 9, 2026 14:29
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

ℹ️ Important: Docs version tagging

👋 Thanks for updating the docs! Just a friendly reminder that our docs are now cumulative. This means all 9.x versions are documented on the same page and published off of the main branch, instead of creating separate pages for each minor version.

We use applies_to tags to mark version-specific features and changes.

Expand for a quick overview

When to use applies_to tags:

✅ At the page level to indicate which products/deployments the content applies to (mandatory)
✅ When features change state (e.g. preview, ga) in a specific version
✅ When availability differs across deployments and environments

What NOT to do:

❌ Don't remove or replace information that applies to an older version
❌ Don't add new information that applies to a specific version without an applies_to tag
❌ Don't forget that applies_to tags can be used at the page, section, and inline level

🤔 Need help?

@jimczi jimczi added the :Search Relevance/Vectors Vector search label Jul 9, 2026
@elasticsearchmachine elasticsearchmachine added the Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch label Jul 9, 2026
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Pinging @elastic/es-search-relevance (Team:Search Relevance)

@elasticsearchmachine elasticsearchmachine removed the needs:triage Requires assignment of a team area label label Jul 9, 2026
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Hi @jimczi, I've updated the changelog YAML for you.

IVFVectorsReader did not override getMergeInstance() or finishMerge(), which Lucene
calls during a merge to switch the read advice and restore it afterwards. Because of
that, the flat vector reader kept the RANDOM advice used for search throughout the
merge, disabling read-ahead on the sequential scans (checkIntegrity and the vector
copy) that a merge performs.

Override both methods so the merge instance is backed by the flat readers' merge
instances, which switch their input to SEQUENTIAL, and finishMerge() restores the
search access pattern. The centroid and posting list inputs are opened with the
default context and already use read-ahead advice, so they are left unchanged.

Signed-off-by: Jim Ferenczi <jim.ferenczi@elastic.co>
@jimczi
jimczi force-pushed the jimczi/diskbbq-merge-sequential-advice branch from 12f3f34 to 5f164dc Compare July 9, 2026 14:49
@elasticsearchmachine

Copy link
Copy Markdown
Collaborator

Hi @jimczi, I've updated the changelog YAML for you.

@jimczi jimczi added the v9.5.1 label Jul 9, 2026

@benwtrent benwtrent 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.

I think this is good.

@thecoop

thecoop commented Aug 6, 2026

Copy link
Copy Markdown
Member

It's worth re-evaluating this in light of #155919, which removes the page cache from merges entirely

jimczi added 2 commits August 6, 2026 16:39
MergeReaderWrapper returned its merge reader without calling
getMergeInstance() on it, and never implemented finishMerge(). Both calls
therefore stopped at the wrapper, so a reader that is only reachable
behind it never got the chance to prepare for, or recover from, a merge.
This is the path taken whenever direct I/O is enabled for the raw
vectors.

ES818BinaryQuantizedVectorsReader had the other half of the problem: it
built a merge instance from the raw reader but never told that reader the
merge had finished, leaving whatever getMergeInstance() changed in place
for subsequent searches.

Delegate both calls in both readers.
@jimczi jimczi changed the title Apply sequential read advice during merge for bbq_disk vectors Apply sequential read advice during vector merges Aug 7, 2026
@jimczi
jimczi merged commit 3d2c7c8 into main Aug 8, 2026
37 checks passed
@jimczi
jimczi deleted the jimczi/diskbbq-merge-sequential-advice branch August 8, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

>bug :Search Relevance/Vectors Vector search Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch v9.6.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants