hudi-agent commented on code in PR #19347:
URL: https://github.com/apache/hudi/pull/19347#discussion_r3633040218


##########
hudi-common/src/test/java/org/apache/hudi/common/table/read/lsm/TestLsmFileGroupRecordIterator.java:
##########
@@ -119,6 +151,95 @@ void 
testSelectDirectLogReadersPrioritizesDeletesThenSmallFiles() {
     assertEquals(new HashSet<>(Arrays.asList(2, 3)), directReadersWithoutBase);
   }
 
+  @Test
+  void testSortedRunFixturesMergeAcrossBaseAndSpilledLogs() throws IOException 
{
+    HoodieTableConfig tableConfig = new HoodieTableConfig();
+    tableConfig.setValue(HoodieTableConfig.RECORDKEY_FIELDS, "id");
+    tableConfig.setValue(HoodieTableConfig.ORDERING_FIELDS, "ts");
+    tableConfig.setValue(HoodieTableConfig.RECORD_MERGE_MODE, 
RecordMergeMode.EVENT_TIME_ORDERING.name());
+    tableConfig.setValue(HoodieTableConfig.BASE_FILE_FORMAT, 
HoodieFileFormat.PARQUET.name());
+    tableConfig.setValue(HoodieTableConfig.POPULATE_META_FIELDS, "false");
+    StorageConfiguration<?> storageConfiguration = 
mock(StorageConfiguration.class);
+    HoodieAvroReaderContext context = org.mockito.Mockito.spy(

Review Comment:
   🤖 nit: `org.mockito.Mockito.spy(...)` stands out here — `doAnswer`, `mock`, 
and `when` are all statically imported in this file, so adding `import static 
org.mockito.Mockito.spy;` and using `spy(...)` directly would be more 
consistent.
   
   <sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag 
quality.</i></sub>



##########
hudi-common/src/test/java/org/apache/hudi/common/table/read/lsm/TestLsmFileGroupRecordIterator.java:
##########
@@ -119,6 +151,95 @@ void 
testSelectDirectLogReadersPrioritizesDeletesThenSmallFiles() {
     assertEquals(new HashSet<>(Arrays.asList(2, 3)), directReadersWithoutBase);
   }
 
+  @Test
+  void testSortedRunFixturesMergeAcrossBaseAndSpilledLogs() throws IOException 
{
+    HoodieTableConfig tableConfig = new HoodieTableConfig();
+    tableConfig.setValue(HoodieTableConfig.RECORDKEY_FIELDS, "id");
+    tableConfig.setValue(HoodieTableConfig.ORDERING_FIELDS, "ts");
+    tableConfig.setValue(HoodieTableConfig.RECORD_MERGE_MODE, 
RecordMergeMode.EVENT_TIME_ORDERING.name());
+    tableConfig.setValue(HoodieTableConfig.BASE_FILE_FORMAT, 
HoodieFileFormat.PARQUET.name());
+    tableConfig.setValue(HoodieTableConfig.POPULATE_META_FIELDS, "false");
+    StorageConfiguration<?> storageConfiguration = 
mock(StorageConfiguration.class);
+    HoodieAvroReaderContext context = org.mockito.Mockito.spy(
+        new HoodieAvroReaderContext(storageConfiguration, tableConfig, 
Option.empty(), Option.empty()));
+    TypedProperties props = new TypedProperties();
+    props.setProperty(HoodieReaderConfig.LSM_SORT_MERGE_SPILL_THRESHOLD.key(), 
"1");
+    props.setProperty(HoodieMemoryConfig.SPILLABLE_MAP_BASE_PATH.key(), 
tempDir.toString());
+    context.initRecordMerger(props);
+
+    FileGroupReaderSchemaHandler<IndexedRecord> schemaHandler = 
mock(FileGroupReaderSchemaHandler.class);
+    when(schemaHandler.getRequiredSchema()).thenReturn(tableSchema());
+    when(schemaHandler.getRequestedSchema()).thenReturn(tableSchema());
+    when(schemaHandler.getTableSchema()).thenReturn(tableSchema());
+    when(schemaHandler.getDeleteContext()).thenReturn(new DeleteContext(props, 
tableSchema()));
+    
when(schemaHandler.getRequiredSchemaForFileAndRenamedColumns(any(StoragePath.class)))
+        .thenReturn(Pair.of(tableSchema(), Collections.emptyMap()));
+    context.setSchemaHandler(schemaHandler);
+    context.setTablePath("/tmp/lsm-fixture");
+
+    doAnswer(invocation -> {
+      StoragePathInfo pathInfo = invocation.getArgument(0);
+      String name = pathInfo.getPath().getName();
+      if (name.endsWith("deletes.parquet")) {
+        HoodieSchema deleteSchema = invocation.getArgument(3);
+        return 
ClosableIterator.wrap(Collections.singletonList(deleteRecord(deleteSchema, "b", 
2L)).iterator());
+      }
+      List<IndexedRecord> records;
+      if (name.endsWith(".parquet") && !name.contains(".log.")) {
+        records = Arrays.asList(indexedRecord("a", "base-a", 1), 
indexedRecord("c", "base-c", 5));
+      } else if (name.contains("_002_")) {
+        records = Arrays.asList(indexedRecord("a", "log2-a", 3), 
indexedRecord("c", "stale-c", 2));
+      } else {
+        records = Arrays.asList(indexedRecord("a", "log1-a", 2), 
indexedRecord("b", "log1-b", 1));
+      }
+      return ClosableIterator.wrap(records.iterator());
+    }).when(context).getFileRecordIterator(any(StoragePathInfo.class), 
anyLong(), anyLong(),
+        any(HoodieSchema.class), any(HoodieSchema.class), 
any(HoodieStorage.class));
+
+    HoodieBaseFile baseFile = new 
HoodieBaseFile(pathInfo("/tmp/file1_1-0-1_001.parquet", 100));
+    List<HoodieLogFile> logs = Arrays.asList(
+        new HoodieLogFile(pathInfo("/tmp/file1_1-0-1_001_1.log.parquet", 50)),
+        new HoodieLogFile(pathInfo("/tmp/file1_1-0-1_002_1.log.parquet", 60)),
+        new HoodieLogFile(pathInfo("/tmp/file1_1-0-1_003_1.deletes.parquet", 
10)));
+    InputSplit split = InputSplit.builder()
+        .baseFileOption(Option.of(baseFile))
+        .logFileStream(logs.stream())
+        .partitionPath("")
+        .start(0)
+        .length(Long.MAX_VALUE)
+        .build();
+    HoodieTableMetaClient metaClient = mock(HoodieTableMetaClient.class);
+    when(metaClient.getTableConfig()).thenReturn(tableConfig);
+    ReaderParameters parameters = 
ReaderParameters.builder().emitDeletes(false).build();
+
+    List<String> actual = new ArrayList<>();
+    try (LsmFileGroupRecordIterator<IndexedRecord> iterator = new 
LsmFileGroupRecordIterator<>(
+        context, mock(HoodieStorage.class), split, 
Collections.singletonList("ts"), metaClient,
+        props, parameters, new HoodieReadStats(), Option.empty())) {
+      assertTrue(iterator.hasNext());

Review Comment:
   🤖 nit: the two consecutive `assertTrue(iterator.hasNext())` calls here look 
like a copy-paste mistake at first glance — if this is intentionally testing 
that `hasNext()` is idempotent, a short comment like `// verify hasNext() is 
idempotent` would make the intent clear.
   
   <sub><i>⚠️ AI-generated; verify before applying. React 👍/👎 to flag 
quality.</i></sub>



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

Reply via email to