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

tkalkirill 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 df6a3b5b6b IGNITE-18571 Fix busyLock usage in 
AbstractPageMemoryTableStorage and related (#1545)
df6a3b5b6b is described below

commit df6a3b5b6b13f308a430f2b21eff1c896dd3f386
Author: Kirill Tkalenko <[email protected]>
AuthorDate: Wed Jan 18 17:49:20 2023 +0300

    IGNITE-18571 Fix busyLock usage in AbstractPageMemoryTableStorage and 
related (#1545)
---
 .../pagememory/AbstractPageMemoryTableStorage.java | 140 ++++---
 .../PersistentPageMemoryStorageEngine.java         |   2 +-
 .../PersistentPageMemoryTableStorage.java          |  10 +-
 .../pagememory/VolatilePageMemoryTableStorage.java |   2 +-
 .../index/hash/PageMemoryHashIndexStorage.java     | 154 +++-----
 .../index/sorted/PageMemorySortedIndexStorage.java | 237 +++++------
 .../mv/AbstractPageMemoryMvPartitionStorage.java   | 432 ++++++++++-----------
 .../mv/PersistentPageMemoryMvPartitionStorage.java | 171 +++-----
 .../mv/VolatilePageMemoryMvPartitionStorage.java   |  95 +----
 9 files changed, 531 insertions(+), 712 deletions(-)

diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/AbstractPageMemoryTableStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/AbstractPageMemoryTableStorage.java
index 0c6759bb88..ce3625e62d 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/AbstractPageMemoryTableStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/AbstractPageMemoryTableStorage.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.storage.pagememory;
 
 import static java.util.concurrent.CompletableFuture.completedFuture;
+import static org.apache.ignite.internal.util.IgniteUtils.inBusyLock;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -25,19 +26,21 @@ import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.function.Supplier;
 import org.apache.ignite.internal.pagememory.DataRegion;
 import org.apache.ignite.internal.pagememory.PageMemory;
 import org.apache.ignite.internal.schema.configuration.TableConfiguration;
 import org.apache.ignite.internal.schema.configuration.TableView;
 import org.apache.ignite.internal.schema.configuration.TablesConfiguration;
-import org.apache.ignite.internal.storage.MvPartitionStorage;
 import org.apache.ignite.internal.storage.StorageException;
 import org.apache.ignite.internal.storage.engine.MvTableStorage;
 import org.apache.ignite.internal.storage.index.HashIndexStorage;
 import org.apache.ignite.internal.storage.index.SortedIndexStorage;
 import 
org.apache.ignite.internal.storage.pagememory.mv.AbstractPageMemoryMvPartitionStorage;
 import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.internal.util.IgniteSpinBusyLock;
 import org.apache.ignite.internal.util.IgniteUtils;
 import org.jetbrains.annotations.Nullable;
 
@@ -47,14 +50,17 @@ import org.jetbrains.annotations.Nullable;
 public abstract class AbstractPageMemoryTableStorage implements MvTableStorage 
{
     protected final TableConfiguration tableCfg;
 
-    protected TablesConfiguration tablesConfiguration;
-
-    protected volatile boolean started;
+    protected final TablesConfiguration tablesCfg;
 
     protected volatile 
AtomicReferenceArray<AbstractPageMemoryMvPartitionStorage> mvPartitions;
 
     protected final ConcurrentMap<Integer, CompletableFuture<Void>> 
partitionIdDestroyFutureMap = new ConcurrentHashMap<>();
 
+    private final IgniteSpinBusyLock busyLock = new IgniteSpinBusyLock();
+
+    /** Prevents double stopping of the component. */
+    private final AtomicBoolean stopGuard = new AtomicBoolean();
+
     /**
      * Constructor.
      *
@@ -62,7 +68,7 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
      */
     protected AbstractPageMemoryTableStorage(TableConfiguration tableCfg, 
TablesConfiguration tablesCfg) {
         this.tableCfg = tableCfg;
-        tablesConfiguration = tablesCfg;
+        this.tablesCfg = tablesCfg;
     }
 
     @Override
@@ -72,7 +78,7 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
 
     @Override
     public TablesConfiguration tablesConfiguration() {
-        return tablesConfiguration;
+        return tablesCfg;
     }
 
     /**
@@ -82,16 +88,22 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
 
     @Override
     public void start() throws StorageException {
-        TableView tableView = tableCfg.value();
+        busy(() -> {
+            TableView tableView = tableCfg.value();
 
-        mvPartitions = new AtomicReferenceArray<>(tableView.partitions());
+            mvPartitions = new AtomicReferenceArray<>(tableView.partitions());
 
-        started = true;
+            return null;
+        });
     }
 
     @Override
     public void stop() throws StorageException {
-        started = false;
+        if (!stopGuard.compareAndSet(false, true)) {
+            return;
+        }
+
+        busyLock.block();
 
         List<AutoCloseable> closeables = new ArrayList<>();
 
@@ -112,7 +124,11 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
 
     @Override
     public CompletableFuture<Void> destroy() {
-        started = false;
+        if (!stopGuard.compareAndSet(false, true)) {
+            return completedFuture(null);
+        }
+
+        busyLock.block();
 
         List<CompletableFuture<Void>> destroyFutures = new ArrayList<>();
 
@@ -167,25 +183,29 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
 
     @Override
     public AbstractPageMemoryMvPartitionStorage getOrCreateMvPartition(int 
partitionId) throws StorageException {
-        AbstractPageMemoryMvPartitionStorage partition = 
getMvPartition(partitionId);
+        return busy(() -> {
+            AbstractPageMemoryMvPartitionStorage partition = 
getMvPartitionBusy(partitionId);
 
-        if (partition != null) {
-            return partition;
-        }
+            if (partition != null) {
+                return partition;
+            }
 
-        partition = createMvPartitionStorage(partitionId);
+            partition = createMvPartitionStorage(partitionId);
 
-        partition.start();
+            partition.start();
 
-        mvPartitions.set(partitionId, partition);
+            mvPartitions.set(partitionId, partition);
 
-        return partition;
+            return partition;
+        });
     }
 
     @Override
     public @Nullable AbstractPageMemoryMvPartitionStorage getMvPartition(int 
partitionId) {
-        assert started : "Storage has not started yet";
+        return busy(() -> getMvPartitionBusy(partitionId));
+    }
 
+    private @Nullable AbstractPageMemoryMvPartitionStorage 
getMvPartitionBusy(int partitionId) {
         checkPartitionId(partitionId);
 
         return mvPartitions.get(partitionId);
@@ -193,60 +213,64 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
 
     @Override
     public CompletableFuture<Void> destroyPartition(int partitionId) {
-        assert started : "Storage has not started yet";
-
-        checkPartitionId(partitionId);
+        return busy(() -> {
+            checkPartitionId(partitionId);
 
-        CompletableFuture<Void> destroyPartitionFuture = new 
CompletableFuture<>();
+            CompletableFuture<Void> destroyPartitionFuture = new 
CompletableFuture<>();
 
-        CompletableFuture<Void> previousDestroyPartitionFuture = 
partitionIdDestroyFutureMap.putIfAbsent(
-                partitionId,
-                destroyPartitionFuture
-        );
+            CompletableFuture<Void> previousDestroyPartitionFuture = 
partitionIdDestroyFutureMap.putIfAbsent(
+                    partitionId,
+                    destroyPartitionFuture
+            );
 
-        if (previousDestroyPartitionFuture != null) {
-            return previousDestroyPartitionFuture;
-        }
-
-        MvPartitionStorage partition = mvPartitions.getAndSet(partitionId, 
null);
+            if (previousDestroyPartitionFuture != null) {
+                return previousDestroyPartitionFuture;
+            }
 
-        if (partition != null) {
-            destroyMvPartitionStorage((AbstractPageMemoryMvPartitionStorage) 
partition).whenComplete((unused, throwable) -> {
-                partitionIdDestroyFutureMap.remove(partitionId);
+            AbstractPageMemoryMvPartitionStorage partition = 
mvPartitions.getAndSet(partitionId, null);
 
-                if (throwable != null) {
-                    destroyPartitionFuture.completeExceptionally(throwable);
-                } else {
-                    destroyPartitionFuture.complete(null);
-                }
-            });
-        } else {
-            partitionIdDestroyFutureMap.remove(partitionId).complete(null);
-        }
+            if (partition != null) {
+                destroyMvPartitionStorage(partition).whenComplete((unused, 
throwable) -> {
+                    partitionIdDestroyFutureMap.remove(partitionId);
+
+                    if (throwable != null) {
+                        
destroyPartitionFuture.completeExceptionally(throwable);
+                    } else {
+                        destroyPartitionFuture.complete(null);
+                    }
+                });
+            } else {
+                partitionIdDestroyFutureMap.remove(partitionId).complete(null);
+            }
 
-        return destroyPartitionFuture;
+            return destroyPartitionFuture;
+        });
     }
 
     @Override
     public SortedIndexStorage getOrCreateSortedIndex(int partitionId, UUID 
indexId) {
-        AbstractPageMemoryMvPartitionStorage partitionStorage = 
getMvPartition(partitionId);
+        return busy(() -> {
+            AbstractPageMemoryMvPartitionStorage partitionStorage = 
getMvPartitionBusy(partitionId);
 
-        if (partitionStorage == null) {
-            throw new StorageException(String.format("Partition ID %d does not 
exist", partitionId));
-        }
+            if (partitionStorage == null) {
+                throw new StorageException(String.format("Partition ID %d does 
not exist", partitionId));
+            }
 
-        return partitionStorage.getOrCreateSortedIndex(indexId);
+            return partitionStorage.getOrCreateSortedIndex(indexId);
+        });
     }
 
     @Override
     public HashIndexStorage getOrCreateHashIndex(int partitionId, UUID 
indexId) {
-        AbstractPageMemoryMvPartitionStorage partitionStorage = 
getMvPartition(partitionId);
+        return busy(() -> {
+            AbstractPageMemoryMvPartitionStorage partitionStorage = 
getMvPartitionBusy(partitionId);
 
-        if (partitionStorage == null) {
-            throw new StorageException(String.format("Partition ID %d does not 
exist", partitionId));
-        }
+            if (partitionStorage == null) {
+                throw new StorageException(String.format("Partition ID %d does 
not exist", partitionId));
+            }
 
-        return partitionStorage.getOrCreateHashIndex(indexId);
+            return partitionStorage.getOrCreateHashIndex(indexId);
+        });
     }
 
     @Override
@@ -276,4 +300,8 @@ public abstract class AbstractPageMemoryTableStorage 
implements MvTableStorage {
             ));
         }
     }
+
+    private <V> V busy(Supplier<V> supplier) {
+        return inBusyLock(busyLock, supplier);
+    }
 }
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
index 79a5e81019..3c6a644d82 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryStorageEngine.java
@@ -189,7 +189,7 @@ public class PersistentPageMemoryStorageEngine implements 
StorageEngine {
             throws StorageException {
         PersistentPageMemoryDataStorageView dataStorageView = 
(PersistentPageMemoryDataStorageView) tableCfg.dataStorage().value();
 
-        return new PersistentPageMemoryTableStorage(this, tableCfg, 
regions.get(dataStorageView.dataRegion()), tablesCfg);
+        return new PersistentPageMemoryTableStorage(tableCfg, tablesCfg, this, 
regions.get(dataStorageView.dataRegion()));
     }
 
     /**
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryTableStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryTableStorage.java
index 2162a7bc56..e0c0fb59f0 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryTableStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/PersistentPageMemoryTableStorage.java
@@ -61,15 +61,15 @@ public class PersistentPageMemoryTableStorage extends 
AbstractPageMemoryTableSto
     /**
      * Constructor.
      *
-     * @param engine Storage engine instance.
      * @param tableCfg Table configuration.
+     * @param engine Storage engine instance.
      * @param dataRegion Data region for the table.
      */
     public PersistentPageMemoryTableStorage(
-            PersistentPageMemoryStorageEngine engine,
             TableConfiguration tableCfg,
-            PersistentPageMemoryDataRegion dataRegion,
-            TablesConfiguration tablesCfg
+            TablesConfiguration tablesCfg,
+            PersistentPageMemoryStorageEngine engine,
+            PersistentPageMemoryDataRegion dataRegion
     ) {
         super(tableCfg, tablesCfg);
 
@@ -156,7 +156,7 @@ public class PersistentPageMemoryTableStorage extends 
AbstractPageMemoryTableSto
                     indexColumnsFreeList,
                     versionChainTree,
                     indexMetaTree,
-                    tablesConfiguration
+                    tablesCfg
             );
         } catch (IgniteInternalCheckedException e) {
             throw new StorageException(
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryTableStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryTableStorage.java
index 6110f22cd4..fb6cf5f178 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryTableStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryTableStorage.java
@@ -72,7 +72,7 @@ public class VolatilePageMemoryTableStorage extends 
AbstractPageMemoryTableStora
 
         return new VolatilePageMemoryMvPartitionStorage(
                 this,
-                tablesConfiguration,
+                tablesCfg,
                 partitionId,
                 versionChainTree,
                 indexMetaTree,
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 cc2e3e2c26..fe2422f229 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
@@ -17,8 +17,8 @@
 
 package org.apache.ignite.internal.storage.pagememory.index.hash;
 
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
 import org.apache.ignite.internal.schema.BinaryTuple;
 import org.apache.ignite.internal.storage.RowId;
 import org.apache.ignite.internal.storage.StorageClosedException;
@@ -36,16 +36,6 @@ import org.apache.ignite.lang.IgniteInternalCheckedException;
  * Implementation of Hash index storage using Page Memory.
  */
 public class PageMemoryHashIndexStorage implements HashIndexStorage {
-    private static final VarHandle CLOSED;
-
-    static {
-        try {
-            CLOSED = 
MethodHandles.lookup().findVarHandle(PageMemoryHashIndexStorage.class, 
"closed", boolean.class);
-        } catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
     /** Index descriptor. */
     private final HashIndexDescriptor descriptor;
 
@@ -65,11 +55,10 @@ public class PageMemoryHashIndexStorage implements 
HashIndexStorage {
     private final RowId highestRowId;
 
     /** Busy lock for synchronous closing. */
-    private final IgniteSpinBusyLock closeBusyLock = new IgniteSpinBusyLock();
+    private final IgniteSpinBusyLock busyLock = new IgniteSpinBusyLock();
 
-    /** To avoid double closure. */
-    @SuppressWarnings("unused")
-    private volatile boolean closed;
+    /** Prevents double stopping of the component. */
+    private final AtomicBoolean stopGuard = new AtomicBoolean();
 
     /**
      * Constructor.
@@ -97,100 +86,76 @@ public class PageMemoryHashIndexStorage implements 
HashIndexStorage {
 
     @Override
     public Cursor<RowId> get(BinaryTuple key) throws StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            IndexColumns indexColumns = new IndexColumns(partitionId, 
key.byteBuffer());
-
-            HashIndexRow lowerBound = new HashIndexRow(indexColumns, 
lowestRowId);
-            HashIndexRow upperBound = new HashIndexRow(indexColumns, 
highestRowId);
+        return busy(() -> {
+            try {
+                IndexColumns indexColumns = new IndexColumns(partitionId, 
key.byteBuffer());
 
-            Cursor<HashIndexRow> cursor = hashIndexTree.find(lowerBound, 
upperBound);
+                HashIndexRow lowerBound = new HashIndexRow(indexColumns, 
lowestRowId);
+                HashIndexRow upperBound = new HashIndexRow(indexColumns, 
highestRowId);
 
-            return new Cursor<>() {
-                @Override
-                public void close() {
-                    cursor.close();
-                }
-
-                @Override
-                public boolean hasNext() {
-                    if (!closeBusyLock.enterBusy()) {
-                        throwStorageClosedException();
-                    }
+                Cursor<HashIndexRow> cursor = hashIndexTree.find(lowerBound, 
upperBound);
 
-                    try {
-                        return cursor.hasNext();
-                    } finally {
-                        closeBusyLock.leaveBusy();
+                return new Cursor<RowId>() {
+                    @Override
+                    public void close() {
+                        cursor.close();
                     }
-                }
 
-                @Override
-                public RowId next() {
-                    if (!closeBusyLock.enterBusy()) {
-                        throwStorageClosedException();
+                    @Override
+                    public boolean hasNext() {
+                        return busy(cursor::hasNext);
                     }
 
-                    try {
-                        return cursor.next().rowId();
-                    } finally {
-                        closeBusyLock.leaveBusy();
+                    @Override
+                    public RowId next() {
+                        return busy(() -> cursor.next().rowId());
                     }
-                }
-            };
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to create scan cursor", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                };
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to create scan cursor", e);
+            }
+        });
     }
 
     @Override
     public void put(IndexRow row) throws StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
+        busy(() -> {
+            try {
+                IndexColumns indexColumns = new IndexColumns(partitionId, 
row.indexColumns().byteBuffer());
 
-        try {
-            IndexColumns indexColumns = new IndexColumns(partitionId, 
row.indexColumns().byteBuffer());
+                HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, 
row.rowId());
 
-            HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, 
row.rowId());
+                var insert = new InsertHashIndexRowInvokeClosure(hashIndexRow, 
freeList, hashIndexTree.inlineSize());
 
-            var insert = new InsertHashIndexRowInvokeClosure(hashIndexRow, 
freeList, hashIndexTree.inlineSize());
+                hashIndexTree.invoke(hashIndexRow, null, insert);
 
-            hashIndexTree.invoke(hashIndexRow, null, insert);
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to put value into index", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                return null;
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to put value into index", 
e);
+            }
+        });
     }
 
     @Override
     public void remove(IndexRow row) throws StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
+        busy(() -> {
+            try {
+                IndexColumns indexColumns = new IndexColumns(partitionId, 
row.indexColumns().byteBuffer());
 
-        try {
-            IndexColumns indexColumns = new IndexColumns(partitionId, 
row.indexColumns().byteBuffer());
+                HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, 
row.rowId());
 
-            HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, 
row.rowId());
+                var remove = new RemoveHashIndexRowInvokeClosure(hashIndexRow, 
freeList);
 
-            var remove = new RemoveHashIndexRowInvokeClosure(hashIndexRow, 
freeList);
+                hashIndexTree.invoke(hashIndexRow, null, remove);
 
-            hashIndexTree.invoke(hashIndexRow, null, remove);
+                // Performs actual deletion from freeList if necessary.
+                remove.afterCompletion();
 
-            // Performs actual deletion from freeList if necessary.
-            remove.afterCompletion();
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to remove value from index", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                return null;
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to remove value from 
index", e);
+            }
+        });
     }
 
     @Override
@@ -203,19 +168,24 @@ public class PageMemoryHashIndexStorage implements 
HashIndexStorage {
      * Closes the hash index storage.
      */
     public void close() {
-        if (!CLOSED.compareAndSet(this, false, true)) {
+        if (!stopGuard.compareAndSet(false, true)) {
             return;
         }
 
-        closeBusyLock.block();
+        busyLock.block();
 
         hashIndexTree.close();
     }
 
-    /**
-     * Throws an exception that the storage is already closed.
-     */
-    private void throwStorageClosedException() {
-        throw new StorageClosedException();
+    private <V> V busy(Supplier<V> supplier) {
+        if (!busyLock.enterBusy()) {
+            throw new StorageClosedException();
+        }
+
+        try {
+            return supplier.get();
+        } finally {
+            busyLock.leaveBusy();
+        }
     }
 }
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 5c2c639e5f..46c730d8a3 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
@@ -17,11 +17,11 @@
 
 package org.apache.ignite.internal.storage.pagememory.index.sorted;
 
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
 import java.nio.ByteBuffer;
 import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
+import java.util.function.Supplier;
 import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
 import org.apache.ignite.internal.schema.BinaryTuple;
 import org.apache.ignite.internal.schema.BinaryTuplePrefix;
@@ -44,16 +44,6 @@ import org.jetbrains.annotations.Nullable;
  * Implementation of Sorted index storage using Page Memory.
  */
 public class PageMemorySortedIndexStorage implements SortedIndexStorage {
-    private static final VarHandle CLOSED;
-
-    static {
-        try {
-            CLOSED = 
MethodHandles.lookup().findVarHandle(PageMemorySortedIndexStorage.class, 
"closed", boolean.class);
-        } catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
     /** Index descriptor. */
     private final SortedIndexDescriptor descriptor;
 
@@ -73,11 +63,10 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
     private final RowId highestRowId;
 
     /** Busy lock for synchronous closing. */
-    private final IgniteSpinBusyLock closeBusyLock = new IgniteSpinBusyLock();
+    private final IgniteSpinBusyLock busyLock = new IgniteSpinBusyLock();
 
-    /** To avoid double closure. */
-    @SuppressWarnings("unused")
-    private volatile boolean closed;
+    /** Prevents double stopping of the component. */
+    private final AtomicBoolean stopGuard = new AtomicBoolean();
 
     /**
      * Constructor.
@@ -105,71 +94,59 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
 
     @Override
     public Cursor<RowId> get(BinaryTuple key) throws StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            SortedIndexRowKey lowerBound = toSortedIndexRow(key, lowestRowId);
+        return busy(() -> {
+            try {
+                SortedIndexRowKey lowerBound = toSortedIndexRow(key, 
lowestRowId);
 
-            SortedIndexRowKey upperBound = toSortedIndexRow(key, highestRowId);
+                SortedIndexRowKey upperBound = toSortedIndexRow(key, 
highestRowId);
 
-            return convertCursor(sortedIndexTree.find(lowerBound, upperBound), 
SortedIndexRow::rowId);
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to create scan cursor", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                return convertCursor(sortedIndexTree.find(lowerBound, 
upperBound), SortedIndexRow::rowId);
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to create scan cursor", e);
+            }
+        });
     }
 
     @Override
     public void put(IndexRow row) {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
+        busy(() -> {
+            try {
+                SortedIndexRow sortedIndexRow = 
toSortedIndexRow(row.indexColumns(), row.rowId());
 
-        try {
-            SortedIndexRow sortedIndexRow = 
toSortedIndexRow(row.indexColumns(), row.rowId());
+                var insert = new 
InsertSortedIndexRowInvokeClosure(sortedIndexRow, freeList, 
sortedIndexTree.inlineSize());
 
-            var insert = new InsertSortedIndexRowInvokeClosure(sortedIndexRow, 
freeList, sortedIndexTree.inlineSize());
+                sortedIndexTree.invoke(sortedIndexRow, null, insert);
 
-            sortedIndexTree.invoke(sortedIndexRow, null, insert);
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to put value into index", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                return null;
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to put value into index", 
e);
+            }
+        });
     }
 
     @Override
     public void remove(IndexRow row) {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
+        busy(() -> {
+            try {
+                SortedIndexRow sortedIndexRow = 
toSortedIndexRow(row.indexColumns(), row.rowId());
 
-        try {
-            SortedIndexRow sortedIndexRow = 
toSortedIndexRow(row.indexColumns(), row.rowId());
+                var remove = new 
RemoveSortedIndexRowInvokeClosure(sortedIndexRow, freeList);
 
-            var remove = new RemoveSortedIndexRowInvokeClosure(sortedIndexRow, 
freeList);
+                sortedIndexTree.invoke(sortedIndexRow, null, remove);
 
-            sortedIndexTree.invoke(sortedIndexRow, null, remove);
+                // Performs actual deletion from freeList if necessary.
+                remove.afterCompletion();
 
-            // Performs actual deletion from freeList if necessary.
-            remove.afterCompletion();
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Failed to remove value from index", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+                return null;
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to remove value from 
index", e);
+            }
+        });
     }
 
     @Override
     public PeekCursor<IndexRow> scan(@Nullable BinaryTuplePrefix lowerBound, 
@Nullable BinaryTuplePrefix upperBound, int flags) {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             boolean includeLower = (flags & GREATER_OR_EQUAL) != 0;
             boolean includeUpper = (flags & LESS_OR_EQUAL) != 0;
 
@@ -178,13 +155,10 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
             SortedIndexRowKey upper = createBound(upperBound, includeUpper);
 
             return new ScanCursor(lower, upper);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
-    @Nullable
-    private SortedIndexRowKey createBound(@Nullable BinaryTuplePrefix bound, 
boolean setEqualityFlag) {
+    private @Nullable SortedIndexRowKey createBound(@Nullable 
BinaryTuplePrefix bound, boolean setEqualityFlag) {
         if (bound == null) {
             return null;
         }
@@ -215,22 +189,15 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
      * Closes the sorted index storage.
      */
     public void close() {
-        if (!CLOSED.compareAndSet(this, false, true)) {
+        if (!stopGuard.compareAndSet(false, true)) {
             return;
         }
 
-        closeBusyLock.block();
+        busyLock.block();
 
         sortedIndexTree.close();
     }
 
-    /**
-     * Throws an exception that the storage is already closed.
-     */
-    private void throwStorageClosedException() {
-        throw new StorageClosedException();
-    }
-
     /**
      * Returns a new cursor that converts elements to another type, and also 
throws {@link StorageClosedException} on
      * {@link Cursor#hasNext()} and {@link Cursor#next()} when the sorted 
index storage is {@link #close()}.
@@ -247,28 +214,12 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
 
             @Override
             public boolean hasNext() {
-                if (!closeBusyLock.enterBusy()) {
-                    throwStorageClosedException();
-                }
-
-                try {
-                    return cursor.hasNext();
-                } finally {
-                    closeBusyLock.leaveBusy();
-                }
+                return busy(cursor::hasNext);
             }
 
             @Override
             public R next() {
-                if (!closeBusyLock.enterBusy()) {
-                    throwStorageClosedException();
-                }
-
-                try {
-                    return mapper.apply(cursor.next());
-                } finally {
-                    closeBusyLock.leaveBusy();
-                }
+                return busy(() -> mapper.apply(cursor.next()));
             }
         };
     }
@@ -298,73 +249,67 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
 
         @Override
         public boolean hasNext() {
-            if (!closeBusyLock.enterBusy()) {
-                throwStorageClosedException();
-            }
-
-            try {
-                advanceIfNeeded();
+            return busy(() -> {
+                try {
+                    advanceIfNeeded();
 
-                return hasNext;
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Error while advancing the cursor", 
e);
-            } finally {
-                closeBusyLock.leaveBusy();
-            }
+                    return hasNext;
+                } catch (IgniteInternalCheckedException e) {
+                    throw new StorageException("Error while advancing the 
cursor", e);
+                }
+            });
         }
 
         @Override
         public IndexRow next() {
-            if (!closeBusyLock.enterBusy()) {
-                throwStorageClosedException();
-            }
-
-            try {
-                advanceIfNeeded();
+            return busy(() -> {
+                try {
+                    advanceIfNeeded();
 
-                boolean hasNext = this.hasNext;
+                    boolean hasNext = this.hasNext;
 
-                if (!hasNext) {
-                    throw new NoSuchElementException();
-                }
+                    if (!hasNext) {
+                        throw new NoSuchElementException();
+                    }
 
-                this.hasNext = null;
+                    this.hasNext = null;
 
-                return toIndexRowImpl(treeRow);
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Error while advancing the cursor", 
e);
-            } finally {
-                closeBusyLock.leaveBusy();
-            }
+                    return toIndexRowImpl(treeRow);
+                } catch (IgniteInternalCheckedException e) {
+                    throw new StorageException("Error while advancing the 
cursor", e);
+                }
+            });
         }
 
         @Override
         public @Nullable IndexRow peek() {
-            if (hasNext != null) {
-                if (hasNext) {
-                    return toIndexRowImpl(treeRow);
+            return busy(() -> {
+                if (hasNext != null) {
+                    if (hasNext) {
+                        return toIndexRowImpl(treeRow);
+                    }
+
+                    return null;
                 }
 
-                return null;
-            }
+                try {
+                    SortedIndexRow nextTreeRow;
 
-            try {
-                SortedIndexRow nextTreeRow;
+                    if (treeRow == null) {
+                        nextTreeRow = lower == null ? 
sortedIndexTree.findFirst() : sortedIndexTree.findNext(lower, true);
+                    } else {
+                        nextTreeRow = sortedIndexTree.findNext(treeRow, false);
+                    }
 
-                if (treeRow == null) {
-                    nextTreeRow = lower == null ? sortedIndexTree.findFirst() 
: sortedIndexTree.findNext(lower, true);
-                } else {
-                    nextTreeRow = sortedIndexTree.findNext(treeRow, false);
-                }
+                    if (nextTreeRow == null || (upper != null && 
compareRows(nextTreeRow, upper) >= 0)) {
+                        return null;
+                    }
 
-                if (nextTreeRow == null || (upper != null && 
compareRows(nextTreeRow, upper) >= 0)) {
-                    return null;
+                    return toIndexRowImpl(nextTreeRow);
+                } catch (IgniteInternalCheckedException e) {
+                    throw new StorageException("Error when peeking next 
element", e);
                 }
-
-                return toIndexRowImpl(nextTreeRow);
-            } catch (IgniteInternalCheckedException e) {
-                throw new StorageException("Error when peeking next element", 
e);
-            }
+            });
         }
 
         private void advanceIfNeeded() throws IgniteInternalCheckedException {
@@ -396,4 +341,16 @@ public class PageMemorySortedIndexStorage implements 
SortedIndexStorage {
             );
         }
     }
+
+    private <V> V busy(Supplier<V> supplier) {
+        if (!busyLock.enterBusy()) {
+            throw new StorageClosedException();
+        }
+
+        try {
+            return supplier.get();
+        } finally {
+            busyLock.leaveBusy();
+        }
+    }
 }
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 7df72b562b..5496706fd4 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
@@ -20,18 +20,17 @@ package org.apache.ignite.internal.storage.pagememory.mv;
 import static 
org.apache.ignite.internal.configuration.util.ConfigurationUtil.getByInternalId;
 import static org.apache.ignite.internal.pagememory.util.PageIdUtils.NULL_LINK;
 
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.VarHandle;
 import java.nio.ByteBuffer;
-import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.NoSuchElementException;
-import java.util.Objects;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Predicate;
-import java.util.stream.Stream;
+import java.util.function.Supplier;
 import org.apache.ignite.configuration.NamedListView;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.pagememory.PageIdAllocator;
@@ -64,8 +63,8 @@ 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.CursorUtils;
 import org.apache.ignite.internal.util.IgniteSpinBusyLock;
+import org.apache.ignite.internal.util.IgniteUtils;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.jetbrains.annotations.Nullable;
 
@@ -77,16 +76,6 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
     private static final Predicate<HybridTimestamp> ALWAYS_LOAD_VALUE = 
timestamp -> true;
 
-    protected static final VarHandle STARTED;
-
-    static {
-        try {
-            STARTED = 
MethodHandles.lookup().findVarHandle(AbstractPageMemoryMvPartitionStorage.class,
 "started", boolean.class);
-        } catch (ReflectiveOperationException e) {
-            throw new ExceptionInInitializerError(e);
-        }
-    }
-
     protected final int partitionId;
 
     protected final int groupId;
@@ -101,8 +90,6 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
     protected final IndexMetaTree indexMetaTree;
 
-    private final TablesConfiguration tablesConfiguration;
-
     protected final DataPageReader rowVersionDataPageReader;
 
     protected final ConcurrentMap<UUID, PageMemoryHashIndexStorage> 
hashIndexes = new ConcurrentHashMap<>();
@@ -110,11 +97,10 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
     protected final ConcurrentMap<UUID, PageMemorySortedIndexStorage> 
sortedIndexes = new ConcurrentHashMap<>();
 
     /** Busy lock for synchronous closing. */
-    protected final IgniteSpinBusyLock closeBusyLock = new 
IgniteSpinBusyLock();
+    protected final IgniteSpinBusyLock busyLock = new IgniteSpinBusyLock();
 
-    /** To avoid double closure. */
-    @SuppressWarnings("unused")
-    private volatile boolean started;
+    /** Prevents double stopping of the component. */
+    protected final AtomicBoolean stopGuard = new AtomicBoolean();
 
     /**
      * Constructor.
@@ -144,8 +130,6 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
         this.versionChainTree = versionChainTree;
         this.indexMetaTree = indexMetaTree;
 
-        tablesConfiguration = tablesCfg;
-
         PageMemory pageMemory = tableStorage.dataRegion().pageMemory();
 
         groupId = tableStorage.configuration().value().tableId();
@@ -157,35 +141,31 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
      * Starts a partition by initializing its internal structures.
      */
     public void start() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
+        busy(() -> {
+            try (Cursor<IndexMeta> cursor = indexMetaTree.find(null, null)) {
+                NamedListView<TableIndexView> indexesCfgView = 
tableStorage.tablesConfiguration().indexes().value();
 
-        try (Cursor<IndexMeta> cursor = indexMetaTree.find(null, null)) {
-            NamedListView<TableIndexView> indexesCfgView = 
tablesConfiguration.indexes().value();
+                while (cursor.hasNext()) {
+                    IndexMeta indexMeta = cursor.next();
 
-            while (cursor.hasNext()) {
-                IndexMeta indexMeta = cursor.next();
+                    TableIndexView indexCfgView = 
getByInternalId(indexesCfgView, indexMeta.id());
 
-                TableIndexView indexCfgView = getByInternalId(indexesCfgView, 
indexMeta.id());
+                    if (indexCfgView instanceof HashIndexView) {
+                        createOrRestoreHashIndex(indexMeta);
+                    } else if (indexCfgView instanceof SortedIndexView) {
+                        createOrRestoreSortedIndex(indexMeta);
+                    } else {
+                        assert indexCfgView == null;
 
-                if (indexCfgView instanceof HashIndexView) {
-                    createOrRestoreHashIndex(indexMeta);
-                } else if (indexCfgView instanceof SortedIndexView) {
-                    createOrRestoreSortedIndex(indexMeta);
-                } else {
-                    assert indexCfgView == null;
-
-                    //TODO: IGNITE-17626 Drop the index synchronously.
+                        //TODO: IGNITE-17626 Drop the index synchronously.
+                    }
                 }
-            }
-        } catch (Exception e) {
-            throw new StorageException("Failed to process SQL indexes during 
the partition start", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
 
-        started = true;
+                return null;
+            } catch (Exception e) {
+                throw new StorageException("Failed to process SQL indexes 
during the partition start", e);
+            }
+        });
     }
 
     /**
@@ -201,15 +181,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
      * @param indexId Index UUID.
      */
     public PageMemoryHashIndexStorage getOrCreateHashIndex(UUID indexId) {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return hashIndexes.computeIfAbsent(indexId, uuid -> 
createOrRestoreHashIndex(new IndexMeta(indexId, 0L)));
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> hashIndexes.computeIfAbsent(indexId, uuid -> 
createOrRestoreHashIndex(new IndexMeta(indexId, 0L))));
     }
 
     /**
@@ -218,19 +190,11 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
      * @param indexId Index UUID.
      */
     public PageMemorySortedIndexStorage getOrCreateSortedIndex(UUID indexId) {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return sortedIndexes.computeIfAbsent(indexId, uuid -> 
createOrRestoreSortedIndex(new IndexMeta(indexId, 0L)));
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> sortedIndexes.computeIfAbsent(indexId, uuid -> 
createOrRestoreSortedIndex(new IndexMeta(indexId, 0L))));
     }
 
     private PageMemoryHashIndexStorage createOrRestoreHashIndex(IndexMeta 
indexMeta) {
-        var indexDescriptor = new HashIndexDescriptor(indexMeta.id(), 
tablesConfiguration.value());
+        var indexDescriptor = new HashIndexDescriptor(indexMeta.id(), 
tableStorage.tablesConfiguration().value());
 
         try {
             PageMemory pageMemory = tableStorage.dataRegion().pageMemory();
@@ -269,7 +233,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
     }
 
     private PageMemorySortedIndexStorage createOrRestoreSortedIndex(IndexMeta 
indexMeta) {
-        var indexDescriptor = new SortedIndexDescriptor(indexMeta.id(), 
tablesConfiguration.value());
+        var indexDescriptor = new SortedIndexDescriptor(indexMeta.id(), 
tableStorage.tablesConfiguration().value());
 
         try {
             PageMemory pageMemory = tableStorage.dataRegion().pageMemory();
@@ -309,11 +273,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
     @Override
     public ReadResult read(RowId rowId, HybridTimestamp timestamp) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             if (rowId.partitionId() != partitionId) {
                 throw new IllegalArgumentException(
                         String.format("RowId partition [%d] is not equal to 
storage partition [%d].", rowId.partitionId(), partitionId));
@@ -330,9 +290,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
             } else {
                 return findRowVersionByTimestamp(versionChain, timestamp);
             }
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     private boolean lookingForLatestVersion(HybridTimestamp timestamp) {
@@ -530,11 +488,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
             throws TxIdMismatchException, StorageException {
         assert rowId.partitionId() == partitionId : rowId;
 
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             VersionChain currentChain = findVersionChain(rowId);
 
             if (currentChain == null) {
@@ -571,20 +525,14 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
             updateVersionChain(chainReplacement);
 
             return res;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     @Override
     public @Nullable BinaryRow abortWrite(RowId rowId) throws StorageException 
{
         assert rowId.partitionId() == partitionId : rowId;
 
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             VersionChain currentVersionChain = findVersionChain(rowId);
 
             if (currentVersionChain == null || 
currentVersionChain.transactionId() == null) {
@@ -611,9 +559,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
             }
 
             return rowVersionToBinaryRow(latestVersion);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     private void removeVersionChain(VersionChain currentVersionChain) {
@@ -628,16 +574,12 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
     public void commitWrite(RowId rowId, HybridTimestamp timestamp) throws 
StorageException {
         assert rowId.partitionId() == partitionId : rowId;
 
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        busy(() -> {
             VersionChain currentVersionChain = findVersionChain(rowId);
 
             if (currentVersionChain == null || 
currentVersionChain.transactionId() == null) {
                 // Row doesn't exist or the chain doesn't contain an 
uncommitted write intent.
-                return;
+                return null;
             }
 
             long chainLink = currentVersionChain.headLink();
@@ -659,9 +601,9 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
             } catch (IgniteInternalCheckedException e) {
                 throw new StorageException("Cannot update transaction ID", e);
             }
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+
+            return null;
+        });
     }
 
     private void removeRowVersion(RowVersion currentVersion) {
@@ -684,11 +626,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
     public void addWriteCommitted(RowId rowId, @Nullable BinaryRow row, 
HybridTimestamp commitTimestamp) throws StorageException {
         assert rowId.partitionId() == partitionId : rowId;
 
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        busy(() -> {
             VersionChain currentChain = findVersionChain(rowId);
 
             if (currentChain != null && currentChain.isUncommitted()) {
@@ -703,9 +641,9 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
             VersionChain chainReplacement = 
VersionChain.createCommitted(rowId, newVersion.link(), newVersion.nextLink());
 
             updateVersionChain(chainReplacement);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+
+            return null;
+        });
     }
 
     private RowVersion insertCommittedRowVersion(@Nullable BinaryRow row, 
HybridTimestamp commitTimestamp, long nextPartitionlessLink) {
@@ -720,47 +658,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
     @Override
     public Cursor<ReadResult> scanVersions(RowId rowId) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            VersionChain versionChain = versionChainTree.findOne(new 
VersionChainKey(rowId));
-
-            if (versionChain == null) {
-                return CursorUtils.emptyCursor();
-            }
-
-            RowVersion head = readRowVersion(versionChain.headLink(), 
ALWAYS_LOAD_VALUE);
-
-            Iterator<ReadResult> iterator = Stream.iterate(
-                            head,
-                            Objects::nonNull,
-                            rowVersion -> {
-                                if (!closeBusyLock.enterBusy()) {
-                                    throwStorageClosedException();
-                                }
-
-                                try {
-                                    if (!rowVersion.hasNextLink()) {
-                                        return null;
-                                    }
-
-                                    return 
readRowVersion(rowVersion.nextLink(), ALWAYS_LOAD_VALUE);
-                                } finally {
-                                    closeBusyLock.leaveBusy();
-                                }
-                            }
-                    )
-                    .map(rowVersion -> 
rowVersionToResultNotFillingLastCommittedTs(versionChain, rowVersion))
-                    .iterator();
-
-            return Cursor.fromBareIterator(iterator);
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException(e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> new ScanVersionsCursor(rowId));
     }
 
     private static ReadResult 
rowVersionToResultNotFillingLastCommittedTs(VersionChain versionChain, 
RowVersion rowVersion) {
@@ -782,11 +680,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
     @Override
     public PartitionTimestampCursor scan(HybridTimestamp timestamp) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             Cursor<VersionChain> treeCursor;
 
             try {
@@ -800,39 +694,29 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
             } else {
                 return new TimestampCursor(treeCursor, timestamp);
             }
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     @Override
     public @Nullable RowId closestRowId(RowId lowerBound) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try (Cursor<VersionChain> cursor = versionChainTree.find(new 
VersionChainKey(lowerBound), null)) {
-            return cursor.hasNext() ? cursor.next().rowId() : null;
-        } catch (Exception e) {
-            throw new StorageException("Error occurred while trying to read a 
row id", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> {
+            try (Cursor<VersionChain> cursor = versionChainTree.find(new 
VersionChainKey(lowerBound), null)) {
+                return cursor.hasNext() ? cursor.next().rowId() : null;
+            } catch (Exception e) {
+                throw new StorageException("Error occurred while trying to 
read a row id", e);
+            }
+        });
     }
 
     @Override
     public long rowsCount() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return versionChainTree.size();
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Error occurred while fetching the 
size.", e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> {
+            try {
+                return versionChainTree.size();
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Error occurred while fetching the 
size.", e);
+            }
+        });
     }
 
     private abstract class BasePartitionTimestampCursor implements 
PartitionTimestampCursor {
@@ -850,17 +734,19 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
 
         @Override
         public final ReadResult next() {
-            if (!hasNext()) {
-                throw new NoSuchElementException("The cursor is exhausted");
-            }
+            return busy(() -> {
+                if (!hasNext()) {
+                    throw new NoSuchElementException("The cursor is 
exhausted");
+                }
 
-            assert nextRead != null;
+                assert nextRead != null;
 
-            ReadResult res = nextRead;
+                ReadResult res = nextRead;
 
-            nextRead = null;
+                nextRead = null;
 
-            return res;
+                return res;
+            });
         }
 
         @Override
@@ -870,11 +756,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
         @Override
         public @Nullable BinaryRow committed(HybridTimestamp timestamp) {
-            if (!closeBusyLock.enterBusy()) {
-                throwStorageClosedException();
-            }
-
-            try {
+            return busy(() -> {
                 if (currentChain == null) {
                     throw new IllegalStateException();
                 }
@@ -887,9 +769,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
 
                 // We don't check if row conforms the key filter here, because 
we've already checked it.
                 return result.binaryRow();
-            } finally {
-                closeBusyLock.leaveBusy();
-            }
+            });
         }
     }
 
@@ -910,22 +790,18 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
 
         @Override
         public boolean hasNext() {
-            if (nextRead != null) {
-                return true;
-            }
-
-            if (iterationExhausted) {
-                return false;
-            }
-
-            currentChain = null;
+            return busy(() -> {
+                if (nextRead != null) {
+                    return true;
+                }
 
-            while (true) {
-                if (!closeBusyLock.enterBusy()) {
-                    throwStorageClosedException();
+                if (iterationExhausted) {
+                    return false;
                 }
 
-                try {
+                currentChain = null;
+
+                while (true) {
                     if (!treeCursor.hasNext()) {
                         iterationExhausted = true;
 
@@ -943,10 +819,8 @@ public abstract class AbstractPageMemoryMvPartitionStorage 
implements MvPartitio
                     currentChain = chain;
 
                     return true;
-                } finally {
-                    closeBusyLock.leaveBusy();
                 }
-            }
+            });
         }
     }
 
@@ -964,20 +838,16 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
 
         @Override
         public boolean hasNext() {
-            if (nextRead != null) {
-                return true;
-            }
-
-            if (iterationExhausted) {
-                return false;
-            }
+            return busy(() -> {
+                if (nextRead != null) {
+                    return true;
+                }
 
-            while (true) {
-                if (!closeBusyLock.enterBusy()) {
-                    throwStorageClosedException();
+                if (iterationExhausted) {
+                    return false;
                 }
 
-                try {
+                while (true) {
                     if (!treeCursor.hasNext()) {
                         iterationExhausted = true;
                         return false;
@@ -994,17 +864,127 @@ public abstract class 
AbstractPageMemoryMvPartitionStorage implements MvPartitio
                     currentChain = chain;
 
                     return true;
-                } finally {
-                    closeBusyLock.leaveBusy();
                 }
+            });
+        }
+    }
+
+    private class ScanVersionsCursor implements Cursor<ReadResult> {
+        final RowId rowId;
+
+        @Nullable
+        private Boolean hasNext;
+
+        @Nullable
+        private VersionChain versionChain;
+
+        @Nullable
+        private RowVersion rowVersion;
+
+        private ScanVersionsCursor(RowId rowId) {
+            this.rowId = rowId;
+        }
+
+        @Override
+        public void close() {
+            // No-op.
+        }
+
+        @Override
+        public boolean hasNext() {
+            return busy(() -> {
+                advanceIfNeeded();
+
+                return hasNext;
+            });
+        }
+
+        @Override
+        public ReadResult next() {
+            return busy(() -> {
+                advanceIfNeeded();
+
+                if (!hasNext) {
+                    throw new NoSuchElementException();
+                }
+
+                hasNext = null;
+
+                return 
rowVersionToResultNotFillingLastCommittedTs(versionChain, rowVersion);
+            });
+        }
+
+        private void advanceIfNeeded() {
+            if (hasNext != null) {
+                return;
+            }
+
+            if (versionChain == null) {
+                try {
+                    versionChain = versionChainTree.findOne(new 
VersionChainKey(rowId));
+                } catch (IgniteInternalCheckedException e) {
+                    throw new StorageException(e);
+                }
+
+                rowVersion = versionChain == null ? null : 
readRowVersion(versionChain.headLink(), ALWAYS_LOAD_VALUE);
+            } else {
+                rowVersion = !rowVersion.hasNextLink() ? null : 
readRowVersion(rowVersion.nextLink(), ALWAYS_LOAD_VALUE);
             }
+
+            hasNext = rowVersion != null;
+        }
+    }
+
+    @Override
+    public void close() {
+        if (!stopGuard.compareAndSet(false, true)) {
+            return;
+        }
+
+        busyLock.block();
+
+        try {
+            IgniteUtils.closeAll(getResourcesToClose());
+        } catch (Exception e) {
+            throw new StorageException(e);
         }
     }
 
     /**
-     * Throws an exception that the storage is already closed.
+     * Returns resources that should be closed on {@link #close()}.
      */
-    protected void throwStorageClosedException() {
-        throw new StorageClosedException();
+    protected List<AutoCloseable> getResourcesToClose() {
+        List<AutoCloseable> resources = new ArrayList<>();
+
+        resources.add(versionChainTree::close);
+        resources.add(indexMetaTree::close);
+
+        hashIndexes.values().forEach(index -> resources.add(index::close));
+        sortedIndexes.values().forEach(index -> resources.add(index::close));
+
+        resources.add(hashIndexes::clear);
+        resources.add(sortedIndexes::clear);
+
+        return resources;
+    }
+
+    /**
+     * Performs a supplier using a {@link #busyLock}.
+     *
+     * @param <V> Type of the returned value.
+     * @param supplier Supplier.
+     * @return Value.
+     * @throws StorageClosedException If the storage is closed.
+     */
+    protected <V> V busy(Supplier<V> supplier) {
+        if (!busyLock.enterBusy()) {
+            throw new StorageClosedException();
+        }
+
+        try {
+            return supplier.get();
+        } finally {
+            busyLock.leaveBusy();
+        }
     }
 }
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
index 2bc5eba659..6576debc83 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/PersistentPageMemoryMvPartitionStorage.java
@@ -17,6 +17,7 @@
 
 package org.apache.ignite.internal.storage.pagememory.mv;
 
+import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
@@ -45,7 +46,6 @@ 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.util.ByteUtils;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
-import org.apache.ignite.lang.IgniteInternalException;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -132,11 +132,7 @@ public class PersistentPageMemoryMvPartitionStorage 
extends AbstractPageMemoryMv
 
     @Override
     public <V> V runConsistently(WriteClosure<V> closure) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             checkpointTimeoutLock.checkpointReadLock();
 
             try {
@@ -144,18 +140,12 @@ public class PersistentPageMemoryMvPartitionStorage 
extends AbstractPageMemoryMv
             } finally {
                 checkpointTimeoutLock.checkpointReadUnlock();
             }
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     @Override
     public CompletableFuture<Void> flush() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
+        return busy(() -> {
             CheckpointProgress lastCheckpoint = 
checkpointManager.lastCheckpointProgress();
 
             CheckpointProgress scheduledCheckpoint;
@@ -172,115 +162,93 @@ public class PersistentPageMemoryMvPartitionStorage 
extends AbstractPageMemoryMv
             }
 
             return 
scheduledCheckpoint.futureFor(CheckpointState.FINISHED).thenApply(res -> null);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     @Override
     public long lastAppliedIndex() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return meta.lastAppliedIndex();
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(meta::lastAppliedIndex);
     }
 
     @Override
     public long lastAppliedTerm() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return meta.lastAppliedTerm();
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(meta::lastAppliedTerm);
     }
 
     @Override
     public void lastApplied(long lastAppliedIndex, long lastAppliedTerm) 
throws StorageException {
-        assert checkpointTimeoutLock.checkpointLockIsHeldByThread();
+        busy(() -> {
+            assert checkpointTimeoutLock.checkpointLockIsHeldByThread();
+
+            CheckpointProgress lastCheckpoint = 
checkpointManager.lastCheckpointProgress();
 
-        CheckpointProgress lastCheckpoint = 
checkpointManager.lastCheckpointProgress();
+            UUID lastCheckpointId = lastCheckpoint == null ? null : 
lastCheckpoint.id();
 
-        UUID lastCheckpointId = lastCheckpoint == null ? null : 
lastCheckpoint.id();
+            meta.lastApplied(lastCheckpointId, lastAppliedIndex, 
lastAppliedTerm);
 
-        meta.lastApplied(lastCheckpointId, lastAppliedIndex, lastAppliedTerm);
+            return null;
+        });
     }
 
     @Override
     public long persistedIndex() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return persistedIndex;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> persistedIndex);
     }
 
     @Override
     @Nullable
     public RaftGroupConfiguration committedGroupConfiguration() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            replicationProtocolGroupConfigReadWriteLock.readLock().lock();
-
+        return busy(() -> {
             try {
-                long configFirstPageId = 
meta.lastReplicationProtocolGroupConfigFirstPageId();
+                replicationProtocolGroupConfigReadWriteLock.readLock().lock();
 
-                if (configFirstPageId == BlobStorage.NO_PAGE_ID) {
-                    return null;
-                }
+                try {
+                    long configFirstPageId = 
meta.lastReplicationProtocolGroupConfigFirstPageId();
 
-                byte[] bytes = 
blobStorage.readBlob(meta.lastReplicationProtocolGroupConfigFirstPageId());
+                    if (configFirstPageId == BlobStorage.NO_PAGE_ID) {
+                        return null;
+                    }
 
-                return replicationProtocolGroupConfigFromBytes(bytes);
-            } finally {
-                
replicationProtocolGroupConfigReadWriteLock.readLock().unlock();
+                    byte[] bytes = 
blobStorage.readBlob(meta.lastReplicationProtocolGroupConfigFirstPageId());
+
+                    return replicationProtocolGroupConfigFromBytes(bytes);
+                } finally {
+                    
replicationProtocolGroupConfigReadWriteLock.readLock().unlock();
+                }
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Failed to read group config, 
groupId=" + groupId + ", partitionId=" + partitionId, e);
             }
-        } catch (IgniteInternalCheckedException e) {
-            throw new IgniteInternalException("Failed to read group config, 
groupId=" + groupId + ", partitionId=" + partitionId, e);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        });
     }
 
     @Override
     public void committedGroupConfiguration(RaftGroupConfiguration config) {
-        assert checkpointTimeoutLock.checkpointLockIsHeldByThread();
+        busy(() -> {
+            assert checkpointTimeoutLock.checkpointLockIsHeldByThread();
 
-        CheckpointProgress lastCheckpoint = 
checkpointManager.lastCheckpointProgress();
-        UUID lastCheckpointId = lastCheckpoint == null ? null : 
lastCheckpoint.id();
+            CheckpointProgress lastCheckpoint = 
checkpointManager.lastCheckpointProgress();
+            UUID lastCheckpointId = lastCheckpoint == null ? null : 
lastCheckpoint.id();
 
-        byte[] groupConfigBytes = 
replicationProtocolGroupConfigToBytes(config);
+            byte[] groupConfigBytes = 
replicationProtocolGroupConfigToBytes(config);
 
-        replicationProtocolGroupConfigReadWriteLock.writeLock().lock();
+            replicationProtocolGroupConfigReadWriteLock.writeLock().lock();
 
-        try {
-            if (meta.lastReplicationProtocolGroupConfigFirstPageId() == 
BlobStorage.NO_PAGE_ID) {
-                long configPageId = blobStorage.addBlob(groupConfigBytes);
+            try {
+                if (meta.lastReplicationProtocolGroupConfigFirstPageId() == 
BlobStorage.NO_PAGE_ID) {
+                    long configPageId = blobStorage.addBlob(groupConfigBytes);
 
-                
meta.lastReplicationProtocolGroupConfigFirstPageId(lastCheckpointId, 
configPageId);
-            } else {
-                
blobStorage.updateBlob(meta.lastReplicationProtocolGroupConfigFirstPageId(), 
groupConfigBytes);
+                    
meta.lastReplicationProtocolGroupConfigFirstPageId(lastCheckpointId, 
configPageId);
+                } else {
+                    
blobStorage.updateBlob(meta.lastReplicationProtocolGroupConfigFirstPageId(), 
groupConfigBytes);
+                }
+            } catch (IgniteInternalCheckedException e) {
+                throw new StorageException("Cannot save committed group 
configuration, groupId=" + groupId + ", partitionId=" + groupId, e);
+            } finally {
+                
replicationProtocolGroupConfigReadWriteLock.writeLock().unlock();
             }
-        } catch (IgniteInternalCheckedException e) {
-            throw new StorageException("Cannot save committed group 
configuration, groupId=" + groupId + ", partitionId=" + groupId, e);
-        } finally {
-            replicationProtocolGroupConfigReadWriteLock.writeLock().unlock();
-        }
+
+            return null;
+        });
     }
 
     @Nullable
@@ -307,33 +275,16 @@ public class PersistentPageMemoryMvPartitionStorage 
extends AbstractPageMemoryMv
     }
 
     @Override
-    public void close() {
-        if (!STARTED.compareAndSet(this, true, false)) {
-            return;
-        }
-
-        closeBusyLock.block();
-
-        checkpointManager.removeCheckpointListener(checkpointListener);
+    protected List<AutoCloseable> getResourcesToClose() {
+        List<AutoCloseable> resourcesToClose = super.getResourcesToClose();
 
-        rowVersionFreeList.close();
-        indexFreeList.close();
+        resourcesToClose.add(() -> 
checkpointManager.removeCheckpointListener(checkpointListener));
 
-        versionChainTree.close();
-        indexMetaTree.close();
-
-        blobStorage.close();
-
-        for (PageMemoryHashIndexStorage hashIndexStorage : 
hashIndexes.values()) {
-            hashIndexStorage.close();
-        }
-
-        for (PageMemorySortedIndexStorage sortedIndexStorage : 
sortedIndexes.values()) {
-            sortedIndexStorage.close();
-        }
+        resourcesToClose.add(rowVersionFreeList::close);
+        resourcesToClose.add(indexFreeList::close);
+        resourcesToClose.add(blobStorage::close);
 
-        hashIndexes.clear();
-        sortedIndexes.clear();
+        return resourcesToClose;
     }
 
     /**
@@ -352,7 +303,7 @@ public class PersistentPageMemoryMvPartitionStorage extends 
AbstractPageMemoryMv
                 try {
                     rowVersionFreeList.saveMetadata();
                 } catch (IgniteInternalCheckedException e) {
-                    throw new IgniteInternalException("Failed to save 
RowVersionFreeList metadata", e);
+                    throw new StorageException("Failed to save 
RowVersionFreeList metadata", e);
                 }
             });
 
@@ -360,7 +311,7 @@ public class PersistentPageMemoryMvPartitionStorage extends 
AbstractPageMemoryMv
                 try {
                     indexFreeList.saveMetadata();
                 } catch (IgniteInternalCheckedException e) {
-                    throw new IgniteInternalException("Failed to save 
IndexColumnsFreeList metadata", e);
+                    throw new StorageException("Failed to save 
IndexColumnsFreeList metadata", e);
                 }
             });
         }
diff --git 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/VolatilePageMemoryMvPartitionStorage.java
 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/VolatilePageMemoryMvPartitionStorage.java
index 4eabeb9faf..0c7fc0b5bd 100644
--- 
a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/VolatilePageMemoryMvPartitionStorage.java
+++ 
b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/VolatilePageMemoryMvPartitionStorage.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.storage.pagememory.mv;
 
+import static java.util.concurrent.CompletableFuture.completedFuture;
+
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Predicate;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
@@ -28,9 +30,7 @@ import org.apache.ignite.internal.storage.MvPartitionStorage;
 import org.apache.ignite.internal.storage.RaftGroupConfiguration;
 import org.apache.ignite.internal.storage.StorageException;
 import 
org.apache.ignite.internal.storage.pagememory.VolatilePageMemoryTableStorage;
-import 
org.apache.ignite.internal.storage.pagememory.index.hash.PageMemoryHashIndexStorage;
 import org.apache.ignite.internal.storage.pagememory.index.meta.IndexMetaTree;
-import 
org.apache.ignite.internal.storage.pagememory.index.sorted.PageMemorySortedIndexStorage;
 import org.apache.ignite.lang.IgniteInternalCheckedException;
 import org.apache.ignite.lang.IgniteInternalException;
 import org.jetbrains.annotations.Nullable;
@@ -85,86 +85,42 @@ public class VolatilePageMemoryMvPartitionStorage extends 
AbstractPageMemoryMvPa
 
     @Override
     public <V> V runConsistently(WriteClosure<V> closure) throws 
StorageException {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return closure.execute();
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(closure::execute);
     }
 
     @Override
     public CompletableFuture<Void> flush() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return CompletableFuture.completedFuture(null);
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> completedFuture(null));
     }
 
     @Override
     public long lastAppliedIndex() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return lastAppliedIndex;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> lastAppliedIndex);
     }
 
     @Override
     public long lastAppliedTerm() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return lastAppliedTerm;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> lastAppliedTerm);
     }
 
     @Override
     public void lastApplied(long lastAppliedIndex, long lastAppliedTerm) 
throws StorageException {
-        this.lastAppliedIndex = lastAppliedIndex;
-        this.lastAppliedTerm = lastAppliedTerm;
+        busy(() -> {
+            this.lastAppliedIndex = lastAppliedIndex;
+            this.lastAppliedTerm = lastAppliedTerm;
+
+            return null;
+        });
     }
 
     @Override
     public long persistedIndex() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return lastAppliedIndex;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> lastAppliedIndex);
     }
 
     @Override
     public @Nullable RaftGroupConfiguration committedGroupConfiguration() {
-        if (!closeBusyLock.enterBusy()) {
-            throwStorageClosedException();
-        }
-
-        try {
-            return groupConfig;
-        } finally {
-            closeBusyLock.leaveBusy();
-        }
+        return busy(() -> groupConfig);
     }
 
     @Override
@@ -172,29 +128,6 @@ public class VolatilePageMemoryMvPartitionStorage extends 
AbstractPageMemoryMvPa
         this.groupConfig = config;
     }
 
-    @Override
-    public void close() {
-        if (!STARTED.compareAndSet(this, true, false)) {
-            return;
-        }
-
-        closeBusyLock.block();
-
-        versionChainTree.close();
-        indexMetaTree.close();
-
-        for (PageMemoryHashIndexStorage hashIndexStorage : 
hashIndexes.values()) {
-            hashIndexStorage.close();
-        }
-
-        for (PageMemorySortedIndexStorage sortedIndexStorage : 
sortedIndexes.values()) {
-            sortedIndexStorage.close();
-        }
-
-        hashIndexes.clear();
-        sortedIndexes.clear();
-    }
-
     /**
      * Destroys internal structures backing this partition.
      *

Reply via email to