xiangfu0 commented on code in PR #19307:
URL: https://github.com/apache/pinot/pull/19307#discussion_r4109727304


##########
pinot-segment-local/src/main/java/org/apache/pinot/segment/local/segment/index/loader/invertedindex/TextIndexHandler.java:
##########
@@ -373,7 +373,7 @@ private void 
convertTextIndexToV3Format(SegmentDirectory.Writer segmentWriter, S
       return;
     }
 
-    // Write the combined file to V3 format (similar to 
rewriteForwardIndexForCompressionChange)
+    // Write the combined file to V3 format (similar to 
ForwardIndexHandler.rewriteForwardIndex)

Review Comment:
   Dropped — `TextIndexHandler.java` is now byte-identical to master, so it is 
out of this PR entirely (the file no longer appears in the diff for #19307, 
#19308 or #19309).
   
   One consequence worth naming: that comment referenced 
`rewriteForwardIndexForCompressionChange`, which this PR renames to 
`ForwardIndexHandler.rewriteForwardIndex`, so with the edit dropped the comment 
now names a method that no longer exists on the stack. It is a dangling doc 
reference only — no code reference and no compile impact (a grep for the old 
name across the stack returns just that one comment). I did not rename the 
method back. Happy to fix the wording in a one-line PR straight onto master, 
which keeps it out of this one.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/custom/RawForwardIndexWithDictionaryTest.java:
##########
@@ -178,7 +188,34 @@ protected List<FieldConfig> getFieldConfigs() {
     FieldConfig rawDictRangeInt =
         new FieldConfig(RAW_DICT_RANGE_INT_DIMENSION, 
FieldConfig.EncodingType.RAW, null, null, null, null,
             dictionaryIndex.deepCopy(), null, null);
-    return List.of(rawDictString, rawDictInvString, rawDictInt, rawDictInvInt, 
rawDictRangeInt);
+    return List.of(rawDictString, rawDictInvString, rawDictInt, rawDictInvInt, 
rawDictRangeInt,
+        codecFieldConfig(CODEC_INT_DIMENSION, "DELTA,T64,LZ4"),
+        codecFieldConfig(CODEC_LONG_DIMENSION, "ZSTD(3)"));
+  }
+
+  private static FieldConfig codecFieldConfig(String column, String codecSpec) 
{
+    ObjectNode forward = JsonUtils.newObjectNode().put("codecSpec", 
codecSpec).put("targetDocsPerChunk", 128);
+    ObjectNode indexes = JsonUtils.newObjectNode();
+    indexes.set("forward", forward);
+    return new 
FieldConfig.Builder(column).withEncodingType(FieldConfig.EncodingType.RAW).withIndexes(indexes).build();
+  }
+
+  /// Exercises persisted INT/LONG pipelines through ingestion and both query 
engines, across chunk boundaries.

Review Comment:
   Done — `RawForwardIndexWithDictionaryTest` is restored to master 
byte-for-byte, so the dictionary-focused fixture is untouched. That removes the 
two codec columns, their schema/Avro/record wiring, the `codecFieldConfig` 
helper and `testCodecPipelineQueries`.
   
   I checked #19309 covers what was here rather than assuming it: its 
`CODEC_SPECS` map drives a dedicated INT and LONG column per spec through both 
engines, with `NUM_DOCS=1000` against `targetDocsPerChunk=256` and point-lookup 
ids straddling the 512 boundary, a dict-encoded STRING column asserted to stay 
DICTIONARY, and `testGeneratedSegmentsUseConfiguredForwardIndexFormats` 
asserting the persisted spec round-trips per column. The one spec exercised 
here that its map lacked was the three-stage `DELTA,T64,LZ4` (it had 
`DELTA,LZ4`, `T64` and `T64,LZ4`), so I added that single entry there — it 
flows through the schema/Avro/field-config generation and the per-codec 
SUM/filter/point-lookup providers automatically, so the coverage genuinely 
moves rather than disappearing. The V7 format itself stays covered by the 
focused round-trip tests in this PR.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



##########
pinot-segment-local/src/test/java/org/apache/pinot/segment/local/segment/index/forward/ForwardIndexCreatorFactoryTest.java:
##########
@@ -105,21 +121,448 @@ public void 
testTableConfigConstructorPropagatesCompressionStatsFlag()
     }
   }
 
+  @DataProvider(name = "v7CodecSpecs")
+  public Object[][] v7CodecSpecs() {
+    return new Object[][]{
+        {"LZ4", DataType.INT}, {"DELTA,LZ4", DataType.INT},
+        {"ZSTD(3)", DataType.LONG}, {"DELTADELTA,GORILLA,ZSTD(3)", 
DataType.LONG}
+    };
+  }
+
+  /// Both compression-only and transform pipelines use V7, including a 
partial final chunk.
+  @Test(dataProvider = "v7CodecSpecs")
+  public void testCodecSpecRoundTripUsesV7Format(String codecSpec, DataType 
storedType)
+      throws Exception {
+    File indexDir = 
Files.createTempDirectory("ForwardIndexCreatorFactoryTest").toFile();
+    try {
+      ForwardIndexConfig config = new 
ForwardIndexConfig.Builder(FieldConfig.EncodingType.RAW)
+          .withCodecSpec(codecSpec)
+          .withTargetDocsPerChunk(2)
+          .build();
+      TableConfig tableConfig = new 
TableConfigBuilder(TableType.OFFLINE).setTableName("testTable").build();
+      // Legacy compression stats are opted in here on purpose: a codecSpec 
column never reports them.
+      tableConfig.getIndexingConfig().setCompressionStatsEnabled(true);
+      long[] values = storedType == DataType.INT
+          ? new long[]{11, 13, 21}
+          : new long[]{Long.MIN_VALUE, (long) Integer.MAX_VALUE + 1, 
Long.MAX_VALUE};
+      try (ForwardIndexCreator creator = 
ForwardIndexCreatorFactory.createIndexCreator(
+          newContext(indexDir, false, tableConfig, values.length, storedType), 
config)) {
+        assertFalse(creator.isDictionaryEncoded());
+        assertNull(creator.getRawForwardIndexChunkCompressionType());
+        for (long value : values) {
+          if (storedType == DataType.INT) {
+            creator.putInt((int) value);
+          } else {
+            creator.putLong(value);
+          }
+        }
+        creator.seal();
+        assertEquals(creator.getRawForwardIndexUncompressedValueSizeInBytes(), 
-1L);
+      }
+      File indexFile = new File(indexDir, COLUMN_NAME + 
V1Constants.Indexes.RAW_SV_FORWARD_INDEX_FILE_EXTENSION);
+      assertTrue(indexFile.exists());
+      try (PinotDataBuffer buffer = 
PinotDataBuffer.mapReadOnlyBigEndianFile(indexFile);
+          ForwardIndexReader<?> reader =
+              
ForwardIndexReaderFactory.getInstance().createRawIndexReader(buffer, 
storedType, true)) {
+        assertTrue(reader instanceof FixedByteChunkSVForwardIndexReaderV7,
+            "codecSpec was routed to " + reader.getClass().getSimpleName());
+        assertEquals(buffer.getInt(0), 
FixedByteChunkSVForwardIndexReaderV7.VERSION);
+        
assertEquals(FixedByteChunkSVForwardIndexReaderV7.readCodecSpec(buffer), 
codecSpec);
+        FixedByteChunkSVForwardIndexReaderV7 v7Reader = 
(FixedByteChunkSVForwardIndexReaderV7) reader;
+        try (FixedByteChunkSVForwardIndexReaderV7.Context context = 
v7Reader.createContext()) {
+          for (int i = 0; i < values.length; i++) {
+            assertEquals(storedType == DataType.INT ? v7Reader.getInt(i, 
context) : v7Reader.getLong(i, context),
+                values[i]);
+          }
+        }
+      }
+    } finally {
+      FileUtils.deleteQuietly(indexDir);
+    }
+  }
+
+  /// One multi-stage pipeline per stored type, plus a single-stage spec so 
the executor's
+  /// multi-stage-only scratch slots and the single-stage path both run at 
chunk-boundary scale.
+  /// Every spec in `v7CodecSpecs` is already round-tripped, including a 
partial final chunk, by
+  /// [#testCodecSpecRoundTripUsesV7Format] above.
+  @DataProvider(name = "v7MultiChunkCodecSpecs")
+  public Object[][] v7MultiChunkCodecSpecs() {
+    return new Object[][]{
+        {"LZ4", DataType.INT}, {"DELTA,LZ4", DataType.INT}, 
{"DELTADELTA,GORILLA,ZSTD(3)", DataType.LONG}
+    };
+  }
+
+  /// Realistic-volume round trip: many chunks, a non-power-of-two target 
normalized to 1024 docs per

Review Comment:
   Consolidated. The shared setup/write/read now lives in one private helper — 
config construction, the table config, the creator write loop with its INT/LONG 
branch, seal, the index-file mapping, `createRawIndexReader`, the V7 routing 
assertion and the sequential read-back. Both tests call it, and the file is 11 
lines shorter (568 to 557).
   
   Both distinct behaviours you named are kept: the power-of-two normalization 
assertions stay in the multi-chunk test verbatim (`targetDocsPerChunk=1000` 
normalizing to 1024, plus numChunks and totalDocs from the header), and the 
random-access path keeps its two separate Contexts with the 2000 random reads. 
The round-trip test keeps its own assertions too (version int, `readCodecSpec` 
round-trip, null chunk-compression type, the uncompressed-size assertion under 
`compressionStatsEnabled`, and the extreme LONG values).
   
   Two side effects worth knowing. Routing both tests through the helper means 
the creator-contract assertions now also apply to the 10009-doc multi-chunk 
case, which previously asserted none of them and hard-cast the reader. And 
because #19308 changes the uncompressed-size expectation for V7, the helper 
asserts both branches (enabled gives `totalDocs * size()`, disabled gives -1), 
so that PR gains the disabled-branch assertion it did not have. Test-method and 
invocation counts are unchanged at 11/26, and the other tests in the file are 
untouched.
   
   _🤖 Addressed by [Claude Code](https://claude.com/claude-code)_



-- 
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