Copilot commented on code in PR #19046:
URL: https://github.com/apache/pinot/pull/19046#discussion_r3669215963


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/loader/invertedindex/VectorIndexHandler.java:
##########
@@ -545,6 +551,58 @@ && hasCombinedFile(indexDir, column, desiredBackend)) {
     }
   }
 
+  /**
+   * Checks whether the HNSW vector index config has changed by comparing the 
metadata file written at index-creation
+   * time with the current {@link VectorIndexConfig}.
+   *
+   * <p>The metadata file (written by {@code HnswVectorIndexCreator}) records 
the vector dimension and
+   * {@link VectorSimilarityFunction} used to build the index. Changing either 
field
+   * produces a structurally incompatible index (wrong vector size) or 
silently wrong query results (wrong distance
+   * metric), so a rebuild is required.
+   *
+   * <p>Legacy segments built before this metadata was introduced have no 
metadata file; config-change detection is
+   * skipped for those segments (the file will be created the next time the 
index is rebuilt for any reason).
+   */
+  private boolean isVectorConfigChanged(File indexDir, String column, 
VectorIndexConfig desiredConfig,
+      String segmentName) {
+    File segmentDirectory = SegmentDirectoryPaths.segmentDirectoryFor(indexDir,
+        _segmentDirectory.getSegmentMetadata().getVersion());
+    Properties metadata = 
VectorIndexUtils.readVectorIndexMetadata(segmentDirectory, column);
+    if (metadata == null) {
+      // No metadata file — legacy segment; skip config-change detection.
+      return false;
+    }
+    try {
+      int storedDimension = 
Integer.parseInt(metadata.getProperty("dimension"));
+      int desiredDimension = desiredConfig.getVectorDimension();
+      if (storedDimension != desiredDimension) {
+        LOGGER.info("Vector index config changed for segment: {}, column: {} "
+                + "(dimension: {} → {}). Index needs to be rebuilt.",
+            segmentName, column, storedDimension, desiredDimension);
+        return true;
+      }
+
+      VectorSimilarityFunction storedFunc =
+          
VectorSimilarityFunction.valueOf(metadata.getProperty("distanceFunction"));
+      VectorIndexConfig.VectorDistanceFunction desiredDistanceFunc = 
desiredConfig.getVectorDistanceFunction();
+      // Null distanceFunction defaults to COSINE (see 
VectorIndexConfig.DEFAULT_VECTOR_DISTANCE_FUNCTION).
+      if (desiredDistanceFunc == null) {
+        desiredDistanceFunc = VectorIndexConfig.VectorDistanceFunction.COSINE;
+      }
+      VectorSimilarityFunction desiredFunc = 
VectorIndexUtils.toSimilarityFunction(desiredDistanceFunc);
+      if (storedFunc != desiredFunc) {
+        LOGGER.info("Vector index config changed for segment: {}, column: {} "
+                + "(distanceFunction: {} → {}). Index needs to be rebuilt.",
+            segmentName, column, storedFunc, desiredFunc);
+        return true;
+      }
+    } catch (Exception e) {
+      LOGGER.warn("Failed to parse vector index metadata for segment: {}, 
column: {}; skipping config-change check",
+          segmentName, column, e);
+    }

Review Comment:
   If the metadata file exists but is unreadable/corrupt or missing expected 
keys, the current behavior is to log a warning and then *skip* the 
config-change check (returning false). That can leave an incompatible index in 
place and produce wrong results when dimension/distance changes. Safer behavior 
is to treat parse failure as requiring a rebuild (the rebuild will also rewrite 
a clean metadata file).



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/creator/impl/vector/HnswVectorIndexCreator.java:
##########
@@ -110,6 +110,8 @@ public void seal() {
     try {
       LOGGER.info("Sealing HNSW index for column: {}", _vectorColumn);
       _indexWriter.forceMerge(1);
+      VectorIndexUtils.writeVectorIndexMetadata(_segmentIndexDir, 
_vectorColumn, _vectorDimension,
+          _vectorSimilarityFunction);

Review Comment:
   The metadata file is written to the current (V1/V2) segment build directory, 
but offline segment generation is documented here as always V1 and then 
converted to V3. The V1/V2→V3 converter copies the Lucene HNSW directory but 
does not copy sibling files like `*.vector.hnsw.metadata`, and then deletes all 
files in the V1/V2 directory — so the metadata will be dropped for V3 segments 
and config-drift detection will be silently skipped.



##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/loader/invertedindex/VectorIndexHandler.java:
##########
@@ -496,7 +501,8 @@ public void updateIndices(SegmentDirectory.Writer 
segmentWriter)
       VectorBackendType desiredBackend = desiredConfig.resolveBackendType();
       VectorBackendType existingBackend = 
VectorIndexUtils.detectVectorIndexBackend(indexDir, column);
       // Storage-format comparison — see the twin check in needUpdateIndices.
-      if (existingBackend != null && existingBackend != 
VectorIndexUtils.storageFormatOf(desiredBackend)) {
+      if ((existingBackend != null && existingBackend != 
VectorIndexUtils.storageFormatOf(desiredBackend))
+          || isVectorConfigChanged(indexDir, column, desiredConfig, 
segmentName)) {
         LOGGER.info("Rebuilding Vector index for segment: {}, column: {} 
(backend changed from {} to {})",
             segmentName, column, existingBackend, desiredBackend);

Review Comment:
   This log line says the backend changed, but the rebuild can also be 
triggered by `isVectorConfigChanged(...)` (dimension/distance change) even when 
the backend stays the same. The current message can be misleading during 
ops/debugging (e.g., logs show "backend changed from HNSW to HNSW").



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to