You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by tk...@apache.org on 2023/01/24 10:42:11 UTC
[ignite-3] branch main updated: IGNITE-18531 Destroy indices when destroying VolatilePageMemoryMvPartitionStorage (#1559)
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 e3cfedb1d1 IGNITE-18531 Destroy indices when destroying VolatilePageMemoryMvPartitionStorage (#1559)
e3cfedb1d1 is described below
commit e3cfedb1d1173ebed0d169f1cafc306f6dae7455
Author: Roman Puchkovskiy <ro...@gmail.com>
AuthorDate: Tue Jan 24 14:42:06 2023 +0400
IGNITE-18531 Destroy indices when destroying VolatilePageMemoryMvPartitionStorage (#1559)
---
.../ignite/internal/pagememory/tree/BplusTree.java | 4 +-
.../internal/pagememory/tree/io/BplusIo.java | 5 +-
.../storage/AbstractMvTableStorageTest.java | 4 +-
.../pagememory/VolatilePageMemoryTableStorage.java | 20 ++--
.../index/hash/PageMemoryHashIndexStorage.java | 44 +++++++-
.../index/hash/io/HashIndexTreeLeafIo.java | 21 ++++
.../index/sorted/PageMemorySortedIndexStorage.java | 38 +++++++
.../index/sorted/io/SortedIndexTreeLeafIo.java | 21 ++++
.../mv/AbstractPageMemoryMvPartitionStorage.java | 30 ++++--
.../mv/PersistentPageMemoryMvPartitionStorage.java | 7 +-
.../mv/VolatilePageMemoryMvPartitionStorage.java | 75 +++++++++++--
.../pagememory/mv/io/VersionChainLeafIo.java | 2 +-
.../VolatilePageMemoryMvTableStorageTest.java | 117 ++++++++++++++++++++-
13 files changed, 351 insertions(+), 37 deletions(-)
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 b0e1172866..f92854d7ab 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
@@ -2828,7 +2828,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
}
if (c != null && io.isLeaf()) {
- io.visit(pageAddr, c);
+ io.visit(this, pageAddr, c);
}
bag.addFreePage(recyclePage(pageId, pageAddr));
@@ -6734,7 +6734,7 @@ public abstract class BplusTree<L, T extends L> extends DataStructure implements
readChildrenPageIdsAndDescend(pageAddr, io, cnt);
} else {
if (actOnEachElement != null) {
- io.visit(pageAddr, actOnEachElement);
+ io.visit(BplusTree.this, pageAddr, actOnEachElement);
workDone += io.getCount(pageAddr);
}
diff --git a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusIo.java b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusIo.java
index faccda01b4..d465616c91 100644
--- a/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusIo.java
+++ b/modules/page-memory/src/main/java/org/apache/ignite/internal/pagememory/tree/io/BplusIo.java
@@ -519,10 +519,11 @@ public abstract class BplusIo<L> extends PageIo {
/**
* Visits the page.
*
+ * @param tree The tree to which the page belongs.
* @param pageAddr Page address.
- * @param c Consumer.
+ * @param c Consumer triggered for each element stored in the page.
*/
- public void visit(long pageAddr, Consumer<L> c) {
+ public void visit(BplusTree<L, ?> tree, long pageAddr, Consumer<L> c) {
// No-op.
}
diff --git a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvTableStorageTest.java b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvTableStorageTest.java
index 8715cfe9b6..3a70adbfef 100644
--- a/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvTableStorageTest.java
+++ b/modules/storage-api/src/testFixtures/java/org/apache/ignite/internal/storage/AbstractMvTableStorageTest.java
@@ -97,9 +97,9 @@ public abstract class AbstractMvTableStorageTest extends BaseMvStoragesTest {
protected MvTableStorage tableStorage;
- private TableIndexView sortedIdx;
+ protected TableIndexView sortedIdx;
- private TableIndexView hashIdx;
+ protected TableIndexView hashIdx;
protected StorageEngine storageEngine;
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 12d48656c9..66aeb50a45 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
@@ -149,20 +149,22 @@ public class VolatilePageMemoryTableStorage extends AbstractPageMemoryTableStora
CompletableFuture<Void> clearStorageAndUpdateDataStructures(AbstractPageMemoryMvPartitionStorage mvPartitionStorage) {
VolatilePageMemoryMvPartitionStorage volatilePartitionStorage = ((VolatilePageMemoryMvPartitionStorage) mvPartitionStorage);
- return volatilePartitionStorage.destroyStructures().thenAccept(unused -> {
- int partitionId = mvPartitionStorage.partitionId();
- TableView tableView = tableCfg.value();
+ volatilePartitionStorage.cleanStructuresData();
- volatilePartitionStorage.updateDataStructuresOnRebalance(
- createVersionChainTree(partitionId, tableView),
- createIndexMetaTree(partitionId, tableView)
- );
- });
+ int partitionId = mvPartitionStorage.partitionId();
+ TableView tableView = tableCfg.value();
+
+ volatilePartitionStorage.updateDataStructuresOnRebalance(
+ createVersionChainTree(partitionId, tableView),
+ createIndexMetaTree(partitionId, tableView)
+ );
+
+ return completedFuture(null);
}
@Override
CompletableFuture<Void> destroyMvPartitionStorage(AbstractPageMemoryMvPartitionStorage mvPartitionStorage) {
- mvPartitionStorage.close();
+ mvPartitionStorage.closeForDestruction();
VolatilePageMemoryMvPartitionStorage volatilePartitionStorage = (VolatilePageMemoryMvPartitionStorage) mvPartitionStorage;
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 8641c96d17..21fd68173f 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
@@ -24,6 +24,10 @@ import static org.apache.ignite.internal.storage.util.StorageUtils.throwExceptio
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.pagememory.util.GradualTaskExecutor;
+import org.apache.ignite.internal.pagememory.util.PageIdUtils;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.storage.RowId;
import org.apache.ignite.internal.storage.StorageException;
@@ -43,6 +47,8 @@ import org.apache.ignite.lang.IgniteStringFormatter;
* Implementation of Hash index storage using Page Memory.
*/
public class PageMemoryHashIndexStorage implements HashIndexStorage {
+ private static final IgniteLogger LOG = Loggers.forClass(PageMemoryHashIndexStorage.class);
+
/** Index descriptor. */
private final HashIndexDescriptor descriptor;
@@ -74,7 +80,11 @@ public class PageMemoryHashIndexStorage implements HashIndexStorage {
* @param freeList Free list to store index columns.
* @param hashIndexTree Hash index tree instance.
*/
- public PageMemoryHashIndexStorage(HashIndexDescriptor descriptor, IndexColumnsFreeList freeList, HashIndexTree hashIndexTree) {
+ public PageMemoryHashIndexStorage(
+ HashIndexDescriptor descriptor,
+ IndexColumnsFreeList freeList,
+ HashIndexTree hashIndexTree
+ ) {
this.descriptor = descriptor;
this.freeList = freeList;
this.hashIndexTree = hashIndexTree;
@@ -183,6 +193,38 @@ public class PageMemoryHashIndexStorage implements HashIndexStorage {
throw new UnsupportedOperationException();
}
+ /**
+ * Starts destruction of the data stored by this index partition.
+ *
+ * @param executor {@link GradualTaskExecutor} on which to destroy.
+ * @throws StorageException If something goes wrong.
+ */
+ public void startDestructionOn(GradualTaskExecutor executor) throws StorageException {
+ try {
+ executor.execute(
+ hashIndexTree.startGradualDestruction(rowKey -> removeIndexColumns((HashIndexRow) rowKey), false)
+ ).whenComplete((res, ex) -> {
+ if (ex != null) {
+ LOG.error("Hash index " + descriptor.id() + " destruction has failed", ex);
+ }
+ });
+ } catch (IgniteInternalCheckedException e) {
+ throw new StorageException("Cannot destroy hash index " + indexDescriptor().id(), e);
+ }
+ }
+
+ private void removeIndexColumns(HashIndexRow indexRow) {
+ if (indexRow.indexColumns().link() != PageIdUtils.NULL_LINK) {
+ try {
+ freeList.removeDataRowByLink(indexRow.indexColumns().link());
+ } catch (IgniteInternalCheckedException e) {
+ throw new StorageException("Cannot destroy hash index " + indexDescriptor().id(), e);
+ }
+
+ indexRow.indexColumns().link(PageIdUtils.NULL_LINK);
+ }
+ }
+
/**
* Closes the hash index storage.
*/
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/io/HashIndexTreeLeafIo.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/io/HashIndexTreeLeafIo.java
index 8207446a9e..6896ae1177 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/io/HashIndexTreeLeafIo.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/hash/io/HashIndexTreeLeafIo.java
@@ -22,6 +22,7 @@ import static org.apache.ignite.internal.storage.pagememory.index.IndexPageTypes
import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.MAX_BINARY_TUPLE_INLINE_SIZE;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.IntStream;
import org.apache.ignite.internal.pagememory.io.IoVersions;
import org.apache.ignite.internal.pagememory.tree.BplusTree;
@@ -29,9 +30,11 @@ import org.apache.ignite.internal.pagememory.tree.io.BplusIo;
import org.apache.ignite.internal.pagememory.tree.io.BplusLeafIo;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.storage.pagememory.index.InlineUtils;
+import org.apache.ignite.internal.storage.pagememory.index.hash.HashIndexRow;
import org.apache.ignite.internal.storage.pagememory.index.hash.HashIndexRowKey;
import org.apache.ignite.internal.storage.pagememory.index.hash.HashIndexTree;
import org.apache.ignite.lang.IgniteInternalCheckedException;
+import org.apache.ignite.lang.IgniteInternalException;
/**
* {@link BplusLeafIo} implementation for {@link HashIndexTree}.
@@ -68,4 +71,22 @@ public class HashIndexTreeLeafIo extends BplusLeafIo<HashIndexRowKey> implements
return getRow(hashIndexTree.dataPageReader(), hashIndexTree.partitionId(), pageAddr, idx);
}
+
+ @Override
+ public void visit(BplusTree<HashIndexRowKey, ?> tree, long pageAddr, Consumer<HashIndexRowKey> c) {
+ HashIndexTree hashIndexTree = (HashIndexTree) tree;
+
+ int count = getCount(pageAddr);
+
+ for (int i = 0; i < count; i++) {
+ HashIndexRow indexRow;
+ try {
+ indexRow = getRow(hashIndexTree.dataPageReader(), hashIndexTree.partitionId(), pageAddr, i);
+ } catch (IgniteInternalCheckedException e) {
+ throw new IgniteInternalException(e);
+ }
+
+ c.accept(indexRow);
+ }
+ }
}
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 45b8b98dcf..79cde00a2e 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
@@ -28,6 +28,10 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
import org.apache.ignite.internal.binarytuple.BinaryTupleCommon;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.pagememory.util.GradualTaskExecutor;
+import org.apache.ignite.internal.pagememory.util.PageIdUtils;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.schema.BinaryTuplePrefix;
import org.apache.ignite.internal.storage.RowId;
@@ -52,6 +56,8 @@ import org.jetbrains.annotations.Nullable;
* Implementation of Sorted index storage using Page Memory.
*/
public class PageMemorySortedIndexStorage implements SortedIndexStorage {
+ private static final IgniteLogger LOG = Loggers.forClass(PageMemorySortedIndexStorage.class);
+
/** Index descriptor. */
private final SortedIndexDescriptor descriptor;
@@ -432,4 +438,36 @@ public class PageMemorySortedIndexStorage implements SortedIndexStorage {
private String createStorageInfo() {
return IgniteStringFormatter.format("indexId={}, partitionId={}", descriptor.id(), partitionId);
}
+
+ /**
+ * Starts destruction of the data stored by this index partition.
+ *
+ * @param executor {@link GradualTaskExecutor} on which to destroy.
+ * @throws StorageException If something goes wrong.
+ */
+ public void startDestructionOn(GradualTaskExecutor executor) throws StorageException {
+ try {
+ executor.execute(
+ sortedIndexTree.startGradualDestruction(rowKey -> removeIndexColumns((SortedIndexRow) rowKey), false)
+ ).whenComplete((res, ex) -> {
+ if (ex != null) {
+ LOG.error("Sorted index " + descriptor.id() + " destruction has failed", ex);
+ }
+ });
+ } catch (IgniteInternalCheckedException e) {
+ throw new StorageException("Cannot destroy sorted index " + indexDescriptor().id(), e);
+ }
+ }
+
+ private void removeIndexColumns(SortedIndexRow indexRow) {
+ if (indexRow.indexColumns().link() != PageIdUtils.NULL_LINK) {
+ try {
+ freeList.removeDataRowByLink(indexRow.indexColumns().link());
+ } catch (IgniteInternalCheckedException e) {
+ throw new StorageException("Cannot destroy sorted index " + indexDescriptor().id(), e);
+ }
+
+ indexRow.indexColumns().link(PageIdUtils.NULL_LINK);
+ }
+ }
}
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/io/SortedIndexTreeLeafIo.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/io/SortedIndexTreeLeafIo.java
index 31bb799003..d351ad4fdc 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/io/SortedIndexTreeLeafIo.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/index/sorted/io/SortedIndexTreeLeafIo.java
@@ -22,6 +22,7 @@ import static org.apache.ignite.internal.storage.pagememory.index.IndexPageTypes
import static org.apache.ignite.internal.storage.pagememory.index.InlineUtils.MAX_BINARY_TUPLE_INLINE_SIZE;
import java.util.List;
+import java.util.function.Consumer;
import java.util.stream.IntStream;
import org.apache.ignite.internal.pagememory.io.IoVersions;
import org.apache.ignite.internal.pagememory.tree.BplusTree;
@@ -29,9 +30,11 @@ import org.apache.ignite.internal.pagememory.tree.io.BplusIo;
import org.apache.ignite.internal.pagememory.tree.io.BplusLeafIo;
import org.apache.ignite.internal.schema.BinaryTuple;
import org.apache.ignite.internal.storage.pagememory.index.InlineUtils;
+import org.apache.ignite.internal.storage.pagememory.index.sorted.SortedIndexRow;
import org.apache.ignite.internal.storage.pagememory.index.sorted.SortedIndexRowKey;
import org.apache.ignite.internal.storage.pagememory.index.sorted.SortedIndexTree;
import org.apache.ignite.lang.IgniteInternalCheckedException;
+import org.apache.ignite.lang.IgniteInternalException;
/**
* {@link BplusLeafIo} implementation for {@link SortedIndexTree}.
@@ -69,4 +72,22 @@ public class SortedIndexTreeLeafIo extends BplusLeafIo<SortedIndexRowKey> implem
return getRow(sortedIndexTree.dataPageReader(), sortedIndexTree.partitionId(), pageAddr, idx);
}
+
+ @Override
+ public void visit(BplusTree<SortedIndexRowKey, ?> tree, long pageAddr, Consumer<SortedIndexRowKey> c) {
+ SortedIndexTree sortedIndexTree = (SortedIndexTree) tree;
+
+ int count = getCount(pageAddr);
+
+ for (int i = 0; i < count; i++) {
+ SortedIndexRow indexRow;
+ try {
+ indexRow = getRow(sortedIndexTree.dataPageReader(), sortedIndexTree.partitionId(), pageAddr, i);
+ } catch (IgniteInternalCheckedException e) {
+ throw new IgniteInternalException(e);
+ }
+
+ c.accept(indexRow);
+ }
+ }
}
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 1af00faf88..323d368648 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
@@ -149,9 +149,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
try (Cursor<IndexMeta> cursor = indexMetaTree.find(null, null)) {
NamedListView<TableIndexView> indexesCfgView = tableStorage.tablesConfiguration().indexes().value();
- while (cursor.hasNext()) {
- IndexMeta indexMeta = cursor.next();
-
+ for (IndexMeta indexMeta : cursor) {
TableIndexView indexCfgView = getByInternalId(indexesCfgView, indexMeta.id());
if (indexCfgView instanceof HashIndexView) {
@@ -987,8 +985,24 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
}
}
+ /**
+ * Closes the partition in preparation for its destruction.
+ */
+ public void closeForDestruction() {
+ close(true);
+ }
+
@Override
public void close() {
+ close(false);
+ }
+
+ /**
+ * Closes the storage.
+ *
+ * @param goingToDestroy If the closure is in preparation for destruction.
+ */
+ private void close(boolean goingToDestroy) {
if (!state.compareAndSet(StorageState.RUNNABLE, StorageState.CLOSED)) {
StorageState state = this.state.get();
@@ -1000,7 +1014,7 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
busyLock.block();
try {
- IgniteUtils.closeAll(getResourcesToClose());
+ IgniteUtils.closeAll(getResourcesToClose(goingToDestroy));
} catch (Exception e) {
throw new StorageException(e);
}
@@ -1008,8 +1022,10 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
/**
* Returns resources that should be closed on {@link #close()}.
+ *
+ * @param goingToDestroy If the closure is in preparation for destruction.
*/
- protected List<AutoCloseable> getResourcesToClose() {
+ protected List<AutoCloseable> getResourcesToClose(boolean goingToDestroy) {
List<AutoCloseable> resources = new ArrayList<>();
resources.add(versionChainTree::close);
@@ -1018,8 +1034,8 @@ public abstract class AbstractPageMemoryMvPartitionStorage implements MvPartitio
hashIndexes.values().forEach(index -> resources.add(index::close));
sortedIndexes.values().forEach(index -> resources.add(index::close));
- resources.add(hashIndexes::clear);
- resources.add(sortedIndexes::clear);
+ // We do not clear hashIndexes and sortedIndexes here because we leave the decision about when to clear them
+ // to the subclasses.
return resources;
}
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 0fd7a18b44..2c3c54d9a5 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
@@ -290,8 +290,11 @@ public class PersistentPageMemoryMvPartitionStorage extends AbstractPageMemoryMv
}
@Override
- protected List<AutoCloseable> getResourcesToClose() {
- List<AutoCloseable> resourcesToClose = super.getResourcesToClose();
+ protected List<AutoCloseable> getResourcesToClose(boolean goingToDestroy) {
+ List<AutoCloseable> resourcesToClose = super.getResourcesToClose(goingToDestroy);
+
+ resourcesToClose.add(hashIndexes::clear);
+ resourcesToClose.add(sortedIndexes::clear);
resourcesToClose.add(() -> checkpointManager.removeCheckpointListener(checkpointListener));
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 88f9a06182..362a3c3529 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
@@ -25,6 +25,8 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import org.apache.ignite.internal.hlc.HybridTimestamp;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.pagememory.tree.BplusTree;
import org.apache.ignite.internal.pagememory.util.GradualTaskExecutor;
import org.apache.ignite.internal.pagememory.util.PageIdUtils;
@@ -45,6 +47,8 @@ import org.jetbrains.annotations.Nullable;
* Implementation of {@link MvPartitionStorage} based on a {@link BplusTree} for in-memory case.
*/
public class VolatilePageMemoryMvPartitionStorage extends AbstractPageMemoryMvPartitionStorage {
+ private static final IgniteLogger LOG = Loggers.forClass(VolatilePageMemoryMvPartitionStorage.class);
+
private static final Predicate<HybridTimestamp> NEVER_LOAD_VALUE = ts -> false;
private final GradualTaskExecutor destructionExecutor;
@@ -148,18 +152,63 @@ public class VolatilePageMemoryMvPartitionStorage extends AbstractPageMemoryMvPa
this.lastAppliedTerm = lastAppliedTerm;
}
+ @Override
+ protected List<AutoCloseable> getResourcesToClose(boolean goingToDestroy) {
+ List<AutoCloseable> resourcesToClose = super.getResourcesToClose(goingToDestroy);
+
+ if (!goingToDestroy) {
+ // If we are going to destroy after closure, we should retain indices because the destruction logic
+ // will need to destroy them as well. It will clean the maps after it starts the destruction.
+
+ resourcesToClose.add(hashIndexes::clear);
+ resourcesToClose.add(sortedIndexes::clear);
+ }
+
+ return resourcesToClose;
+ }
+
+ /**
+ * Cleans data backing this partition. Indices are destroyed, but index desscriptors are
+ * not removed from this partition so that they can be refilled with data later.
+ */
+ public void cleanStructuresData() {
+ destroyStructures(false);
+ }
+
+ /**
+ * Destroys internal structures (including indices) backing this partition.
+ */
+ public void destroyStructures() {
+ destroyStructures(true);
+ }
+
/**
- * Destroys internal structures backing this partition.
+ * Destroys internal structures (including indices) backing this partition.
*
- * @return future that completes when the destruction completes.
+ * @param removeIndexDescriptors Whether indices should be completely removed, not just their contents destroyed.
*/
- public CompletableFuture<Void> destroyStructures() {
- // TODO: IGNITE-18531 - destroy indices.
+ private void destroyStructures(boolean removeIndexDescriptors) {
+ startMvDataDestruction();
+ startIndexMetaTreeDestruction();
+
+ hashIndexes.values().forEach(indexStorage -> indexStorage.startDestructionOn(destructionExecutor));
+ sortedIndexes.values().forEach(indexStorage -> indexStorage.startDestructionOn(destructionExecutor));
+
+ if (removeIndexDescriptors) {
+ hashIndexes.clear();
+ sortedIndexes.clear();
+ }
+ }
+ private void startMvDataDestruction() {
try {
- return destructionExecutor.execute(
+ destructionExecutor.execute(
versionChainTree.startGradualDestruction(chainKey -> destroyVersionChain((VersionChain) chainKey), false)
- );
+ ).whenComplete((res, ex) -> {
+ if (ex != null) {
+ LOG.error("Version chains destruction failed in group={}, partition={}", ex, groupId, partitionId);
+ }
+ });
} catch (IgniteInternalCheckedException e) {
throw new StorageException("Cannot destroy MV partition in group=" + groupId + ", partition=" + partitionId, e);
}
@@ -185,6 +234,20 @@ public class VolatilePageMemoryMvPartitionStorage extends AbstractPageMemoryMvPa
}
}
+ private void startIndexMetaTreeDestruction() {
+ try {
+ destructionExecutor.execute(
+ indexMetaTree.startGradualDestruction(null, false)
+ ).whenComplete((res, ex) -> {
+ if (ex != null) {
+ LOG.error("Index meta tree destruction failed in group={}, partition={}", ex, groupId, partitionId);
+ }
+ });
+ } catch (IgniteInternalCheckedException e) {
+ throw new StorageException("Cannot destroy index meta tree in group=" + groupId + ", partition=" + partitionId, e);
+ }
+ }
+
@Override
List<AutoCloseable> getResourcesToCloseOnRebalance() {
return List.of(versionChainTree::close, indexMetaTree::close);
diff --git a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/io/VersionChainLeafIo.java b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/io/VersionChainLeafIo.java
index b0a98ddd3a..d21972b67a 100644
--- a/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/io/VersionChainLeafIo.java
+++ b/modules/storage-page-memory/src/main/java/org/apache/ignite/internal/storage/pagememory/mv/io/VersionChainLeafIo.java
@@ -67,7 +67,7 @@ public final class VersionChainLeafIo extends BplusLeafIo<VersionChainKey> imple
}
@Override
- public void visit(long pageAddr, Consumer<VersionChainKey> c) {
+ public void visit(BplusTree<VersionChainKey, ?> tree, long pageAddr, Consumer<VersionChainKey> c) {
int partitionId = getPartitionId(pageAddr);
int count = getCount(pageAddr);
diff --git a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryMvTableStorageTest.java b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryMvTableStorageTest.java
index b701c8963d..f9b75766d2 100644
--- a/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryMvTableStorageTest.java
+++ b/modules/storage-page-memory/src/test/java/org/apache/ignite/internal/storage/pagememory/VolatilePageMemoryMvTableStorageTest.java
@@ -23,19 +23,28 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import java.nio.ByteBuffer;
+import org.apache.ignite.internal.binarytuple.BinaryTupleBuilder;
import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
import org.apache.ignite.internal.pagememory.evict.PageEvictionTracker;
import org.apache.ignite.internal.pagememory.evict.PageEvictionTrackerNoOp;
import org.apache.ignite.internal.pagememory.io.PageIoRegistry;
+import org.apache.ignite.internal.schema.BinaryTuple;
+import org.apache.ignite.internal.schema.BinaryTupleSchema;
+import org.apache.ignite.internal.schema.BinaryTupleSchema.Element;
+import org.apache.ignite.internal.schema.NativeTypes;
import org.apache.ignite.internal.schema.TableRow;
import org.apache.ignite.internal.schema.configuration.TablesConfiguration;
import org.apache.ignite.internal.storage.AbstractMvTableStorageTest;
import org.apache.ignite.internal.storage.MvPartitionStorage;
import org.apache.ignite.internal.storage.RowId;
+import org.apache.ignite.internal.storage.index.HashIndexStorage;
+import org.apache.ignite.internal.storage.index.IndexRowImpl;
+import org.apache.ignite.internal.storage.index.SortedIndexStorage;
import org.apache.ignite.internal.storage.pagememory.configuration.schema.VolatilePageMemoryStorageEngineConfiguration;
import org.apache.ignite.lang.IgniteInternalCheckedException;
import org.junit.jupiter.api.BeforeEach;
@@ -75,16 +84,19 @@ public class VolatilePageMemoryMvTableStorageTest extends AbstractMvTableStorage
assertThat(tableStorage.destroyPartition(0), willSucceedFast());
- assertDestructionCompletes(emptyDataPagesBeforeDestroy);
+ assertMvDataDestructionCompletes(emptyDataPagesBeforeDestroy);
}
- private void assertDestructionCompletes(long emptyDataPagesBeforeDestroy) throws InterruptedException, IgniteInternalCheckedException {
+ private void assertMvDataDestructionCompletes(long emptyDataPagesBeforeDestroy)
+ throws InterruptedException, IgniteInternalCheckedException {
assertTrue(waitForCondition(
() -> dataRegion().rowVersionFreeList().emptyDataPages() > emptyDataPagesBeforeDestroy,
5_000
));
- verify(pageEvictionTracker, times(1)).forgetPage(anyLong());
+ // Make sure that some page storing row versions gets emptied (so we can be sure that row versions
+ // get removed).
+ verify(pageEvictionTracker, timeout(5_000)).forgetPage(anyLong());
}
private void insertOneRow(MvPartitionStorage partitionStorage) {
@@ -107,7 +119,102 @@ public class VolatilePageMemoryMvTableStorageTest extends AbstractMvTableStorage
assertThat(tableStorage.destroy(), willSucceedFast());
- assertDestructionCompletes(emptyDataPagesBeforeDestroy);
+ assertMvDataDestructionCompletes(emptyDataPagesBeforeDestroy);
+ }
+
+ @Test
+ void partitionDestructionFreesHashIndexPages() throws Exception {
+ tableStorage.getOrCreateMvPartition(0);
+
+ HashIndexStorage indexStorage = tableStorage.getOrCreateHashIndex(0, hashIdx.id());
+
+ indexStorage.put(nonInlinableIndexRow());
+
+ // Using RowVersionFreeList to track removal because RowVersionFreeList is used as a ReuseList for IndexColumnsFreeList.
+ long emptyIndexPagesBeforeDestroy = dataRegion().rowVersionFreeList().emptyDataPages();
+
+ assertThat(tableStorage.destroyPartition(0), willSucceedFast());
+
+ assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
+ }
+
+ private static IndexRowImpl nonInlinableIndexRow() {
+ RowId rowId = new RowId(PARTITION_ID);
+
+ BinaryTupleSchema schema = BinaryTupleSchema.create(new Element[]{
+ new Element(NativeTypes.INT32, false),
+ new Element(NativeTypes.stringOf(300), false)
+ });
+
+ ByteBuffer buffer = new BinaryTupleBuilder(schema.elementCount(), schema.hasNullableElements())
+ .appendInt(1)
+ .appendString("a".repeat(300))
+ .build();
+
+ BinaryTuple tuple = new BinaryTuple(schema, buffer);
+
+ return new IndexRowImpl(tuple, rowId);
+ }
+
+ private void assertIndexDataDestructionCompletes(long emptyIndexPagesBeforeDestroy)
+ throws InterruptedException, IgniteInternalCheckedException {
+ // Using RowVersionFreeList to track removal because RowVersionFreeList is used as a ReuseList for IndexColumnsFreeList.
+ assertTrue(waitForCondition(
+ () -> dataRegion().rowVersionFreeList().emptyDataPages() > emptyIndexPagesBeforeDestroy,
+ 5_000
+ ));
+
+ // Make sure that some page storing index columns gets emptied (so we can be sure that index columns
+ // get removed).
+ verify(pageEvictionTracker, timeout(5_000)).forgetPage(anyLong());
+ }
+
+ @Test
+ void partitionDestructionFreesSortedIndexPages() throws Exception {
+ tableStorage.getOrCreateMvPartition(0);
+
+ SortedIndexStorage indexStorage = tableStorage.getOrCreateSortedIndex(0, sortedIdx.id());
+
+ indexStorage.put(nonInlinableIndexRow());
+
+ // Using RowVersionFreeList to track removal because RowVersionFreeList is used as a ReuseList for IndexColumnsFreeList.
+ long emptyIndexPagesBeforeDestroy = dataRegion().rowVersionFreeList().emptyDataPages();
+
+ assertThat(tableStorage.destroyPartition(0), willSucceedFast());
+
+ assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
+ }
+
+ @Test
+ void tableStorageDestructionFreesHashIndexPages() throws Exception {
+ tableStorage.getOrCreateMvPartition(0);
+
+ HashIndexStorage indexStorage = tableStorage.getOrCreateHashIndex(0, hashIdx.id());
+
+ indexStorage.put(nonInlinableIndexRow());
+
+ // Using RowVersionFreeList to track removal because RowVersionFreeList is used as a ReuseList for IndexColumnsFreeList.
+ long emptyIndexPagesBeforeDestroy = dataRegion().rowVersionFreeList().emptyDataPages();
+
+ assertThat(tableStorage.destroy(), willSucceedFast());
+
+ assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
+ }
+
+ @Test
+ void tableStorageDestructionFreesSortedIndexPages() throws Exception {
+ tableStorage.getOrCreateMvPartition(0);
+
+ SortedIndexStorage indexStorage = tableStorage.getOrCreateSortedIndex(0, sortedIdx.id());
+
+ indexStorage.put(nonInlinableIndexRow());
+
+ // Using RowVersionFreeList to track removal because RowVersionFreeList is used as a ReuseList for IndexColumnsFreeList.
+ long emptyIndexPagesBeforeDestroy = dataRegion().rowVersionFreeList().emptyDataPages();
+
+ assertThat(tableStorage.destroy(), willSucceedFast());
+
+ assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
}
private VolatilePageMemoryDataRegion dataRegion() {