This is an automated email from the ASF dual-hosted git repository.

smengcl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a81f47f661 HDDS-16250. Improve RocksDB read performance by reducing 
unnecessary iterator positioning (#11084)
8a81f47f661 is described below

commit 8a81f47f66129283cd6124c178fcf16f48539722
Author: Siyao Meng <[email protected]>
AuthorDate: Sat Aug 22 00:10:15 2026 -0700

    HDDS-16250. Improve RocksDB read performance by reducing unnecessary 
iterator positioning (#11084)
    
    Generated-by: Codex (GPT-5.6 Sol)
---
 .../hdds/utils/db/RDBStoreAbstractIterator.java    | 18 ++++++++++++
 .../hdds/utils/db/RDBStoreByteArrayIterator.java   |  1 -
 .../hdds/utils/db/RDBStoreCodecBufferIterator.java |  1 -
 .../utils/db/TestRDBStoreByteArrayIterator.java    | 29 +++++++++++-------
 .../utils/db/TestRDBStoreCodecBufferIterator.java  | 34 +++++++++++++++-------
 5 files changed, 59 insertions(+), 24 deletions(-)

diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreAbstractIterator.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreAbstractIterator.java
index 9ebaf368e9f..15cac80dbd0 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreAbstractIterator.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreAbstractIterator.java
@@ -43,6 +43,10 @@ abstract class RDBStoreAbstractIterator<RAW>
   private final RAW prefix;
   private final IteratorType type;
   private final AtomicBoolean isIteratorClosed = new AtomicBoolean(false);
+  // Defer positioning until the first iterator operation. Explicit positioning
+  // avoids a redundant seek, while ordinary iteration still starts at the 
table
+  // or prefix start through initializeIfNeeded().
+  private boolean initialized;
 
   /**
    * Constructor for RDBStoreAbstractIterator.
@@ -107,17 +111,27 @@ private void setCurrentEntry() {
     }
   }
 
+  private void initializeIfNeeded() {
+    if (!initialized) {
+      // Preserve the original start position for callers that iterate without
+      // explicitly positioning the iterator.
+      seekToFirst();
+    }
+  }
+
   @Override
   public final boolean hasNext() {
     if (isDbClosed()) {
       return false;
     }
+    initializeIfNeeded();
     return rocksDBIterator.get().isValid() &&
         (prefix == null || startsWithPrefix(key()));
   }
 
   @Override
   public final Table.KeyValue<RAW, RAW> next() {
+    initializeIfNeeded();
     setCurrentEntry();
     if (currentEntry != null) {
       rocksDBIterator.get().next();
@@ -133,6 +147,7 @@ public final void seekToFirst() {
     } else {
       seek0(prefix);
     }
+    initialized = true;
     setCurrentEntry();
   }
 
@@ -143,12 +158,14 @@ public final void seekToLast() {
     } else {
       throw new UnsupportedOperationException("seekToLast: prefix != null");
     }
+    initialized = true;
     setCurrentEntry();
   }
 
   @Override
   public final Table.KeyValue<RAW, RAW> seek(RAW key) {
     seek0(key);
+    initialized = true;
     setCurrentEntry();
     return currentEntry;
   }
@@ -158,6 +175,7 @@ public final void removeFromDB() throws 
RocksDatabaseException, CodecException {
     if (rocksDBTable == null) {
       throw new UnsupportedOperationException("remove");
     }
+    initializeIfNeeded();
     if (currentEntry != null) {
       delete(currentEntry.getKey());
     } else {
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreByteArrayIterator.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreByteArrayIterator.java
index 67593f744e3..4238f52fa7c 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreByteArrayIterator.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreByteArrayIterator.java
@@ -31,7 +31,6 @@ private static byte[] copyPrefix(byte[] prefix) {
   RDBStoreByteArrayIterator(ManagedRocksIterator iterator,
       RDBTable table, byte[] prefix, IteratorType type) {
     super(iterator, table, copyPrefix(prefix), type);
-    seekToFirst();
   }
 
   @Override
diff --git 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreCodecBufferIterator.java
 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreCodecBufferIterator.java
index aa703249ebe..5430d725651 100644
--- 
a/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreCodecBufferIterator.java
+++ 
b/hadoop-hdds/framework/src/main/java/org/apache/hadoop/hdds/utils/db/RDBStoreCodecBufferIterator.java
@@ -43,7 +43,6 @@ class RDBStoreCodecBufferIterator extends 
RDBStoreAbstractIterator<CodecBuffer>
     this.valueBuffer = new Buffer(
         new CodecBuffer.Capacity(name + "-iterator-value", 4 << 10),
         getType().readValue() ? buffer -> 
getRocksDBIterator().get().value(buffer) : null);
-    seekToFirst();
   }
 
   void assertOpen() {
diff --git 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
index 136990522b7..0ca97b9e521 100644
--- 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
+++ 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreByteArrayIterator.java
@@ -122,6 +122,7 @@ public void testHasNextDependsOnIsvalid() {
 
     assertTrue(iter.hasNext());
     assertFalse(iter.hasNext());
+    verify(rocksDBIteratorMock, times(1)).seekToFirst();
   }
 
   @Test
@@ -133,6 +134,7 @@ public void 
testNextCallsIsValidThenGetsTheValueAndStepsToNext() {
 
     iter.next();
 
+    verifier.verify(rocksDBIteratorMock).seekToFirst();
     verifier.verify(rocksDBIteratorMock).isValid();
     verifier.verify(rocksDBIteratorMock).key();
     verifier.verify(rocksDBIteratorMock).value();
@@ -140,10 +142,10 @@ public void 
testNextCallsIsValidThenGetsTheValueAndStepsToNext() {
   }
 
   @Test
-  public void testConstructorSeeksToFirstElement() {
-    newIterator();
+  public void testConstructorDoesNotSeek() {
+    newIterator().close();
 
-    verify(rocksDBIteratorMock, times(1)).seekToFirst();
+    verify(rocksDBIteratorMock, never()).seekToFirst();
   }
 
   @Test
@@ -152,7 +154,7 @@ public void testSeekToFirstSeeks() {
 
     iter.seekToFirst();
 
-    verify(rocksDBIteratorMock, times(2)).seekToFirst();
+    verify(rocksDBIteratorMock, times(1)).seekToFirst();
   }
 
   @Test
@@ -161,23 +163,28 @@ public void testSeekToLastSeeks() {
 
     iter.seekToLast();
 
+    verify(rocksDBIteratorMock, never()).seekToFirst();
     verify(rocksDBIteratorMock, times(1)).seekToLast();
   }
 
   @Test
-  public void testSeekReturnsTheActualKey() throws Exception {
+  public void testSeekSkipsInitialPrefixSeekAndReturnsActualKey()
+      throws Exception {
     when(rocksDBIteratorMock.isValid()).thenReturn(true);
     when(rocksDBIteratorMock.key()).thenReturn(new byte[]{0x00});
     when(rocksDBIteratorMock.value()).thenReturn(new byte[]{0x7f});
 
-    RDBStoreByteArrayIterator iter = newIterator();
-    final Table.KeyValue<byte[], byte[]> val = iter.seek(new byte[]{0x55});
+    RDBStoreByteArrayIterator iter = newIterator(new byte[]{0x11});
+    byte[] target = new byte[]{0x55};
+    final Table.KeyValue<byte[], byte[]> val = iter.seek(target);
 
     InOrder verifier = inOrder(rocksDBIteratorMock);
+    ArgumentCaptor<byte[]> seekKey = forClass(byte[].class);
 
-    verify(rocksDBIteratorMock, times(1)).seekToFirst(); //at construct time
+    verify(rocksDBIteratorMock, never()).seekToFirst();
     verify(rocksDBIteratorMock, never()).seekToLast();
-    verifier.verify(rocksDBIteratorMock, times(1)).seek(any(byte[].class));
+    verifier.verify(rocksDBIteratorMock, times(1)).seek(seekKey.capture());
+    assertArrayEquals(target, seekKey.getValue());
     verifier.verify(rocksDBIteratorMock, times(1)).isValid();
     verifier.verify(rocksDBIteratorMock, times(1)).key();
     verifier.verify(rocksDBIteratorMock, times(1)).value();
@@ -261,7 +268,7 @@ public void testCloseCloses() throws Exception {
   @Test
   public void testNullPrefixedIterator() throws IOException {
     RDBStoreByteArrayIterator iter = newIterator(null);
-    verify(rocksDBIteratorMock, times(1)).seekToFirst();
+    verify(rocksDBIteratorMock, never()).seekToFirst();
     clearInvocations(rocksDBIteratorMock);
 
     iter.seekToFirst();
@@ -283,7 +290,7 @@ public void testNullPrefixedIterator() throws IOException {
   public void testNormalPrefixedIterator() throws IOException {
     byte[] testPrefix = "sample".getBytes(StandardCharsets.UTF_8);
     RDBStoreByteArrayIterator iter = newIterator(testPrefix);
-    verify(rocksDBIteratorMock, times(1)).seek(testPrefix);
+    verify(rocksDBIteratorMock, never()).seek(any(byte[].class));
     clearInvocations(rocksDBIteratorMock);
 
     iter.seekToFirst();
diff --git 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreCodecBufferIterator.java
 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreCodecBufferIterator.java
index cddb11e9528..e9fddd20966 100644
--- 
a/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreCodecBufferIterator.java
+++ 
b/hadoop-hdds/framework/src/test/java/org/apache/hadoop/hdds/utils/db/TestRDBStoreCodecBufferIterator.java
@@ -23,6 +23,7 @@
 import static org.junit.jupiter.api.Assertions.assertInstanceOf;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentCaptor.forClass;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.inOrder;
@@ -45,6 +46,7 @@
 import org.apache.log4j.Logger;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.stubbing.Answer;
 import org.rocksdb.RocksIterator;
@@ -138,6 +140,7 @@ public void testHasNextDependsOnIsvalid() throws Exception {
       assertTrue(i.hasNext());
       assertFalse(i.hasNext());
     }
+    verify(rocksIteratorMock, times(1)).seekToFirst();
 
     CodecTestUtil.gc();
   }
@@ -151,6 +154,7 @@ public void 
testNextCallsIsValidThenGetsTheValueAndStepsToNext()
       i.next();
     }
 
+    verifier.verify(rocksIteratorMock).seekToFirst();
     verifier.verify(rocksIteratorMock).isValid();
     verifier.verify(rocksIteratorMock).key(any(ByteBuffer.class));
     verifier.verify(rocksIteratorMock).value(any(ByteBuffer.class));
@@ -160,10 +164,10 @@ public void 
testNextCallsIsValidThenGetsTheValueAndStepsToNext()
   }
 
   @Test
-  public void testConstructorSeeksToFirstElement() throws Exception {
+  public void testConstructorDoesNotSeek() throws Exception {
     newIterator().close();
 
-    verify(rocksIteratorMock, times(1)).seekToFirst();
+    verify(rocksIteratorMock, never()).seekToFirst();
 
     CodecTestUtil.gc();
   }
@@ -173,7 +177,7 @@ public void testSeekToFirstSeeks() throws Exception {
     try (RDBStoreCodecBufferIterator i = newIterator()) {
       i.seekToFirst();
     }
-    verify(rocksIteratorMock, times(2)).seekToFirst();
+    verify(rocksIteratorMock, times(1)).seekToFirst();
 
     CodecTestUtil.gc();
   }
@@ -184,29 +188,37 @@ public void testSeekToLastSeeks() throws Exception {
       i.seekToLast();
     }
 
+    verify(rocksIteratorMock, never()).seekToFirst();
     verify(rocksIteratorMock, times(1)).seekToLast();
 
     CodecTestUtil.gc();
   }
 
   @Test
-  public void testSeekReturnsTheActualKey() throws Exception {
+  public void testSeekSkipsInitialPrefixSeekAndReturnsActualKey()
+      throws Exception {
     when(rocksIteratorMock.isValid()).thenReturn(true);
     when(rocksIteratorMock.key(any(ByteBuffer.class)))
         .then(newAnswerInt("key1", 0x00));
     when(rocksIteratorMock.value(any(ByteBuffer.class)))
         .then(newAnswerInt("val1", 0x7f));
 
-    try (RDBStoreCodecBufferIterator i = newIterator();
-         CodecBuffer target = CodecBuffer.wrap(new byte[]{0x55})) {
+    byte[] targetBytes = new byte[]{0x55};
+    try (RDBStoreCodecBufferIterator i = newIterator(
+        CodecBuffer.wrap(new byte[]{0x11}));
+         CodecBuffer target = CodecBuffer.wrap(targetBytes)) {
       final Table.KeyValue<CodecBuffer, CodecBuffer> val = i.seek(target);
 
       InOrder verifier = inOrder(rocksIteratorMock);
+      ArgumentCaptor<ByteBuffer> seekKey = forClass(ByteBuffer.class);
 
-      verify(rocksIteratorMock, times(1)).seekToFirst(); //at construct time
+      verify(rocksIteratorMock, never()).seekToFirst();
       verify(rocksIteratorMock, never()).seekToLast();
-      verifier.verify(rocksIteratorMock, times(1))
-          .seek(any(ByteBuffer.class));
+      verifier.verify(rocksIteratorMock, times(1)).seek(seekKey.capture());
+      ByteBuffer actualSeekKey = seekKey.getValue().duplicate();
+      byte[] actualSeekKeyBytes = new byte[actualSeekKey.remaining()];
+      actualSeekKey.get(actualSeekKeyBytes);
+      assertArrayEquals(targetBytes, actualSeekKeyBytes);
       verifier.verify(rocksIteratorMock, times(1)).isValid();
       verifier.verify(rocksIteratorMock, times(1)).key(any(ByteBuffer.class));
       verifier.verify(rocksIteratorMock, 
times(1)).value(any(ByteBuffer.class));
@@ -310,7 +322,7 @@ public void testCloseCloses() throws Exception {
   @Test
   public void testNullPrefixedIterator() throws Exception {
     try (RDBStoreCodecBufferIterator i = newIterator()) {
-      verify(rocksIteratorMock, times(1)).seekToFirst();
+      verify(rocksIteratorMock, never()).seekToFirst();
       clearInvocations(rocksIteratorMock);
 
       i.seekToFirst();
@@ -335,7 +347,7 @@ public void testNormalPrefixedIterator() throws Exception {
     try (RDBStoreCodecBufferIterator i = newIterator(
         CodecBuffer.wrap(prefixBytes))) {
       final ByteBuffer prefix = ByteBuffer.wrap(prefixBytes);
-      verify(rocksIteratorMock, times(1)).seek(prefix);
+      verify(rocksIteratorMock, never()).seek(any(ByteBuffer.class));
       clearInvocations(rocksIteratorMock);
 
       i.seekToFirst();


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

Reply via email to