Skip to content

Conversation

@visz11
Copy link

@visz11 visz11 commented Oct 16, 2025

User description

Add a non-quantized HNSW vectors format supporting bfloat16 and directIO


CodeAnt-AI Description

Add an HNSW vectors format that supports bfloat16 storage and direct I/O

What Changed

  • Introduces a new non-quantized HNSW vectors format that can be enabled for indexing and nearest-neighbor search; it can be configured to store vectors in bfloat16 and to use direct I/O.
  • Registers the new format so it is discoverable by the system's vector codec lookup.
  • When bfloat16 is enabled, stored and reported off-heap sizes use the smaller bfloat16 byte size per vector component, reducing per-vector storage.
  • Adds unit tests for the HNSW format and a bfloat16-specific test suite; tests cover limits, string representation, off-heap size reporting, and allow small numeric differences when using bfloat16 encoding.

Impact

✅ HNSW indexing with optional bfloat16 encoding
✅ Lower storage per vector when bfloat16 is enabled
✅ New tests improve confidence and tolerate bfloat16 numeric variance

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for ES93 HNSW vector format with enhanced configuration options for vector merging and encoding.
  • Tests

    • Added comprehensive test coverage for ES93 HNSW vector format functionality.

@codeant-ai
Copy link

codeant-ai bot commented Oct 16, 2025

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@gemini-code-assist
Copy link

Summary of Changes

Hello @visz11, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to Elasticsearch's vector search capabilities by adding ES93HnswVectorsFormat. This new non-quantized HNSW vector format provides native support for bfloat16 data types and direct I/O, aiming to offer improved efficiency and flexibility for storing and querying vector embeddings. The changes include the core implementation of the format, integration into the module system, and comprehensive testing to ensure its correctness and performance characteristics.

Highlights

  • New HNSW Vector Format: Introduces ES93HnswVectorsFormat for non-quantized HNSW vector storage, enhancing Elasticsearch's vector search capabilities.
  • BFloat16 Support: The new format and related components are designed to natively support bfloat16 data types, offering a balance between precision and efficiency.
  • Direct I/O Capability: The ES93HnswVectorsFormat includes support for direct I/O operations, which can lead to improved performance for vector storage and retrieval.
  • Module and Service Registration: The new format is properly integrated into the module system and registered as a Lucene KnnVectorsFormat service, making it discoverable and usable within the application.
  • Comprehensive Testing: Dedicated test classes (ES93HnswVectorsFormatTests and ES93HnswBFloat16VectorsFormatTests) have been added to validate the new format's functionality, including bfloat16 specific behavior, parameter limits, and off-heap memory usage.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link

coderabbitai bot commented Oct 16, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

ES 9.3 introduces ES93HnswVectorsFormat, a new HNSW vector format supporting BFloat16 encoding with configurable merge parameters. A bug fix updates BFloat16 vector writer to use correct off-heap storage. Service provider registration and comprehensive test coverage validate the implementation.

Changes

Cohort / File(s) Summary
New HNSW Vector Format Implementation
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java
New public class extending AbstractHnswVectorsFormat with three constructors (no-arg, with flat format params, with merge settings). Overrides flatVectorsFormat(), fieldsWriter(), and fieldsReader() to integrate ES93GenericFlatVectorsFormat with Lucene99HnswVectorsWriter/Reader.
Service Registration & Module Configuration
server/src/main/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat, server/src/main/java/module-info.java
Registers ES93HnswVectorsFormat in KnnVectorsFormat service provider list and module-info.java provides clause for ES 9.3 HNSW vectors support.
BFloat16 Vector Writer Fix
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93BFloat16FlatVectorsWriter.java
Updates mergeOneFieldToIndex to use OffHeapBFloat16VectorValues.DenseOffHeapVectorValues instead of OffHeapFloatVectorValues for correct BFloat16 off-heap storage.
HNSW Format Test Suite
server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormatTests.java
New public test class extending BaseKnnVectorsFormatTestCase. Validates ES93HnswVectorsFormat string representation, constructor argument limits (maxConn, beamWidth), and off-heap byte size calculations for vector fields with float and BFloat16 encoding.
BFloat16-Specific HNSW Tests
server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswBFloat16VectorsFormatTests.java
New test class extending ES93HnswVectorsFormatTests with BFloat16 enabled (useBFloat16=true) and FLOAT32 vector encoding. Skips byte-data scenarios and wraps inherited tests to expect AssertionError, then validates float values are within 1% tolerance using custom regex parser and comparison helper.

Sequence Diagram

sequenceDiagram
    participant Client
    participant ES93HnswVectorsFormat
    participant ES93GenericFlatVectorsFormat
    participant Lucene99HnswVectorsWriter
    participant Lucene99HnswVectorsReader

    Client->>ES93HnswVectorsFormat: new ES93HnswVectorsFormat()
    ES93HnswVectorsFormat->>ES93GenericFlatVectorsFormat: new (default)
    
    Note over ES93HnswVectorsFormat: Initialized with<br/>default NAME and flatVectorsFormat

    Client->>ES93HnswVectorsFormat: fieldsWriter(state)
    ES93HnswVectorsFormat->>Lucene99HnswVectorsWriter: new (state, maxConn,<br/>beamWidth, flatWriter, mergeSettings)
    Lucene99HnswVectorsWriter-->>Client: KnnVectorsWriter

    Client->>ES93HnswVectorsFormat: fieldsReader(state)
    ES93HnswVectorsFormat->>Lucene99HnswVectorsReader: new (state, flatReader)
    Lucene99HnswVectorsReader-->>Client: KnnVectorsReader
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The changes span multiple areas with mixed complexity: straightforward service registration and module configuration; a simple one-line bug fix in the BFloat16 writer; a moderate new class implementation with standard HNSW format integration; and comprehensive test classes with custom assertion logic requiring careful validation of float tolerance parsing and comparison semantics.

Poem

🐰 A new vector format hops into ES nine-three,
HNSW with BFloat16 precision running free!
Off-heap storage corrected, tests in place,
Searching hyperspaces at a faster pace! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Add ES93HnswVectorsFormat" is directly and fully related to the central change in the pull request. The primary objective of this changeset is to introduce a new ES93HnswVectorsFormat class that extends AbstractHnswVectorsFormat, and the PR includes supporting changes such as module-info.java updates, service provider configuration, related class modifications, and test coverage for this new format. The title is specific and concise, clearly indicating what is being added without using vague terminology, and a teammate scanning the commit history would readily understand that this PR introduces a new HNSW vectors format implementation for ES 9.3.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch clone-bbq_hnsw-bfloat16-format

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 16, 2025
@visz11
Copy link
Author

visz11 commented Oct 16, 2025

/refacto-visz

Copy link

@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 introduces a new non-quantized HNSW vectors format (ES93HnswVectorsFormat) that supports bfloat16 and directIO. The changes include modifications to module-info.java, ES93BFloat16FlatVectorsWriter.java, the creation of ES93HnswVectorsFormat.java, updates to KnnVectorsFormat, and the addition of ES93HnswBFloat16VectorsFormatTests.java and ES93HnswVectorsFormatTests.java. The review focuses on correctness and maintainability.

fieldInfo.getVectorSimilarityFunction(),
new OffHeapFloatVectorValues.DenseOffHeapVectorValues(
new OffHeapBFloat16VectorValues.DenseOffHeapVectorValues(
fieldInfo.getVectorDimension(),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The class OffHeapBFloat16VectorValues is used here, suggesting that the file OffHeapFloatVectorValues is no longer needed and can be removed. Please confirm if this is the case and remove the import if it's no longer used.

}

@Override
protected FlatVectorsFormat flatVectorsFormat() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider renaming this method to getFlatVectorsFormat to follow Java naming conventions for getter methods. This improves readability and consistency.

Suggested change
protected FlatVectorsFormat flatVectorsFormat() {
protected FlatVectorsFormat getFlatVectorsFormat() {
return flatVectorsFormat;
}


@Override
public void testRandom() throws Exception {
AssertionError err = expectThrows(AssertionError.class, super::testRandom);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The test testRandom is expected to throw an AssertionError. It would be helpful to add a comment explaining why this is expected and what kind of assertions are being made within the assertFloatsWithinBounds method. This will improve the readability and maintainability of the test.

Comment on lines +87 to +97
private static void assertFloatsWithinBounds(AssertionError error) {
Matcher m = FLOAT_ASSERTION_FAILURE.matcher(error.getMessage());
if (m.matches() == false) {
throw error; // nothing to do with us, just rethrow
}

// numbers just need to be in the same vicinity
double expected = Double.parseDouble(m.group(1));
double actual = Double.parseDouble(m.group(2));
double allowedError = expected * 0.01; // within 1%
assertThat(error.getMessage(), actual, closeTo(expected, allowedError));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The allowed error is calculated as 1% of the expected value. While this might be sufficient, consider making this configurable or using a more robust method to determine the allowed error, especially if the expected values can be very small. A relative error might not be appropriate for values close to zero.

@refacto-visz
Copy link

refacto-visz bot commented Oct 16, 2025

Add ES93HnswVectorsFormat

TL;DR: Introduces a new non-quantized HNSW vectors format with bfloat16 storage and direct I/O support for improved vector search performance.


Refacto PR Summary

Implements ES93HnswVectorsFormat extending AbstractHnswVectorsFormat with configurable bfloat16 encoding and direct I/O capabilities.
Adds a new HNSW (Hierarchical Navigable Small World) vector format that supports optional bfloat16 encoding for reduced memory usage and direct I/O for performance optimization. The implementation includes proper service registration, vector storage optimization using 16-bit floats instead of 32-bit when enabled, and comprehensive test coverage that accounts for precision differences in bfloat16 calculations. This enhancement provides more efficient vector storage and search capabilities while maintaining compatibility with existing Lucene vector infrastructure.

Change Highlights

Click to expand
  • ES93HnswVectorsFormat.java: New HNSW format with bfloat16/directIO configuration
  • ES93BFloat16FlatVectorsWriter.java: Fixed vector values class for bfloat16 support
  • module-info.java: Registered new format in module exports
  • META-INF/services/: Added service provider registration for format discovery
  • ES93HnswVectorsFormatTests.java: Base test suite with limits and configuration validation
  • ES93HnswBFloat16VectorsFormatTests.java: Specialized tests handling bfloat16 precision differences

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Format as ES93HnswVectorsFormat
    participant Writer as Lucene99HnswVectorsWriter
    participant FlatFormat as ES93GenericFlatVectorsFormat
    participant Storage as Vector Storage
    
    App->>Format: Initialize with bfloat16=true, directIO=true
    Format->>FlatFormat: Create with encoding options
    App->>Format: fieldsWriter(SegmentWriteState)
    Format->>Writer: Create with flatVectorsFormat writer
    Writer->>FlatFormat: Write vectors with bfloat16 encoding
    FlatFormat->>Storage: Store as 16-bit floats (reduced size)
    Storage-->>App: Confirm storage with optimized memory usage
Loading

Testing Guide

Click to expand
  1. Format initialization: Test ES93HnswVectorsFormat with various maxConn/beamWidth parameters, verify IllegalArgumentException for invalid values
  2. BFloat16 encoding: Create vectors with bfloat16=true, verify storage uses 16-bit encoding and off-heap size calculations are correct
  3. Precision tolerance: Run vector similarity searches with bfloat16, confirm results within 1% tolerance of float32 precision
  4. Service discovery: Verify format appears in KnnVectorsFormat service loader and is accessible via codec lookup
  5. Integration testing: Index vectors using new format, perform nearest neighbor searches, validate performance improvements with direct I/O

// numbers just need to be in the same vicinity
double expected = Double.parseDouble(m.group(1));
double actual = Double.parseDouble(m.group(2));
double allowedError = expected * 0.01; // within 1%
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Use a relative tolerance based on the absolute magnitude and a small minimum epsilon so comparisons don't fail when the expected value is zero or very small. [possible bug]

Suggested change
double allowedError = expected * 0.01; // within 1%
// use absolute magnitude for relative tolerance and enforce a small minimum epsilon to handle expected == 0
double allowedError = Math.max(Math.abs(expected) * 0.01, 1e-6); // within 1% or at least 1e-6
Why Change? ⭐

This change is syntactically valid Java and uses only java.lang.Math methods, which are always available. It preserves the intended relative-tolerance behavior for non-zero expected values (allowedError = 1% of |expected|) while avoiding an overly strict allowedError of 0 when expected == 0 by enforcing a small epsilon (1e-6). The assertion call and surrounding parsing remain unchanged. There are no new imports or external dependencies, and the new computation does not introduce new exceptions for normal numeric inputs, so this is safe to apply and thus verified.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93BFloat16FlatVectorsWriter.java (2)

229-233: Fix incorrect class name in exception message

Mismatch: references ES92BFloat16FlatVectorsWriter in ES93 class. Update message to avoid confusion.

-                case BYTE -> throw new UnsupportedOperationException("ES92BFloat16FlatVectorsWriter only supports float vectors");
+                case BYTE -> throw new UnsupportedOperationException("ES93BFloat16FlatVectorsWriter only supports float vectors");

369-375: ramBytesUsed undercounts; FLOAT32 stored as float[] (4B), not BF16 (2B)

This can under-report memory and affect IW accounting. Use Float.BYTES for FLOAT32.

-            int byteSize = fieldInfo.getVectorEncoding() == VectorEncoding.FLOAT32
-                ? BFloat16.BYTES
-                : fieldInfo.getVectorEncoding().byteSize;
+            int byteSize = fieldInfo.getVectorEncoding() == VectorEncoding.FLOAT32
+                ? Float.BYTES
+                : fieldInfo.getVectorEncoding().byteSize;
🧹 Nitpick comments (2)
server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormatTests.java (1)

109-113: Verify off-heap size keys and make casting robust

  • Is "vex" the intended key? If not, typo will hide accounting issues.
  • Prefer Number.longValue() to avoid brittle casts.

Example:

assertEquals((long) vector.length * bytes, ((Number) offHeap.get("vec")).longValue());
assertEquals(1L, ((Number) offHeap.get("vex")).longValue());
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java (1)

24-66: Consider adding class and constructor javadoc.

While the code is clear, adding javadoc comments would improve maintainability, especially for:

  • Class-level description explaining ES93 format features (bfloat16, directIO support)
  • Constructor parameter descriptions
  • Relationship to underlying Lucene99 HNSW implementation

Example:

+/**
+ * ES 9.3 HNSW vectors format supporting BFloat16 encoding and direct I/O.
+ * Wraps Lucene 9.9 HNSW implementation with ES-specific flat vectors format.
+ */
 public class ES93HnswVectorsFormat extends AbstractHnswVectorsFormat {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9371a61 and 37faca9.

📒 Files selected for processing (6)
  • server/src/main/java/module-info.java (1 hunks)
  • server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93BFloat16FlatVectorsWriter.java (1 hunks)
  • server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java (1 hunks)
  • server/src/main/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat (1 hunks)
  • server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswBFloat16VectorsFormatTests.java (1 hunks)
  • server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormatTests.java (1 hunks)
🔇 Additional comments (11)
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93BFloat16FlatVectorsWriter.java (1)

250-260: Correct off-heap BF16 usage in HNSW merge path

Switch to OffHeapBFloat16VectorValues is correct (dimension/cardinality/stride align). LGTM.

server/src/main/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat (1)

13-13: Registering ES93HnswVectorsFormat

Provider entry is correct and consistent with module-info. LGTM.

server/src/main/java/module-info.java (1)

468-469: Module provider wiring looks correct

ES93HnswVectorsFormat properly added to KnnVectorsFormat providers. LGTM.

server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java (8)

12-22: LGTM! Clean import structure.

The imports are minimal, well-organized, and appropriate for an HNSW vectors format implementation. The use of Lucene 9.9 components (Lucene99HnswVectorsReader/Writer) is consistent with Elasticsearch's codec strategy.


24-26: LGTM! Proper class structure.

The class correctly extends AbstractHnswVectorsFormat and defines an appropriate NAME constant following ES versioning conventions.


30-33: LGTM! Clean default constructor.

The no-arg constructor properly initializes with defaults, delegating configuration to ES93GenericFlatVectorsFormat.


40-50: LGTM! Complete constructor with merge configuration.

This constructor properly passes all parameters, including merge worker settings, to the parent class and initializes the flat vectors format with bfloat16 and directIO options.


52-55: LGTM! Standard getter override.

The method correctly returns the encapsulated flatVectorsFormat instance.


57-60: LGTM! Correct writer construction.

The method properly constructs a Lucene99HnswVectorsWriter with appropriate parameters and delegates flat vector writing to the underlying format.


62-65: LGTM! Correct reader construction.

The method properly constructs a Lucene99HnswVectorsReader with the segment state and delegates flat vector reading to the underlying format.


35-38: Verify merge parameter initialization in parent class.

This constructor calls super(NAME, maxConn, beamWidth) without explicitly passing merge parameters. However, line 59's fieldsWriter() method references numMergeWorkers and mergeExec. Ensure that AbstractHnswVectorsFormat's 3-parameter constructor properly initializes these fields to safe default values (e.g., null for mergeExec, default value for numMergeWorkers) that are acceptable to Lucene99HnswVectorsWriter.

Run the following script to verify the parent class constructor:

Comment on lines +85 to +98
private static final Pattern FLOAT_ASSERTION_FAILURE = Pattern.compile(".*expected:<([0-9.-]+)> but was:<([0-9.-]+)>");

private static void assertFloatsWithinBounds(AssertionError error) {
Matcher m = FLOAT_ASSERTION_FAILURE.matcher(error.getMessage());
if (m.matches() == false) {
throw error; // nothing to do with us, just rethrow
}

// numbers just need to be in the same vicinity
double expected = Double.parseDouble(m.group(1));
double actual = Double.parseDouble(m.group(2));
double allowedError = expected * 0.01; // within 1%
assertThat(error.getMessage(), actual, closeTo(expected, allowedError));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix tolerance calc and regex in BF16 float assertion helper

  • allowedError must be non-negative; zero expected needs absolute fallback.
  • Regex should handle scientific notation.

Apply:

-    private static final Pattern FLOAT_ASSERTION_FAILURE = Pattern.compile(".*expected:<([0-9.-]+)> but was:<([0-9.-]+)>");
+    private static final Pattern FLOAT_ASSERTION_FAILURE = Pattern.compile(
+        ".*expected:<([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)> but was:<([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)>.*"
+    );
@@
-        double allowedError = expected * 0.01;  // within 1%
-        assertThat(error.getMessage(), actual, closeTo(expected, allowedError));
+        double allowedError = Math.max(1e-6, Math.abs(expected) * 0.01);  // within 1% or small abs floor
+        assertThat(error.getMessage(), actual, closeTo(expected, allowedError));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static final Pattern FLOAT_ASSERTION_FAILURE = Pattern.compile(".*expected:<([0-9.-]+)> but was:<([0-9.-]+)>");
private static void assertFloatsWithinBounds(AssertionError error) {
Matcher m = FLOAT_ASSERTION_FAILURE.matcher(error.getMessage());
if (m.matches() == false) {
throw error; // nothing to do with us, just rethrow
}
// numbers just need to be in the same vicinity
double expected = Double.parseDouble(m.group(1));
double actual = Double.parseDouble(m.group(2));
double allowedError = expected * 0.01; // within 1%
assertThat(error.getMessage(), actual, closeTo(expected, allowedError));
}
private static final Pattern FLOAT_ASSERTION_FAILURE = Pattern.compile(
".*expected:<([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)> but was:<([-+]?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?)>.*"
);
private static void assertFloatsWithinBounds(AssertionError error) {
Matcher m = FLOAT_ASSERTION_FAILURE.matcher(error.getMessage());
if (m.matches() == false) {
throw error; // nothing to do with us, just rethrow
}
// numbers just need to be in the same vicinity
double expected = Double.parseDouble(m.group(1));
double actual = Double.parseDouble(m.group(2));
double allowedError = Math.max(1e-6, Math.abs(expected) * 0.01); // within 1% or small abs floor
assertThat(error.getMessage(), actual, closeTo(expected, allowedError));
}
🤖 Prompt for AI Agents
In
server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswBFloat16VectorsFormatTests.java
around lines 85-98, the float-assertion helper uses a regex that doesn't accept
scientific notation and computes allowedError directly from expected which can
be negative or zero; update the regex to accept decimal and scientific notation
for both groups (e.g. allow digits, optional sign, decimal point and optional
exponent with [eE][+-]?\\d+), and compute allowedError as a non-negative value
using Math.abs(expected) * 0.01 with a small absolute fallback when expected is
zero (e.g. Math.max(Math.abs(expected) * 0.01, 1e-6)); keep parsing with
Double.parseDouble and assert with closeTo using the corrected allowedError.

@codeant-ai
Copy link

codeant-ai bot commented Oct 16, 2025

Pull Request Feedback 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Resource management
    The call to create the nested flat vectors writer is inlined inside the HNSW writer constructor. If the HNSW writer constructor throws, the nested writer may never be closed, causing a resource leak. Ensure ownership/close semantics and exception-safety when composing writers.

  • Stride integer overflow risk
    The call constructs the off-heap dense accessor with fieldInfo.getVectorDimension() * BFloat16.BYTES inline. Multiplication can overflow an int for extremely large dimensions, resulting in negative or incorrect stride. Validate and compute the stride safely (use long and bounds checks) before passing it to the constructor.

  • RAM accounting mismatch
    The FieldWriter.ramBytesUsed() routine uses BFloat16.BYTES when the field encoding is FLOAT32. However the in-memory representation of vectors stored in vectors is float[] (4 bytes per component), so the reported heap RAM usage will be underestimated when bfloat16 is the on-disk encoding. Verify and correct the estimator so heap usage reflects the actual in-memory layout.

  • Direct I/O / bfloat16 compatibility
    The new ES93GenericFlatVectorsFormat may enable direct I/O and bfloat16 encoding. Verify that the Lucene99HnswVectorsWriter/Reader and the flat format agree on alignment, buffer lifetimes, and byte-size expectations (e.g., off-heap accounting). Mismatches could lead to corruption or incorrect size reporting.

  • Module Visibility / Exports
    The es93 vectors package is currently exported only "to org.elasticsearch.test.knn". Ensure consumers that need to instantiate/discover the provider at runtime have adequate module visibility. If other runtime modules (beyond tests) need access, you may need to export the package to those modules or export it unqualified.

  • Numeric parsing fragility
    The regex used to extract expected/actual numeric values from the AssertionError message only allows digits, dot and minus. It will fail for scientific notation (e.g. "1.23E-4"), plus signs, or other localized formats, causing the code to rethrow unrelated assertion errors instead of validating tolerance.

org.elasticsearch.index.codec.vectors.diskbbq.ES920DiskBBQVectorsFormat
org.elasticsearch.index.codec.vectors.diskbbq.next.ESNextDiskBBQVectorsFormat
org.elasticsearch.index.codec.vectors.es93.ES93BinaryQuantizedVectorsFormat
org.elasticsearch.index.codec.vectors.es93.ES93HnswVectorsFormat
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Verify that the fully-qualified name org.elasticsearch.index.codec.vectors.es93.ES93HnswVectorsFormat exactly matches the implementation class (package + class name), that the class is declared public and exposes the expected SPI-compatible constructor/signature, and confirm the class is included in the built JAR so ServiceLoader can load it at runtime. [possible issue]

@codeant-ai
Copy link

codeant-ai bot commented Oct 16, 2025

CodeAnt AI finished reviewing your PR.

@refacto-visz
Copy link

refacto-visz bot commented Oct 16, 2025

Refacto is reviewing this PR. Please wait for the review comments to be posted.

@refacto-visz
Copy link

refacto-visz bot commented Oct 16, 2025

Code Review: Vector Format Implementation

👍 Well Done
Comprehensive Test Coverage

Thorough testing including error tolerance for bfloat16 precision and proper inheritance patterns

Efficient Vector Storage

BFloat16 encoding reduces memory footprint by 50% per vector component

Clean Constructor Design

Multiple constructor overloads provide flexible configuration options while maintaining clarity

📁 Selected files for review (6)
  • server/src/main/java/module-info.java
  • server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93BFloat16FlatVectorsWriter.java
  • server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java
  • server/src/main/resources/META-INF/services/org.apache.lucene.codecs.KnnVectorsFormat
  • server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswBFloat16VectorsFormatTests.java
  • server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormatTests.java
🎯 Custom Instructions
✅ Applied Instructions
Organization Guidelines
  • Keep pull requests small and focused (prefer < 400 lines of code).
  • All CI/CD checks, linting, and unit tests must pass before merge.
  • Use feature flags for new functionality and include a clear rollback plan.
  • Follow the company security checklist:
    • No hard-coded secrets or credentials.
    • Validate all external inputs.
    • Use parameterized queries for DB access.

Scope: All files

📝 Additional Comments
server/src/main/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswVectorsFormat.java (2)
Constructor Parameter Validation

Constructors accept configuration parameters without local validation, relying entirely on superclass validation. Adding explicit parameter validation would improve error messages and fail-fast behavior, making configuration issues easier to diagnose during development.

Standards:

  • Clean-Code-Functions
  • SOLID-SRP
  • Maintainability-Quality-Robustness
Memory Efficiency Optimization

Constructor creates new ES93GenericFlatVectorsFormat instances without considering object reuse patterns. For high-frequency vector operations, constructor overhead could impact performance. Consider implementing format instance caching or factory pattern for frequently used configurations.

Standards:

  • ISO-IEC-25010-Performance-Efficiency-Resource-Utilization
  • Memory-Allocation-Optimization
server/src/test/java/org/elasticsearch/index/codec/vectors/es93/ES93HnswBFloat16VectorsFormatTests.java (3)
Test Assertion Enhancement

Float assertion handling uses regex parsing of error messages and hardcoded 1% tolerance. This approach is brittle to assertion message format changes and may not be appropriate for all floating-point contexts. Consider using dedicated floating-point assertion libraries with configurable precision.

Standards:

  • Clean-Code-Functions
  • Maintainability-Quality-Testability
  • Design-Pattern-Strategy
Test Precision Tolerance

Fixed 1% tolerance for bfloat16 precision validation may be insufficient for edge cases with very small or very large floating-point values. Mathematical accuracy could benefit from adaptive tolerance based on magnitude or using relative epsilon calculations. Current logic assumes uniform precision requirements across all value ranges.

Standards:

  • Mathematical-Accuracy-Floating-Point
  • Algorithm-Correctness-Numerical-Precision
Test Method Duplication

Multiple test methods contain identical empty implementations with 'no bytes' comments. This pattern creates maintenance overhead when test signatures change and provides no actual test coverage. Consider using test assumptions or conditional execution instead of empty method overrides.

Standards:

  • Clean-Code-Comments
  • Maintainability-Quality-Testability
  • SOLID-SRP

new OffHeapBFloat16VectorValues.DenseOffHeapVectorValues(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Import Statement

Usage of OffHeapBFloat16VectorValues class without visible import statement. Runtime ClassNotFoundException will occur if import missing causing vector indexing failures. Service degradation results from failed vector operations.

import org.elasticsearch.index.codec.vectors.es93.OffHeapBFloat16VectorValues;
Commitable Suggestion
Suggested change
new OffHeapBFloat16VectorValues.DenseOffHeapVectorValues(
import org.elasticsearch.index.codec.vectors.es93.OffHeapBFloat16VectorValues;
Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • ISO-IEC-25010-Functional-Correctness-Appropriateness

Comment on lines +40 to +50
public ES93HnswVectorsFormat(
int maxConn,
int beamWidth,
boolean bfloat16,
boolean useDirectIO,
int numMergeWorkers,
ExecutorService mergeExec
) {
super(NAME, maxConn, beamWidth, numMergeWorkers, mergeExec);
flatVectorsFormat = new ES93GenericFlatVectorsFormat(bfloat16, useDirectIO);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executor Validation Gap

Constructor accepts ExecutorService parameter without null validation before passing to superclass. NullPointerException during vector merging operations if null executor provided. Vector indexing failures result from unhandled null executor state.

Standards
  • ISO-IEC-25010-Reliability-Fault-Tolerance
  • DbC-Preconditions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants