blob: a510ff75a24b5be9091024fdf50e620f48789ddd [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.index.hash;
import static org.apache.ignite.internal.storage.util.StorageUtils.throwExceptionIfStorageInProgressOfRebalance;
import java.util.Objects;
import org.apache.ignite.internal.lang.IgniteInternalCheckedException;
import org.apache.ignite.internal.pagememory.freelist.FreeListImpl;
import org.apache.ignite.internal.pagememory.util.GradualTask;
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;
import org.apache.ignite.internal.storage.index.HashIndexStorage;
import org.apache.ignite.internal.storage.index.IndexRow;
import org.apache.ignite.internal.storage.index.StorageHashIndexDescriptor;
import org.apache.ignite.internal.storage.pagememory.index.AbstractPageMemoryIndexStorage;
import org.apache.ignite.internal.storage.pagememory.index.freelist.IndexColumns;
import org.apache.ignite.internal.storage.pagememory.index.meta.IndexMeta;
import org.apache.ignite.internal.storage.pagememory.index.meta.IndexMetaTree;
import org.apache.ignite.internal.util.Cursor;
import org.jetbrains.annotations.Nullable;
/**
* Implementation of Hash index storage using Page Memory.
*/
public class PageMemoryHashIndexStorage extends AbstractPageMemoryIndexStorage<HashIndexRowKey, HashIndexRow, HashIndexTree>
implements HashIndexStorage {
/**
* Index descriptor.
*
* <p>Can be {@code null} only during recovery.
*/
@Nullable
private final StorageHashIndexDescriptor descriptor;
/**
* Constructor.
*
* @param indexMeta Index meta.
* @param descriptor Hash index descriptor.
* @param freeList Free list.
* @param indexTree Hash index tree instance.
* @param indexMetaTree Index meta tree instance.
*/
public PageMemoryHashIndexStorage(
IndexMeta indexMeta,
@Nullable StorageHashIndexDescriptor descriptor,
FreeListImpl freeList,
HashIndexTree indexTree,
IndexMetaTree indexMetaTree,
boolean isVolatile
) {
super(indexMeta, indexTree.partitionId(), indexTree, freeList, indexMetaTree, isVolatile);
this.descriptor = descriptor;
}
@Override
public StorageHashIndexDescriptor indexDescriptor() {
assert descriptor != null : "This tree must only be used during recovery";
return descriptor;
}
@Override
public Cursor<RowId> get(BinaryTuple key) throws StorageException {
return busyDataRead(() -> {
throwExceptionIfStorageInProgressOfRebalance(state.get(), this::createStorageInfo);
throwExceptionIfIndexIsNotBuilt();
IndexColumns indexColumns = new IndexColumns(partitionId, key.byteBuffer());
HashIndexRow lowerBound = new HashIndexRow(indexColumns, lowestRowId);
return new ScanCursor<RowId>(lowerBound) {
@Override
protected RowId map(HashIndexRow value) {
return value.rowId();
}
@Override
protected boolean exceedsUpperBound(HashIndexRow value) {
return !Objects.equals(value.indexColumns().valueBuffer(), key.byteBuffer());
}
};
});
}
@Override
public void put(IndexRow row) throws StorageException {
busyNonDataRead(() -> {
try {
IndexColumns indexColumns = new IndexColumns(partitionId, row.indexColumns().byteBuffer());
HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, row.rowId());
HashIndexTree tree = indexTree;
var insert = new InsertHashIndexRowInvokeClosure(hashIndexRow, freeList, tree.inlineSize());
tree.invoke(hashIndexRow, null, insert);
return null;
} catch (IgniteInternalCheckedException e) {
throw new StorageException("Failed to put value into index", e);
}
});
}
@Override
public void remove(IndexRow row) throws StorageException {
busyNonDataRead(() -> {
throwExceptionIfStorageInProgressOfRebalance(state.get(), this::createStorageInfo);
try {
IndexColumns indexColumns = new IndexColumns(partitionId, row.indexColumns().byteBuffer());
HashIndexRow hashIndexRow = new HashIndexRow(indexColumns, row.rowId());
var remove = new RemoveHashIndexRowInvokeClosure(hashIndexRow, freeList);
indexTree.invoke(hashIndexRow, null, remove);
// Performs actual deletion from freeList if necessary.
remove.afterCompletion();
return null;
} catch (IgniteInternalCheckedException e) {
throw new StorageException("Failed to remove value from index", e);
}
});
}
@Override
protected GradualTask createDestructionTask(int maxWorkUnits) throws IgniteInternalCheckedException {
return indexTree.startGradualDestruction(
rowKey -> removeIndexColumns((HashIndexRow) rowKey),
false,
maxWorkUnits
);
}
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);
}
}
}