Repository: apache/rocketmq (Java)
Date: 2023-09-11 15:05:07+00:00
Task type: Bug Fix

Issue:
[Bug] wrong result of searchOffset in tiered storage
### Before Creating the Bug Report

- [X] I found a bug, not just asking a question, which should be created in [GitHub Discussions](https://github.com/apache/rocketmq/discussions).

- [X] I have searched the [GitHub Issues](https://github.com/apache/rocketmq/issues) and [GitHub Discussions](https://github.com/apache/rocketmq/discussions)  of this repository and believe that this is not a duplicate.

- [X] I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.


### Runtime platform environment

Ubuntu

### RocketMQ version

5.1.0

### JDK Version

1.8.0

### Describe the Bug

There are 3 segments in fileSegmentList, TieredFlatFile.getFileByTime(timestamp=0, boundaryType=LOWER) should return the first segment, but now returns the last segment.

![image](https://github.com/apache/rocketmq/assets/14816818/61133d1e-27b2-4f04-a781-a17f91d7c939)


### Steps to Reproduce

1. start broker with tiered storage
2. send some messages.
3. use consumer.searchOffset to search offset with parameter timestamp=0, boundaryType=LOWER

### What Did You Expect to See?

searchOffset  should return min offset.


### What Did You See Instead?

searchOffset not returns min offset.


### Additional Context

_No response_


Issue discussion:

Solution patch:
diff --git a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/TieredFlatFile.java b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/TieredFlatFile.java
--- a/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/TieredFlatFile.java
+++ b/tieredstore/src/main/java/org/apache/rocketmq/tieredstore/file/TieredFlatFile.java
@@ -365,7 +365,10 @@ protected TieredFileSegment getFileByTime(long timestamp, BoundaryType boundaryT
             if (!segmentList.isEmpty()) {
                 return boundaryType == BoundaryType.UPPER ? segmentList.get(0) : segmentList.get(segmentList.size() - 1);
             }
-            return fileSegmentList.isEmpty() ? null : fileSegmentList.get(fileSegmentList.size() - 1);
+            if (fileSegmentList.isEmpty()) {
+                return null;
+            }
+            return boundaryType == BoundaryType.UPPER ? fileSegmentList.get(fileSegmentList.size() - 1) : fileSegmentList.get(0);
         } finally {
             fileSegmentLock.readLock().unlock();
         }


Test patch:
diff --git a/tieredstore/src/test/java/org/apache/rocketmq/tieredstore/file/TieredFlatFileTest.java b/tieredstore/src/test/java/org/apache/rocketmq/tieredstore/file/TieredFlatFileTest.java
--- a/tieredstore/src/test/java/org/apache/rocketmq/tieredstore/file/TieredFlatFileTest.java
+++ b/tieredstore/src/test/java/org/apache/rocketmq/tieredstore/file/TieredFlatFileTest.java
@@ -16,10 +16,7 @@
  */
 package org.apache.rocketmq.tieredstore.file;
 
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
+import org.apache.rocketmq.common.BoundaryType;
 import org.apache.rocketmq.common.message.MessageQueue;
 import org.apache.rocketmq.tieredstore.TieredStoreTestUtil;
 import org.apache.rocketmq.tieredstore.common.FileSegmentType;
@@ -35,6 +32,11 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
 public class TieredFlatFileTest {
 
     private final String storePath = TieredStoreTestUtil.getRandomStorePath();
@@ -301,4 +303,40 @@ public void testRollingNewFile() {
         fileQueue.rollingNewFile();
         Assert.assertEquals(2, fileQueue.getFileSegmentCount());
     }
+
+    @Test
+    public void testGetFileByTime() {
+        String filePath = TieredStoreUtil.toPath(queue);
+        TieredFlatFile tieredFlatFile = fileQueueFactory.createFlatFileForCommitLog(filePath);
+        TieredFileSegment fileSegment1 = new MemoryFileSegment(FileSegmentType.CONSUME_QUEUE, queue, 1100, storeConfig);
+        fileSegment1.setMinTimestamp(100);
+        fileSegment1.setMaxTimestamp(200);
+
+        TieredFileSegment fileSegment2 = new MemoryFileSegment(FileSegmentType.CONSUME_QUEUE, queue, 1100, storeConfig);
+        fileSegment2.setMinTimestamp(200);
+        fileSegment2.setMaxTimestamp(300);
+
+        tieredFlatFile.getFileSegmentList().add(fileSegment1);
+        tieredFlatFile.getFileSegmentList().add(fileSegment2);
+
+        TieredFileSegment segmentUpper = tieredFlatFile.getFileByTime(400, BoundaryType.UPPER);
+        Assert.assertEquals(fileSegment2, segmentUpper);
+
+        TieredFileSegment segmentLower = tieredFlatFile.getFileByTime(400, BoundaryType.LOWER);
+        Assert.assertEquals(fileSegment2, segmentLower);
+
+
+        TieredFileSegment segmentUpper2 = tieredFlatFile.getFileByTime(0, BoundaryType.UPPER);
+        Assert.assertEquals(fileSegment1, segmentUpper2);
+
+        TieredFileSegment segmentLower2 = tieredFlatFile.getFileByTime(0, BoundaryType.LOWER);
+        Assert.assertEquals(fileSegment1, segmentLower2);
+
+
+        TieredFileSegment segmentUpper3 = tieredFlatFile.getFileByTime(200, BoundaryType.UPPER);
+        Assert.assertEquals(fileSegment1, segmentUpper3);
+
+        TieredFileSegment segmentLower3 = tieredFlatFile.getFileByTime(200, BoundaryType.LOWER);
+        Assert.assertEquals(fileSegment2, segmentLower3);
+    }
 }