You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by sd...@apache.org on 2022/09/30 14:43:38 UTC

[ignite-3] branch main updated: IGNITE-17790 Remove IgniteCursor class (#1144)

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

sdanilov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new fcc79e88a5 IGNITE-17790 Remove IgniteCursor class (#1144)
fcc79e88a5 is described below

commit fcc79e88a51db06db9907ddf63b8a4d0732943c3
Author: Alexander Polovtcev <al...@gmail.com>
AuthorDate: Fri Sep 30 17:43:33 2022 +0300

    IGNITE-17790 Remove IgniteCursor class (#1144)
---
 .../org/apache/ignite/internal/util/Cursor.java    |  14 --
 .../apache/ignite/internal/util}/CursorUtils.java  |  28 ++--
 .../apache/ignite/internal/util/IgniteCursor.java  |  67 ----------
 .../ignite/internal/util}/CursorUtilsTest.java     |  11 +-
 .../tree/AbstractBplusTreePageMemoryTest.java      |  86 ++++++------
 .../pagememory/freelist/AbstractFreeList.java      |  54 ++++++--
 .../ignite/internal/pagememory/tree/BplusTree.java | 144 ++++++++++++---------
 .../internal/pagememory/tree/IgniteTree.java       |   6 +-
 .../index/hash/PageMemoryHashIndexStorage.java     |   7 +-
 .../index/sorted/PageMemorySortedIndexStorage.java |  11 +-
 .../mv/AbstractPageMemoryMvPartitionStorage.java   |  66 +++-------
 .../storage/pagememory/util/TreeCursorAdapter.java |  83 ------------
 .../rocksdb/index/RocksDbSortedIndexStorage.java   |   8 +-
 13 files changed, 225 insertions(+), 360 deletions(-)

diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/Cursor.java b/modules/core/src/main/java/org/apache/ignite/internal/util/Cursor.java
index aa9f547684..e4a6f53a07 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/Cursor.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/Cursor.java
@@ -17,7 +17,6 @@
 
 package org.apache.ignite.internal.util;
 
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -28,25 +27,12 @@ import java.util.stream.StreamSupport;
  * @param <T> Type of elements.
  */
 public interface Cursor<T> extends Iterator<T>, Iterable<T>, AutoCloseable {
-    /** Empty cursor instance. */
-    Cursor<?> EMPTY = fromIterator(Collections.emptyIterator());
-
     /** {@inheritDoc} */
     @Override
     default Iterator<T> iterator() {
         return this;
     }
 
-    /**
-     * Creates an empty cursor.
-     *
-     * @param <T> Type of elements in iterator.
-     * @return Cursor.
-     */
-    static <T> Cursor<T> empty() {
-        return (Cursor<T>) EMPTY;
-    }
-
     /**
      * Creates an iterator based cursor.
      *
diff --git a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/CursorUtils.java
similarity index 90%
rename from modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtils.java
rename to modules/core/src/main/java/org/apache/ignite/internal/util/CursorUtils.java
index 050023d63d..6ae560ed76 100644
--- a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/CursorUtils.java
@@ -15,19 +15,31 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.storage.rocksdb.index;
+package org.apache.ignite.internal.util;
 
+import java.util.Collections;
 import java.util.NoSuchElementException;
 import java.util.function.Function;
 import java.util.function.Predicate;
-import org.apache.ignite.internal.util.Cursor;
-import org.apache.ignite.internal.util.IgniteUtils;
 import org.jetbrains.annotations.Nullable;
 
 /**
  * Utility class for working with cursors.
  */
-class CursorUtils {
+public class CursorUtils {
+    /** Empty cursor instance. */
+    private static final Cursor<?> EMPTY = Cursor.fromIterator(Collections.emptyIterator());
+
+    /**
+     * Creates an empty cursor.
+     *
+     * @param <T> Type of elements in iterator.
+     * @return Cursor.
+     */
+    public static <T> Cursor<T> emptyCursor() {
+        return (Cursor<T>) EMPTY;
+    }
+
     /**
      * Cursor wrapper that discards elements while they match a given predicate. As soon as any element does not match the predicate,
      * no more elements will be discarded.
@@ -99,7 +111,7 @@ class CursorUtils {
      * @param <T> Cursor element type.
      * @return Cursor wrapper.
      */
-    static <T> Cursor<T> dropWhile(Cursor<T> cursor, Predicate<T> predicate) {
+    public static <T> Cursor<T> dropWhile(Cursor<T> cursor, Predicate<T> predicate) {
         return new DropWhileCursor<>(cursor, predicate);
     }
 
@@ -171,7 +183,7 @@ class CursorUtils {
      * @param <T> Cursor element type.
      * @return Cursor wrapper.
      */
-    static <T> Cursor<T> takeWhile(Cursor<T> cursor, Predicate<T> predicate) {
+    public static <T> Cursor<T> takeWhile(Cursor<T> cursor, Predicate<T> predicate) {
         return new TakeWhileCursor<>(cursor, predicate);
     }
 
@@ -216,7 +228,7 @@ class CursorUtils {
      * @param <U> Type of the transformed data.
      * @return Cursor wrapper.
      */
-    static <T, U> Cursor<U> map(Cursor<T> cursor, Function<T, U> mapper) {
+    public static <T, U> Cursor<U> map(Cursor<T> cursor, Function<T, U> mapper) {
         return new MapCursor<>(cursor, mapper);
     }
 
@@ -228,7 +240,7 @@ class CursorUtils {
      * @param <T> Cursor element type.
      * @return Cursor that iterates over both given cursors.
      */
-    static <T> Cursor<T> concat(Cursor<T> a, Cursor<T> b) {
+    public static <T> Cursor<T> concat(Cursor<T> a, Cursor<T> b) {
         return new Cursor<>() {
             private Cursor<T> currentCursor = a;
 
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteCursor.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteCursor.java
deleted file mode 100644
index 83053870ff..0000000000
--- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteCursor.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.util;
-
-import java.util.Iterator;
-import org.apache.ignite.lang.IgniteInternalCheckedException;
-
-/**
- * Simple cursor abstraction. Initial state must be "before first".
- */
-public interface IgniteCursor<T> {
-    /**
-     * Attempt to move cursor position forward.
-     *
-     * @return {@code true} If we were able to move position of cursor forward.
-     * @throws IgniteInternalCheckedException If failed.
-     */
-    boolean next() throws IgniteInternalCheckedException;
-
-    /**
-     * Gets element at current position. Must be called only after successful {@link #next()} call.
-     *
-     * @return Element at current position.
-     * @throws IgniteInternalCheckedException If failed.
-     */
-    T get() throws IgniteInternalCheckedException;
-
-    /**
-     * Returns a wrapped {@link IgniteCursor cursor} over an {@link Iterator iterator}.
-     *
-     * @param iterator Iterator.
-     */
-    static <T> IgniteCursor<T> wrap(Iterator<T> iterator) {
-        return new IgniteCursor<>() {
-            T next;
-
-            /** {@inheritDoc} */
-            @Override
-            public boolean next() {
-                next = iterator.hasNext() ? iterator.next() : null;
-
-                return next != null;
-            }
-
-            /** {@inheritDoc} */
-            @Override
-            public T get() {
-                return next;
-            }
-        };
-    }
-}
diff --git a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtilsTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/CursorUtilsTest.java
similarity index 86%
rename from modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtilsTest.java
rename to modules/core/src/test/java/org/apache/ignite/internal/util/CursorUtilsTest.java
index 9e5c6be715..7b888461ab 100644
--- a/modules/storage-rocksdb/src/test/java/org/apache/ignite/internal/storage/rocksdb/index/CursorUtilsTest.java
+++ b/modules/core/src/test/java/org/apache/ignite/internal/util/CursorUtilsTest.java
@@ -15,19 +15,18 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.storage.rocksdb.index;
+package org.apache.ignite.internal.util;
 
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.concat;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.dropWhile;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.map;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.takeWhile;
+import static org.apache.ignite.internal.util.CursorUtils.concat;
+import static org.apache.ignite.internal.util.CursorUtils.dropWhile;
+import static org.apache.ignite.internal.util.CursorUtils.map;
+import static org.apache.ignite.internal.util.CursorUtils.takeWhile;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.emptyIterable;
 import static org.hamcrest.Matchers.is;
 
 import java.util.Arrays;
-import org.apache.ignite.internal.util.Cursor;
 import org.junit.jupiter.api.Test;
 
 /**
diff --git a/modules/page-memory/src/integrationTest/java/org/apache/ignite/internal/pagememory/tree/AbstractBplusTreePageMemoryTest.java b/modules/page-memory/src/integrationTest/java/org/apache/ignite/internal/pagememory/tree/AbstractBplusTreePageMemoryTest.java
index 689c00a3c9..b9d7c03739 100644
--- a/modules/page-memory/src/integrationTest/java/org/apache/ignite/internal/pagememory/tree/AbstractBplusTreePageMemoryTest.java
+++ b/modules/page-memory/src/integrationTest/java/org/apache/ignite/internal/pagememory/tree/AbstractBplusTreePageMemoryTest.java
@@ -89,7 +89,7 @@ import org.apache.ignite.internal.pagememory.tree.io.BplusMetaIo;
 import org.apache.ignite.internal.pagememory.util.PageLockListener;
 import org.apache.ignite.internal.pagememory.util.PageLockListenerNoOp;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
-import org.apache.ignite.internal.util.IgniteCursor;
+import org.apache.ignite.internal.util.Cursor;
 import org.apache.ignite.internal.util.IgniteRandom;
 import org.apache.ignite.internal.util.IgniteStripedLock;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
@@ -331,11 +331,11 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
         }
     }
 
-    private void checkCursor(IgniteCursor<Long> cursor, Iterator<Long> iterator) throws IgniteInternalCheckedException {
-        while (cursor.next()) {
+    private void checkCursor(Cursor<Long> cursor, Iterator<Long> iterator) {
+        while (cursor.hasNext()) {
             assertTrue(iterator.hasNext());
 
-            assertEquals(iterator.next(), cursor.get());
+            assertEquals(iterator.next(), cursor.next());
         }
 
         assertFalse(iterator.hasNext());
@@ -644,7 +644,7 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
             assertNoLocks();
         }
 
-        assertFalse(tree.find(null, null).next());
+        assertFalse(tree.find(null, null).hasNext());
         assertEquals(0, tree.size());
         assertEquals(0, tree.rootLevel());
 
@@ -985,12 +985,12 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
 
         tree.validateTree();
 
-        IgniteCursor<Long> c = tree.find(null, null);
+        Cursor<Long> c = tree.find(null, null);
 
         long x = 0;
 
-        while (c.next()) {
-            assertEquals(Long.valueOf(x++), c.get());
+        while (c.hasNext()) {
+            assertEquals(Long.valueOf(x++), c.next());
         }
 
         assertEquals(keys, x);
@@ -1037,10 +1037,10 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
     }
 
     private void assertEqualContents(IgniteTree<Long, Long> tree, Map<Long, Long> map) throws Exception {
-        IgniteCursor<Long> cursor = tree.find(null, null);
+        Cursor<Long> cursor = tree.find(null, null);
 
-        while (cursor.next()) {
-            Long x = cursor.get();
+        while (cursor.hasNext()) {
+            Long x = cursor.next();
 
             assert x != null;
 
@@ -1060,8 +1060,8 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
 
         TestTree tree = createTestTree(true);
 
-        assertFalse(tree.find(null, null).next());
-        assertFalse(tree.find(0L, 1L).next());
+        assertFalse(tree.find(null, null).hasNext());
+        assertFalse(tree.find(0L, 1L).hasNext());
 
         tree.put(1L);
         tree.put(2L);
@@ -1069,20 +1069,12 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
 
         assertEquals(3, size(tree.find(null, null)));
 
-        assertFalse(tree.find(4L, null).next());
-        assertFalse(tree.find(null, 0L).next());
+        assertFalse(tree.find(4L, null).hasNext());
+        assertFalse(tree.find(null, 0L).hasNext());
 
         assertNoLocks();
     }
 
-    private void doTestCursor(boolean canGetRow) throws Exception {
-        TestTree tree = createTestTree(canGetRow);
-
-        for (long i = 15; i >= 0; i--) {
-            tree.put(i);
-        }
-    }
-
     @Test
     public void testCursorConcurrentMerge() throws Exception {
         MAX_PER_PAGE = 5;
@@ -1108,19 +1100,19 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
 
         Long upperBound = 30_000L + rnd.nextInt(2 * MAX_PER_PAGE);
 
-        IgniteCursor<Long> c = tree.find(null, upperBound);
+        Cursor<Long> c = tree.find(null, upperBound);
         Iterator<Long> i = map.headMap(upperBound, true).keySet().iterator();
 
         Long last = null;
 
         for (int j = 0; j < off; j++) {
-            assertTrue(c.next());
+            assertTrue(c.hasNext());
 
             // println(" <-> " + c.get());
 
-            assertEquals(i.next(), c.get());
+            last = c.next();
 
-            last = c.get();
+            assertEquals(i.next(), last);
 
             assertNoLocks();
         }
@@ -1130,18 +1122,20 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
 
             c = tree.find(last, upperBound);
 
-            assertTrue(c.next());
-            assertEquals(last, c.get());
+            assertTrue(c.hasNext());
+            assertEquals(last, c.next());
 
             assertNoLocks();
         }
 
-        while (c.next()) {
+        while (c.hasNext()) {
             // println(" --> " + c.get());
 
-            assertNotNull(c.get());
-            assertEquals(i.next(), c.get());
-            assertEquals(c.get(), tree.remove(c.get()));
+            Long t = c.next();
+
+            assertNotNull(t);
+            assertEquals(i.next(), t);
+            assertEquals(t, tree.remove(t));
 
             i.remove();
 
@@ -2455,20 +2449,22 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
                 int low = DataStructure.randomInt(CNT);
                 int high = low + DataStructure.randomInt(CNT - low);
 
-                IgniteCursor<Long> c = tree.find((long) low, (long) high);
+                Cursor<Long> c = tree.find((long) low, (long) high);
 
                 Long last = null;
 
-                while (c.next()) {
+                while (c.hasNext()) {
+                    Long t = c.next();
+
                     // Correct bounds.
-                    assertTrue(c.get() >= low, low + " <= " + c.get() + " <= " + high);
-                    assertTrue(c.get() <= high, low + " <= " + c.get() + " <= " + high);
+                    assertTrue(t >= low, low + " <= " + t + " <= " + high);
+                    assertTrue(t <= high, low + " <= " + t + " <= " + high);
 
                     if (last != null) {  // No duplicates.
-                        assertTrue(c.get() > last, low + " <= " + last + " < " + c.get() + " <= " + high);
+                        assertTrue(t > last, low + " <= " + last + " < " + t + " <= " + high);
                     }
 
-                    last = c.get();
+                    last = t;
                 }
 
                 TestTreeFindFirstClosure cl = new TestTreeFindFirstClosure();
@@ -2496,10 +2492,10 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
             asyncRunFut.get(getTestTimeout(), MILLISECONDS);
         }
 
-        IgniteCursor<Long> cursor = tree.find(null, null);
+        Cursor<Long> cursor = tree.find(null, null);
 
-        while (cursor.next()) {
-            Long x = cursor.get();
+        while (cursor.hasNext()) {
+            Long x = cursor.next();
 
             assert x != null;
 
@@ -2515,10 +2511,12 @@ public abstract class AbstractBplusTreePageMemoryTest extends BaseIgniteAbstract
         assertNoLocks();
     }
 
-    private static int size(IgniteCursor<?> c) throws Exception {
+    private static int size(Cursor<?> c) {
         int cnt = 0;
 
-        while (c.next()) {
+        while (c.hasNext()) {
+            c.next();
+
             cnt++;
         }
 
diff --git a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/freelist/AbstractFreeList.java b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/freelist/AbstractFreeList.java
index 28f116a8c2..495ca9f3de 100644
--- a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/freelist/AbstractFreeList.java
+++ b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/freelist/AbstractFreeList.java
@@ -25,6 +25,7 @@ import static org.apache.ignite.internal.util.IgniteUtils.isPow2;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReferenceArray;
 import org.apache.ignite.internal.logger.IgniteLogger;
@@ -42,7 +43,6 @@ import org.apache.ignite.internal.pagememory.reuse.ReuseList;
 import org.apache.ignite.internal.pagememory.util.PageHandler;
 import org.apache.ignite.internal.pagememory.util.PageIdUtils;
 import org.apache.ignite.internal.pagememory.util.PageLockListener;
-import org.apache.ignite.internal.util.IgniteCursor;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.Nullable;
 
@@ -220,7 +220,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
         }
     }
 
-    private final class WriteRowsHandler implements PageHandler<IgniteCursor<T>, Integer> {
+    private final class WriteRowsHandler implements PageHandler<CachedIterator<T>, Integer> {
         /** {@inheritDoc} */
         @Override
         public Integer run(
@@ -229,17 +229,19 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
                 long page,
                 long pageAddr,
                 PageIo iox,
-                IgniteCursor<T> cur,
+                CachedIterator<T> it,
                 int written,
                 IoStatisticsHolder statHolder
         ) throws IgniteInternalCheckedException {
             AbstractDataPageIo<T> io = (AbstractDataPageIo<T>) iox;
 
             // Fill the page up to the end.
-            while (written != COMPLETE || (!evictionTracker.evictionRequired() && cur.next())) {
-                T row = cur.get();
+            while (written != COMPLETE || (!evictionTracker.evictionRequired() && it.hasNext())) {
+                T row = it.get();
 
                 if (written == COMPLETE) {
+                    row = it.next();
+
                     // If the data row was completely written without remainder, proceed to the next.
                     if ((written = writeWholePages(row, statHolder)) == COMPLETE) {
                         continue;
@@ -531,24 +533,24 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
     @Override
     public void insertDataRows(Collection<T> rows, IoStatisticsHolder statHolder) throws IgniteInternalCheckedException {
         try {
-            IgniteCursor<T> cur = IgniteCursor.wrap(rows.iterator());
+            CachedIterator<T> it = new CachedIterator<>(rows.iterator());
 
             int written = COMPLETE;
 
-            while (written != COMPLETE || cur.next()) {
-                T row = cur.get();
-
+            while (written != COMPLETE || it.hasNext()) {
                 // If eviction is required - free up memory before locking the next page.
                 while (evictionTracker.evictionRequired()) {
                     evictionTracker.evictDataPage();
                 }
 
                 if (written == COMPLETE) {
-                    written = writeWholePages(row, statHolder);
+                    written = writeWholePages(it.next(), statHolder);
 
                     continue;
                 }
 
+                T row = it.get();
+
                 AbstractDataPageIo initIo = null;
 
                 long pageId = takePage(row.size() - written, row, statHolder);
@@ -559,7 +561,7 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
                     initIo = row.ioVersions().latest();
                 }
 
-                written = write(pageId, writeRowsHnd, initIo, cur, written, FAIL_I, statHolder);
+                written = write(pageId, writeRowsHnd, initIo, it, written, FAIL_I, statHolder);
 
                 assert written != FAIL_I; // We can't fail here.
             }
@@ -568,6 +570,35 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
         }
     }
 
+    /**
+     * {@link Iterator} implementation that allows to access the current element multiple times.
+     */
+    private static class CachedIterator<T> implements Iterator<T> {
+        private final Iterator<T> it;
+
+        private T next;
+
+        CachedIterator(Iterator<T> it) {
+            this.it = it;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return it.hasNext();
+        }
+
+        @Override
+        public T next() {
+            next = it.next();
+
+            return next;
+        }
+
+        T get() {
+            return next;
+        }
+    }
+
     /**
      * Write fragments of the row, which occupy the whole memory page. A data row is ignored if it is less than the max payload of an empty
      * data page.
@@ -861,4 +892,3 @@ public abstract class AbstractFreeList<T extends Storable> extends PagesList imp
         return "FreeList [name=" + name() + ']';
     }
 }
-
diff --git a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/BplusTree.java b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/BplusTree.java
index 454ae616bd..ee5f63726b 100644
--- a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/BplusTree.java
+++ b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/BplusTree.java
@@ -29,6 +29,7 @@ import static org.apache.ignite.internal.pagememory.tree.BplusTree.Result.NOT_FO
 import static org.apache.ignite.internal.pagememory.tree.BplusTree.Result.RETRY;
 import static org.apache.ignite.internal.pagememory.tree.BplusTree.Result.RETRY_ROOT;
 import static org.apache.ignite.internal.pagememory.util.PageIdUtils.effectivePageId;
+import static org.apache.ignite.internal.util.ArrayUtils.OBJECT_EMPTY_ARRAY;
 import static org.apache.ignite.internal.util.ArrayUtils.clearTail;
 import static org.apache.ignite.internal.util.ArrayUtils.set;
 import static org.apache.ignite.internal.util.IgniteUtils.hexLong;
@@ -43,10 +44,12 @@ import java.util.Comparator;
 import java.util.Deque;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.NoSuchElementException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
+import org.apache.ignite.internal.configuration.storage.StorageException;
 import org.apache.ignite.internal.pagememory.CorruptedDataStructureException;
 import org.apache.ignite.internal.pagememory.PageMemory;
 import org.apache.ignite.internal.pagememory.datastructure.DataStructure;
@@ -64,8 +67,8 @@ import org.apache.ignite.internal.pagememory.tree.io.BplusMetaIo;
 import org.apache.ignite.internal.pagememory.util.PageHandler;
 import org.apache.ignite.internal.pagememory.util.PageLockListener;
 import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.util.Cursor;
 import org.apache.ignite.internal.util.FastTimestamps;
-import org.apache.ignite.internal.util.IgniteCursor;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.apache.ignite.lang.IgniteInternalException;
 import org.apache.ignite.lang.IgniteStringBuilder;
@@ -189,9 +192,6 @@ import org.jetbrains.annotations.Nullable;
  */
 @SuppressWarnings({"ConstantValueVariableUse"})
 public abstract class BplusTree<L, T extends L> extends DataStructure implements IgniteTree<L, T> {
-    /** Empty array. */
-    private static final Object[] EMPTY = {};
-
     /** Destroy msg. */
     public static final String CONC_DESTROY_MSG = "Tree is being concurrently destroyed: ";
 
@@ -239,7 +239,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        protected List<Long> getChildren(final Long pageId) {
+        protected List<Long> getChildren(Long pageId) {
             if (pageId == null || pageId == 0L) {
                 return null;
             }
@@ -295,7 +295,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        protected String formatTreeNode(final Long pageId) {
+        protected String formatTreeNode(Long pageId) {
             if (pageId == null) {
                 return ">NPE<";
             }
@@ -485,8 +485,8 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
             assert p.btmLvl == 0 : "split is impossible with replace";
             assert lvl == 0 : "Replace via page handler is only possible on the leaves level.";
 
-            final int cnt = io.getCount(pageAddr);
-            final int idx = findInsertionPoint(lvl, io, pageAddr, 0, cnt, p.row, 0);
+            int cnt = io.getCount(pageAddr);
+            int idx = findInsertionPoint(lvl, io, pageAddr, 0, cnt, p.row, 0);
 
             if (idx < 0) {
                 // Not found, split or merge happened.
@@ -600,7 +600,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
                 return RETRY;
             }
 
-            final int cnt = io.getCount(leafAddr);
+            int cnt = io.getCount(leafAddr);
 
             assert cnt <= Short.MAX_VALUE : cnt;
 
@@ -1077,14 +1077,14 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      *      address. Otherwise we will not do the lock and will use the given address.
      * @throws IgniteInternalCheckedException If failed.
      */
-    private TreeMetaData treeMeta(final long metaPageAddr) throws IgniteInternalCheckedException {
+    private TreeMetaData treeMeta(long metaPageAddr) throws IgniteInternalCheckedException {
         TreeMetaData meta0 = treeMeta;
 
         if (meta0 != null) {
             return meta0;
         }
 
-        final long metaPage = acquirePage(metaPageId);
+        long metaPage = acquirePage(metaPageId);
 
         try {
             long pageAddr;
@@ -1162,7 +1162,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      *      address. Otherwise we will not do the lock and will use the given address.
      * @return Page ID.
      */
-    private long getFirstPageId(long metaId, long metaPage, int lvl, final long metaPageAddr) {
+    private long getFirstPageId(long metaId, long metaPage, int lvl, long metaPageAddr) {
         long pageAddr = metaPageAddr != 0L ? metaPageAddr : readLock(metaId, metaPage); // Meta can't be removed.
 
         try {
@@ -1194,7 +1194,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      * @return Cursor.
      * @throws IgniteInternalCheckedException If failed.
      */
-    private IgniteCursor<T> findLowerUnbounded(
+    private Cursor<T> findLowerUnbounded(
             L upper,
             boolean upIncl,
             TreeRowClosure<L, T> c,
@@ -1245,13 +1245,13 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
     /** {@inheritDoc} */
     @Override
-    public final IgniteCursor<T> find(L lower, L upper) throws IgniteInternalCheckedException {
+    public final Cursor<T> find(L lower, L upper) throws IgniteInternalCheckedException {
         return find(lower, upper, null);
     }
 
     /** {@inheritDoc} */
     @Override
-    public final IgniteCursor<T> find(L lower, L upper, Object x) throws IgniteInternalCheckedException {
+    public final Cursor<T> find(L lower, L upper, Object x) throws IgniteInternalCheckedException {
         return find(lower, upper, null, x);
     }
 
@@ -1265,7 +1265,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      * @return Cursor.
      * @throws IgniteInternalCheckedException If failed.
      */
-    public IgniteCursor<T> find(L lower, L upper, TreeRowClosure<L, T> c, Object x) throws IgniteInternalCheckedException {
+    public Cursor<T> find(L lower, L upper, TreeRowClosure<L, T> c, Object x) throws IgniteInternalCheckedException {
         return find(lower, upper, true, true, c, x);
     }
 
@@ -1281,7 +1281,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      * @return Cursor.
      * @throws IgniteInternalCheckedException If failed.
      */
-    public IgniteCursor<T> find(
+    public Cursor<T> find(
             @Nullable L lower,
             @Nullable L upper,
             boolean lowIncl,
@@ -1506,7 +1506,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
      * @return Value.
      * @throws IgniteInternalCheckedException If failed.
      */
-    public T findLast(final TreeRowClosure<L, T> c) throws IgniteInternalCheckedException {
+    public T findLast(TreeRowClosure<L, T> c) throws IgniteInternalCheckedException {
         checkDestroyed();
 
         Get g = null;
@@ -1607,7 +1607,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
         }
     }
 
-    private Result findDown(final Get g, final long pageId, final long fwdId, final int lvl) throws IgniteInternalCheckedException {
+    private Result findDown(Get g, long pageId, long fwdId, int lvl) throws IgniteInternalCheckedException {
         long page = acquirePage(pageId);
 
         try {
@@ -2101,11 +2101,11 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
     }
 
     private Result invokeDown(
-            final Invoke x,
-            final long pageId,
-            final long backId,
-            final long fwdId,
-            final int lvl
+            Invoke x,
+            long pageId,
+            long backId,
+            long fwdId,
+            int lvl
     ) throws IgniteInternalCheckedException {
         assert lvl >= 0 : lvl;
 
@@ -2257,11 +2257,11 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
     }
 
     private Result removeDown(
-            final Remove r,
-            final long pageId,
-            final long backId,
-            final long fwdId,
-            final int lvl
+            Remove r,
+            long pageId,
+            long backId,
+            long fwdId,
+            int lvl
     ) throws IgniteInternalCheckedException {
         assert lvl >= 0 : lvl;
 
@@ -2686,7 +2686,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         Deque<IgniteTuple3<Long, Long, Long>> lockedPages = new LinkedList<>();
 
-        final long lockMaxTime = maxLockHoldTime();
+        long lockMaxTime = maxLockHoldTime();
 
         long metaPage = acquirePage(metaPageId);
 
@@ -2917,10 +2917,10 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
         return read(pageId, askNeighbor, g, back ? TRUE.ordinal() : FALSE.ordinal(), RETRY);
     }
 
-    private Result putDown(final Put p, final long pageId, final long fwdId, int lvl) throws IgniteInternalCheckedException {
+    private Result putDown(Put p, long pageId, long fwdId, int lvl) throws IgniteInternalCheckedException {
         assert lvl >= 0 : lvl;
 
-        final long page = acquirePage(pageId);
+        long page = acquirePage(pageId);
 
         try {
             for (; ; ) {
@@ -2993,10 +2993,10 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
     }
 
     private Result visitDown(
-            final TreeVisitor v,
-            final long pageId,
-            final long fwdId,
-            final int lvl
+            TreeVisitor v,
+            long pageId,
+            long fwdId,
+            int lvl
     ) throws IgniteInternalCheckedException {
         long page = acquirePage(pageId);
 
@@ -3322,7 +3322,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        boolean found(BplusIo<L> io, long pageAddr, int idx, int lvl) throws IgniteInternalCheckedException {
+        boolean found(BplusIo<L> io, long pageAddr, int idx, int lvl) {
             throw new IllegalStateException(); // Must never be called because we always have a shift.
         }
 
@@ -3363,7 +3363,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        boolean found(BplusIo<L> io, long pageAddr, int idx, int lvl) throws IgniteInternalCheckedException {
+        boolean found(BplusIo<L> io, long pageAddr, int idx, int lvl) {
             throw new IllegalStateException(); // Must never be called because we always have a shift.
         }
 
@@ -4060,7 +4060,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
          * @param arg Implementation specific argument.
          * @param clo Closure.
          */
-        private Invoke(L row, Object arg, final InvokeClosure<T> clo) {
+        private Invoke(L row, Object arg, InvokeClosure<T> clo) {
             super(row, false);
 
             assert clo != null;
@@ -4462,7 +4462,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
          * @return Added tail.
          */
         protected final Tail<L> addTail(long pageId, long page, long pageAddr, BplusIo<L> io, int lvl, byte type) {
-            final Tail<L> t = new Tail<>(pageId, page, pageAddr, io, type, lvl);
+            Tail<L> t = new Tail<>(pageId, page, pageAddr, io, type, lvl);
 
             if (tail == null) {
                 tail = t;
@@ -6026,14 +6026,14 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
     /**
      * Forward cursor.
      */
-    private final class ForwardCursor extends AbstractForwardCursor implements IgniteCursor<T> {
+    private final class ForwardCursor extends AbstractForwardCursor implements Cursor<T> {
         /** Implementation specific argument. */
         @Nullable
         final Object arg;
 
         /** Rows. */
         @Nullable
-        private T[] rows = (T[]) EMPTY;
+        private T[] rows = (T[]) OBJECT_EMPTY_ARRAY;
 
         /** Row index. */
         private int row = -1;
@@ -6042,6 +6042,9 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
         @Nullable
         private final TreeRowClosure<L, T> filter;
 
+        @Nullable
+        private Boolean hasNext = null;
+
         /**
          * Lower unbound cursor.
          *
@@ -6099,7 +6102,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
                 return false;
             }
 
-            if (rows == EMPTY) {
+            if (rows == OBJECT_EMPTY_ARRAY) {
                 rows = (T[]) new Object[cnt0];
             }
 
@@ -6112,7 +6115,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
             }
 
             if (resCnt == 0) {
-                rows = (T[]) EMPTY;
+                rows = (T[]) OBJECT_EMPTY_ARRAY;
 
                 return false;
             }
@@ -6124,8 +6127,10 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        boolean reinitialize0() throws IgniteInternalCheckedException {
-            return next();
+        boolean reinitialize0() {
+            hasNext = null;
+
+            return hasNext();
         }
 
         /** {@inheritDoc} */
@@ -6134,7 +6139,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
             if (readDone) {
                 rows = null;
             } else {
-                if (rows != EMPTY) {
+                if (rows != OBJECT_EMPTY_ARRAY) {
                     assert rows.length > 0; // Otherwise it makes no sense to create an array.
 
                     // Fake clear.
@@ -6151,22 +6156,16 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        public boolean next() throws IgniteInternalCheckedException {
+        public boolean hasNext() {
             if (rows == null) {
                 return false;
             }
 
-            if (++row < rows.length && rows[row] != null) {
-                clearLastRow(); // Allow to GC the last returned row.
-
-                return true;
+            if (hasNext == null) {
+                hasNext = advance();
             }
 
-            T lastRow = clearLastRow();
-
-            row = 0;
-
-            return nextPage(lastRow);
+            return hasNext;
         }
 
         /**
@@ -6190,13 +6189,42 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
 
         /** {@inheritDoc} */
         @Override
-        public T get() {
+        public T next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+
             T r = rows[row];
 
             assert r != null;
 
+            hasNext = null;
+
             return r;
         }
+
+        private boolean advance() {
+            if (++row < rows.length && rows[row] != null) {
+                clearLastRow(); // Allow to GC the last returned row.
+
+                return true;
+            }
+
+            T lastRow = clearLastRow();
+
+            row = 0;
+
+            try {
+                return nextPage(lastRow);
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Unable to read the next page", e);
+            }
+        }
+
+        @Override
+        public void close() {
+            // No-op.
+        }
     }
 
     /**
diff --git a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/IgniteTree.java b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/IgniteTree.java
index e99faef565..b182029ad8 100644
--- a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/IgniteTree.java
+++ b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/IgniteTree.java
@@ -17,7 +17,7 @@
 
 package org.apache.ignite.internal.pagememory.tree;
 
-import org.apache.ignite.internal.util.IgniteCursor;
+import org.apache.ignite.internal.util.Cursor;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.Nullable;
 
@@ -63,7 +63,7 @@ public interface IgniteTree<L, T> {
      * @param upper Upper bound or {@code null} if unbounded.
      * @throws IgniteInternalCheckedException If failed.
      */
-    IgniteCursor<T> find(@Nullable L lower, @Nullable L upper) throws IgniteInternalCheckedException;
+    Cursor<T> find(@Nullable L lower, @Nullable L upper) throws IgniteInternalCheckedException;
 
     /**
      * Returns a cursor from lower to upper bounds inclusive.
@@ -73,7 +73,7 @@ public interface IgniteTree<L, T> {
      * @param x Implementation specific argument, {@code null} always means that we need to return full detached data row.
      * @throws IgniteInternalCheckedException If failed.
      */
-    IgniteCursor<T> find(@Nullable L lower, @Nullable L upper, @Nullable Object x) throws IgniteInternalCheckedException;
+    Cursor<T> find(@Nullable L lower, @Nullable L upper, @Nullable Object x) throws IgniteInternalCheckedException;
 
     /**
      * Returns a value mapped to the lowest key, or {@code null} if tree is empty.
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/PageMemoryHashIndexStorage.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/PageMemoryHashIndexStorage.java
index dc17e525be..2c88cf930c 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/PageMemoryHashIndexStorage.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/PageMemoryHashIndexStorage.java
@@ -25,9 +25,8 @@ import org.apache.ignite.internal.storage.index.HashIndexStorage;
 import org.apache.ignite.internal.storage.index.IndexRow;
 import org.apache.ignite.internal.storage.pagememory.index.freelist.IndexColumns;
 import org.apache.ignite.internal.storage.pagememory.index.freelist.IndexColumnsFreeList;
-import org.apache.ignite.internal.storage.pagememory.util.TreeCursorAdapter;
 import org.apache.ignite.internal.util.Cursor;
-import org.apache.ignite.internal.util.IgniteCursor;
+import org.apache.ignite.internal.util.CursorUtils;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 
 /**
@@ -83,7 +82,7 @@ public class PageMemoryHashIndexStorage implements HashIndexStorage {
         HashIndexRow lowerBound = new HashIndexRow(indexColumns, lowestRowId);
         HashIndexRow upperBound = new HashIndexRow(indexColumns, highestRowId);
 
-        IgniteCursor<HashIndexRow> cursor;
+        Cursor<HashIndexRow> cursor;
 
         try {
             cursor = hashIndexTree.find(lowerBound, upperBound);
@@ -91,7 +90,7 @@ public class PageMemoryHashIndexStorage implements HashIndexStorage {
             throw new StorageException("Failed to create scan cursor", e);
         }
 
-        return new TreeCursorAdapter<>(cursor, HashIndexRow::rowId);
+        return CursorUtils.map(cursor, HashIndexRow::rowId);
     }
 
     @Override
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/PageMemorySortedIndexStorage.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/PageMemorySortedIndexStorage.java
index 7946c77853..c6e8f9e108 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/PageMemorySortedIndexStorage.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/PageMemorySortedIndexStorage.java
@@ -27,9 +27,8 @@ import org.apache.ignite.internal.storage.index.SortedIndexDescriptor;
 import org.apache.ignite.internal.storage.index.SortedIndexStorage;
 import org.apache.ignite.internal.storage.pagememory.index.freelist.IndexColumns;
 import org.apache.ignite.internal.storage.pagememory.index.freelist.IndexColumnsFreeList;
-import org.apache.ignite.internal.storage.pagememory.util.TreeCursorAdapter;
 import org.apache.ignite.internal.util.Cursor;
-import org.apache.ignite.internal.util.IgniteCursor;
+import org.apache.ignite.internal.util.CursorUtils;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.Nullable;
 
@@ -75,7 +74,7 @@ public class PageMemorySortedIndexStorage implements SortedIndexStorage {
 
         SortedIndexRowKey prefixKey = toSortedIndexRowKey(prefix);
 
-        IgniteCursor<SortedIndexRow> cursor;
+        Cursor<SortedIndexRow> cursor;
 
         try {
             cursor = sortedIndexTree.find(prefixKey, prefixKey);
@@ -83,7 +82,7 @@ public class PageMemorySortedIndexStorage implements SortedIndexStorage {
             throw new StorageException("Failed to create scan cursor", e);
         }
 
-        return new TreeCursorAdapter<>(cursor, SortedIndexRow::rowId);
+        return CursorUtils.map(cursor, SortedIndexRow::rowId);
     }
 
     @Override
@@ -121,7 +120,7 @@ public class PageMemorySortedIndexStorage implements SortedIndexStorage {
 
     @Override
     public Cursor<IndexRow> scan(@Nullable BinaryTuplePrefix lowerBound, @Nullable BinaryTuplePrefix upperBound, int flags) {
-        IgniteCursor<SortedIndexRow> cursor;
+        Cursor<SortedIndexRow> cursor;
 
         try {
             cursor = sortedIndexTree.find(
@@ -136,7 +135,7 @@ public class PageMemorySortedIndexStorage implements SortedIndexStorage {
             throw new StorageException("Failed to create scan cursor", e);
         }
 
-        return new TreeCursorAdapter<>(cursor, this::toIndexRowImpl);
+        return CursorUtils.map(cursor, this::toIndexRowImpl);
     }
 
     private @Nullable SortedIndexRowKey toSortedIndexRowKey(@Nullable BinaryTuplePrefix binaryTuple) {
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AbstractPageMemoryMvPartitionStorage.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AbstractPageMemoryMvPartitionStorage.java
index bc0b4c053c..31dab54e4a 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AbstractPageMemoryMvPartitionStorage.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/AbstractPageMemoryMvPartitionStorage.java
@@ -62,7 +62,7 @@ import org.apache.ignite.internal.storage.pagememory.index.meta.IndexMetaTree;
 import org.apache.ignite.internal.storage.pagememory.index.sorted.PageMemorySortedIndexStorage;
 import org.apache.ignite.internal.storage.pagememory.index.sorted.SortedIndexTree;
 import org.apache.ignite.internal.util.Cursor;
-import org.apache.ignite.internal.util.IgniteCursor;
+import org.apache.ignite.internal.util.CursorUtils;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.Nullable;
 
@@ -142,12 +142,12 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
      */
     public void start() {
         try {
-            IgniteCursor<IndexMeta> cursor = indexMetaTree.find(null, null);
+            Cursor<IndexMeta> cursor = indexMetaTree.find(null, null);
 
             NamedListView<TableIndexView> indexesCfgView = tablesConfiguration.indexes().value();
 
-            while (cursor.next()) {
-                IndexMeta indexMeta = cursor.get();
+            while (cursor.hasNext()) {
+                IndexMeta indexMeta = cursor.next();
 
                 TableIndexView indexCfgView = getByInternalId(indexesCfgView, indexMeta.id());
 
@@ -632,7 +632,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
             VersionChain versionChain = versionChainTree.findOne(new VersionChainKey(rowId));
 
             if (versionChain == null) {
-                return Cursor.empty();
+                return CursorUtils.emptyCursor();
             }
 
             RowVersion head = readRowVersion(versionChain.headLink(), ALWAYS_LOAD_VALUE);
@@ -656,7 +656,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
     public PartitionTimestampCursor scan(Predicate<BinaryRow> keyFilter, HybridTimestamp timestamp) throws StorageException {
         assert timestamp != null;
 
-        IgniteCursor<VersionChain> treeCursor;
+        Cursor<VersionChain> treeCursor;
 
         try {
             treeCursor = versionChainTree.find(null, null);
@@ -670,7 +670,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
     private Cursor<BinaryRow> internalScan(Predicate<BinaryRow> keyFilter, UUID txId) {
         assert txId != null;
 
-        IgniteCursor<VersionChain> treeCursor;
+        Cursor<VersionChain> treeCursor;
 
         try {
             treeCursor = versionChainTree.find(null, null);
@@ -716,7 +716,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
      * See {@link PartitionTimestampCursor} for the details on the API.
      */
     private class TimestampCursor implements PartitionTimestampCursor {
-        private final IgniteCursor<VersionChain> treeCursor;
+        private final Cursor<VersionChain> treeCursor;
 
         private final Predicate<BinaryRow> keyFilter;
 
@@ -730,7 +730,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
 
         private boolean iterationExhausted = false;
 
-        public TimestampCursor(IgniteCursor<VersionChain> treeCursor, Predicate<BinaryRow> keyFilter, HybridTimestamp timestamp) {
+        public TimestampCursor(Cursor<VersionChain> treeCursor, Predicate<BinaryRow> keyFilter, HybridTimestamp timestamp) {
             this.treeCursor = treeCursor;
             this.keyFilter = keyFilter;
             this.timestamp = timestamp;
@@ -749,15 +749,13 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
             currentChain = null;
 
             while (true) {
-                boolean positionedToNext = tryAdvanceTreeCursor();
-
-                if (!positionedToNext) {
+                if (!treeCursor.hasNext()) {
                     iterationExhausted = true;
 
                     return false;
                 }
 
-                VersionChain chain = getCurrentChainFromTreeCursor();
+                VersionChain chain = treeCursor.next();
                 ReadResult res = findRowVersionByTimestamp(chain, timestamp);
 
                 if (res.isEmpty()) {
@@ -775,22 +773,6 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
             }
         }
 
-        private boolean tryAdvanceTreeCursor() {
-            try {
-                return treeCursor.next();
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Error when trying to advance tree cursor", e);
-            }
-        }
-
-        private VersionChain getCurrentChainFromTreeCursor() {
-            try {
-                return treeCursor.get();
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Failed to get element from tree cursor", e);
-            }
-        }
-
         @Override
         public ReadResult next() {
             if (!hasNext()) {
@@ -830,7 +812,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
      * or already committed in a different transaction.
      */
     private class TransactionIdCursor implements Cursor<BinaryRow> {
-        private final IgniteCursor<VersionChain> treeCursor;
+        private final Cursor<VersionChain> treeCursor;
 
         private final Predicate<BinaryRow> keyFilter;
 
@@ -841,7 +823,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
         private boolean iterationExhausted = false;
 
         public TransactionIdCursor(
-                IgniteCursor<VersionChain> treeCursor,
+                Cursor<VersionChain> treeCursor,
                 Predicate<BinaryRow> keyFilter,
                 @Nullable UUID transactionId
         ) {
@@ -861,14 +843,12 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
             }
 
             while (true) {
-                boolean positionedToNext = tryAdvanceTreeCursor();
-
-                if (!positionedToNext) {
+                if (!treeCursor.hasNext()) {
                     iterationExhausted = true;
                     return false;
                 }
 
-                VersionChain chain = getCurrentChainFromTreeCursor();
+                VersionChain chain = treeCursor.next();
                 BinaryRow row = findLatestRowVersion(chain, transactionId, keyFilter);
 
                 if (row != null) {
@@ -878,22 +858,6 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
             }
         }
 
-        private boolean tryAdvanceTreeCursor() {
-            try {
-                return treeCursor.next();
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Error when trying to advance tree cursor", e);
-            }
-        }
-
-        private VersionChain getCurrentChainFromTreeCursor() {
-            try {
-                return treeCursor.get();
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Failed to get element from tree cursor", e);
-            }
-        }
-
         @Override
         public BinaryRow next() {
             if (!hasNext()) {
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/util/TreeCursorAdapter.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/util/TreeCursorAdapter.java
deleted file mode 100644
index fc67539f90..0000000000
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/util/TreeCursorAdapter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.storage.pagememory.util;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.function.Function;
-import org.apache.ignite.internal.storage.StorageException;
-import org.apache.ignite.internal.util.Cursor;
-import org.apache.ignite.internal.util.IgniteCursor;
-import org.apache.ignite.lang.IgniteInternalCheckedException;
-
-/**
- * Wraps {@link IgniteCursor} into a {@link Iterator}.
- *
- * @param <TREE_ROWT> Type of elements in a tree cursor.
- * @param <CURSOR_ROWT> Type of elements in a resulting iterator.
- */
-public class TreeCursorAdapter<TREE_ROWT, CURSOR_ROWT> implements Cursor<CURSOR_ROWT> {
-    /** Cursor instance from the tree. */
-    private final IgniteCursor<TREE_ROWT> cursor;
-
-    /** Value mapper to transform the data. */
-    private final Function<TREE_ROWT, CURSOR_ROWT> mapper;
-
-    /** Cached {@link IgniteCursor#next()} value. */
-    private Boolean hasNext;
-
-    public TreeCursorAdapter(IgniteCursor<TREE_ROWT> cursor, Function<TREE_ROWT, CURSOR_ROWT> mapper) {
-        this.cursor = cursor;
-        this.mapper = mapper;
-    }
-
-    @Override
-    public boolean hasNext() throws StorageException {
-        try {
-            if (hasNext == null) {
-                hasNext = cursor.next();
-            }
-
-            return hasNext;
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to read next element from the tree", e);
-        }
-    }
-
-    @Override
-    public CURSOR_ROWT next() throws NoSuchElementException, StorageException {
-        if (!hasNext()) {
-            throw new NoSuchElementException();
-        }
-
-        try {
-            TREE_ROWT treeRow = cursor.get();
-
-            hasNext = null;
-
-            return mapper.apply(treeRow);
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to read next element from the tree", e);
-        }
-    }
-
-    @Override
-    public void close() throws Exception {
-        // no-op
-    }
-}
diff --git a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorage.java b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorage.java
index 38dfe81389..603bb6b7bb 100644
--- a/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorage.java
+++ b/modules/storage-rocksdb/src/main/java/org/apache/ignite/internal/storage/rocksdb/index/RocksDbSortedIndexStorage.java
@@ -17,11 +17,11 @@
 
 package org.apache.ignite.internal.storage.rocksdb.index;
 
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.concat;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.dropWhile;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.map;
-import static org.apache.ignite.internal.storage.rocksdb.index.CursorUtils.takeWhile;
 import static org.apache.ignite.internal.util.ArrayUtils.BYTE_EMPTY_ARRAY;
+import static org.apache.ignite.internal.util.CursorUtils.concat;
+import static org.apache.ignite.internal.util.CursorUtils.dropWhile;
+import static org.apache.ignite.internal.util.CursorUtils.map;
+import static org.apache.ignite.internal.util.CursorUtils.takeWhile;
 
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;