blob: 60438422cd8b95d67c3c04a031f38743cc35ea9f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.storage.pagememory;
import static org.apache.ignite.internal.catalog.CatalogService.DEFAULT_STORAGE_PROFILE;
import static org.apache.ignite.internal.catalog.commands.CatalogUtils.DEFAULT_PARTITION_COUNT;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.waitForCondition;
import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willSucceedFast;
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.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.InjectConfiguration;
import org.apache.ignite.internal.lang.IgniteInternalCheckedException;
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.BinaryRow;
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.storage.AbstractMvTableStorageTest;
import org.apache.ignite.internal.storage.MvPartitionStorage;
import org.apache.ignite.internal.storage.RowId;
import org.apache.ignite.internal.storage.configurations.StorageConfiguration;
import org.apache.ignite.internal.storage.engine.MvTableStorage;
import org.apache.ignite.internal.storage.engine.StorageTableDescriptor;
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.internal.type.NativeTypes;
import org.apache.ignite.internal.util.IgniteUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Tests for {@link VolatilePageMemoryTableStorage}.
*/
public class VolatilePageMemoryMvTableStorageTest extends AbstractMvTableStorageTest {
private final PageEvictionTracker pageEvictionTracker = spy(PageEvictionTrackerNoOp.INSTANCE);
private VolatilePageMemoryStorageEngine engine;
@BeforeEach
void setUp(
@InjectConfiguration VolatilePageMemoryStorageEngineConfiguration engineConfig,
@InjectConfiguration("mock.profiles.default = {engine = \"aimem\"}")
StorageConfiguration storageConfiguration
) {
var ioRegistry = new PageIoRegistry();
ioRegistry.loadFromServiceLoader();
engine = new VolatilePageMemoryStorageEngine("node", engineConfig, storageConfiguration, ioRegistry, pageEvictionTracker);
engine.start();
initialize();
}
@AfterEach
@Override
protected void tearDown() throws Exception {
super.tearDown();
IgniteUtils.closeAllManually(engine == null ? null : engine::stop);
}
@Override
protected MvTableStorage createMvTableStorage() {
return engine.createMvTable(
new StorageTableDescriptor(1, DEFAULT_PARTITION_COUNT, DEFAULT_STORAGE_PROFILE),
indexDescriptorSupplier
);
}
@Test
void partitionDestructionFreesPartitionPages() throws Exception {
MvPartitionStorage partitionStorage = getOrCreateMvPartition(0);
insertOneRow(partitionStorage);
long emptyDataPagesBeforeDestroy = dataRegion().freeList().emptyDataPages();
assertThat(tableStorage.destroyPartition(0), willSucceedFast());
assertMvDataDestructionCompletes(emptyDataPagesBeforeDestroy);
}
private void assertMvDataDestructionCompletes(long emptyDataPagesBeforeDestroy)
throws InterruptedException, IgniteInternalCheckedException {
assertTrue(waitForCondition(
() -> dataRegion().freeList().emptyDataPages() > emptyDataPagesBeforeDestroy,
5_000
));
// 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) {
BinaryRow binaryRow = binaryRow(new TestKey(0, "0"), new TestValue(1, "1"));
partitionStorage.runConsistently(locker -> {
RowId rowId = new RowId(PARTITION_ID);
locker.lock(rowId);
partitionStorage.addWriteCommitted(rowId, binaryRow, clock.now());
return null;
});
}
@Test
void tableStorageDestructionFreesPartitionsPages() throws Exception {
MvPartitionStorage partitionStorage = getOrCreateMvPartition(0);
insertOneRow(partitionStorage);
long emptyDataPagesBeforeDestroy = dataRegion().freeList().emptyDataPages();
assertThat(tableStorage.destroy(), willSucceedFast());
assertMvDataDestructionCompletes(emptyDataPagesBeforeDestroy);
}
@Test
void partitionDestructionFreesHashIndexPages() throws Exception {
getOrCreateMvPartition(0);
HashIndexStorage indexStorage = tableStorage.getOrCreateHashIndex(0, hashIdx);
indexStorage.put(nonInlinableIndexRow());
long emptyIndexPagesBeforeDestroy = dataRegion().freeList().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())
.appendInt(1)
.appendString("a".repeat(300))
.build();
BinaryTuple tuple = new BinaryTuple(schema.elementCount(), buffer);
return new IndexRowImpl(tuple, rowId);
}
private void assertIndexDataDestructionCompletes(long emptyIndexPagesBeforeDestroy)
throws InterruptedException, IgniteInternalCheckedException {
assertTrue(waitForCondition(
() -> dataRegion().freeList().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 {
getOrCreateMvPartition(0);
SortedIndexStorage indexStorage = tableStorage.getOrCreateSortedIndex(0, sortedIdx);
indexStorage.put(nonInlinableIndexRow());
long emptyIndexPagesBeforeDestroy = dataRegion().freeList().emptyDataPages();
assertThat(tableStorage.destroyPartition(0), willSucceedFast());
assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
}
@Test
void tableStorageDestructionFreesHashIndexPages() throws Exception {
getOrCreateMvPartition(0);
HashIndexStorage indexStorage = tableStorage.getOrCreateHashIndex(0, hashIdx);
indexStorage.put(nonInlinableIndexRow());
long emptyIndexPagesBeforeDestroy = dataRegion().freeList().emptyDataPages();
assertThat(tableStorage.destroy(), willSucceedFast());
assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
}
@Test
void tableStorageDestructionFreesSortedIndexPages() throws Exception {
getOrCreateMvPartition(0);
SortedIndexStorage indexStorage = tableStorage.getOrCreateSortedIndex(0, sortedIdx);
indexStorage.put(nonInlinableIndexRow());
long emptyIndexPagesBeforeDestroy = dataRegion().freeList().emptyDataPages();
assertThat(tableStorage.destroy(), willSucceedFast());
assertIndexDataDestructionCompletes(emptyIndexPagesBeforeDestroy);
}
private VolatilePageMemoryDataRegion dataRegion() {
return ((VolatilePageMemoryTableStorage) tableStorage).dataRegion();
}
}