blob: 00cb35e18e69ca9b7a82d9bd54c86f3050e0244f [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.cache.query.index.sorted;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.internal.GridKernalContext;
import org.apache.ignite.internal.IgniteInternalFuture;
import org.apache.ignite.internal.cache.query.index.IndexName;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexKeyType;
import org.apache.ignite.internal.cache.query.index.sorted.inline.InlineIndexTree;
import org.apache.ignite.internal.cache.query.index.sorted.keys.IndexKey;
import org.apache.ignite.internal.metric.IoStatisticsHolderIndex;
import org.apache.ignite.internal.pagemem.PageIdUtils;
import org.apache.ignite.internal.pagemem.PageMemory;
import org.apache.ignite.internal.processors.cache.CacheGroupContext;
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.IgniteCacheOffheapManager;
import org.apache.ignite.internal.processors.cache.persistence.CacheDataRow;
import org.apache.ignite.internal.processors.cache.persistence.RootPage;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.pendingtask.DurableBackgroundTask;
import org.apache.ignite.internal.processors.cache.persistence.metastorage.pendingtask.DurableBackgroundTaskResult;
import org.apache.ignite.internal.processors.cache.persistence.tree.BPlusTree;
import org.apache.ignite.internal.processors.cache.persistence.tree.io.PageIoResolver;
import org.apache.ignite.internal.util.future.GridFutureAdapter;
import org.apache.ignite.internal.util.typedef.internal.CU;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.internal.util.worker.GridWorker;
import org.apache.ignite.thread.IgniteThread;
import org.jetbrains.annotations.Nullable;
import static java.util.Collections.singleton;
import static org.apache.ignite.internal.metric.IoStatisticsType.SORTED_INDEX;
/**
* Tasks that cleans up index tree.
*/
public class DurableBackgroundCleanupIndexTreeTask implements DurableBackgroundTask {
/** */
private static final long serialVersionUID = 0L;
/** */
private List<Long> rootPages;
/** */
private transient volatile List<InlineIndexTree> trees;
/** */
private String cacheGrpName;
/** */
private final String cacheName;
/** */
private String schemaName;
/** */
private final String treeName;
/** */
private final String idxName;
/** */
private final String id;
/** Logger. */
@Nullable private transient volatile IgniteLogger log;
/** Worker tasks. */
@Nullable private transient volatile GridWorker worker;
/** */
public DurableBackgroundCleanupIndexTreeTask(
List<Long> rootPages,
List<InlineIndexTree> trees,
String cacheGrpName,
String cacheName,
IndexName idxName,
String treeName
) {
this.rootPages = rootPages;
this.trees = trees;
this.cacheGrpName = cacheGrpName;
this.cacheName = cacheName;
this.id = UUID.randomUUID().toString();
this.idxName = idxName.idxName();
this.schemaName = idxName.schemaName();
this.treeName = treeName;
}
/** {@inheritDoc} */
@Override public String name() {
return "DROP_SQL_INDEX-" + schemaName + "." + idxName + "-" + id;
}
/** {@inheritDoc} */
@Override public IgniteInternalFuture<DurableBackgroundTaskResult> executeAsync(GridKernalContext ctx) {
log = ctx.log(this.getClass());
assert worker == null;
GridFutureAdapter<DurableBackgroundTaskResult> fut = new GridFutureAdapter<>();
worker = new GridWorker(
ctx.igniteInstanceName(),
"async-durable-background-task-executor-" + name(),
log
) {
/** {@inheritDoc} */
@Override protected void body() {
try {
execute(ctx);
worker = null;
fut.onDone(DurableBackgroundTaskResult.complete(null));
}
catch (Throwable t) {
worker = null;
fut.onDone(DurableBackgroundTaskResult.restart(t));
}
}
};
new IgniteThread(worker).start();
return fut;
}
/**
* Task execution.
*
* @param ctx Kernal context.
*/
private void execute(GridKernalContext ctx) {
List<InlineIndexTree> trees0 = trees;
if (trees0 == null) {
trees0 = new ArrayList<>(rootPages.size());
GridCacheContext cctx = ctx.cache().context().cacheContext(CU.cacheId(cacheName));
int grpId = CU.cacheGroupId(cacheName, cacheGrpName);
CacheGroupContext grpCtx = ctx.cache().cacheGroup(grpId);
// If group context is null, it means that group doesn't exist and we don't need this task anymore.
if (grpCtx == null)
return;
IgniteCacheOffheapManager offheap = grpCtx.offheap();
if (treeName != null) {
ctx.cache().context().database().checkpointReadLock();
try {
int cacheId = CU.cacheId(cacheName);
for (int segment = 0; segment < rootPages.size(); segment++) {
try {
RootPage rootPage = offheap.findRootPageForIndex(cacheId, treeName, segment);
if (rootPage != null && rootPages.get(segment) == rootPage.pageId().pageId())
offheap.dropRootPageForIndex(cacheId, treeName, segment);
}
catch (IgniteCheckedException e) {
throw new IgniteException(e);
}
}
}
finally {
ctx.cache().context().database().checkpointReadUnlock();
}
}
IoStatisticsHolderIndex stats = new IoStatisticsHolderIndex(
SORTED_INDEX,
cctx.name(),
idxName,
cctx.kernalContext().metric()
);
PageMemory pageMem = grpCtx.dataRegion().pageMemory();
for (int i = 0; i < rootPages.size(); i++) {
Long rootPage = rootPages.get(i);
assert rootPage != null;
if (skipDeletedRoot(grpId, pageMem, rootPage)) {
ctx.log(getClass()).warning(S.toString("Skipping deletion of the index tree",
"cacheGrpName", cacheGrpName, false,
"cacheName", cacheName, false,
"idxName", idxName, false,
"segment", i, false,
"rootPageId", PageIdUtils.toDetailString(rootPage), false
));
continue;
}
// Below we create a fake index tree using it's root page, stubbing some parameters,
// because we just going to free memory pages that are occupied by tree structure.
try {
String treeName = "deletedTree_" + i + "_" + name();
InlineIndexTree tree = new InlineIndexTree(
null, cctx, treeName, cctx.offheap(), cctx.offheap().reuseListForIndex(treeName),
cctx.dataRegion().pageMemory(), PageIoResolver.DEFAULT_PAGE_IO_RESOLVER,
rootPage, false, 0, new IndexKeyTypeSettings(), null,
stats, new NoopRowHandlerFactory(), null);
trees0.add(tree);
}
catch (IgniteCheckedException e) {
throw new IgniteException(e);
}
}
}
ctx.cache().context().database().checkpointReadLock();
try {
for (int i = 0; i < trees0.size(); i++) {
BPlusTree tree = trees0.get(i);
try {
tree.destroy(null, true);
}
catch (IgniteCheckedException e) {
throw new IgniteException(e);
}
}
}
finally {
ctx.cache().context().database().checkpointReadUnlock();
}
}
/**
* Checks that pageId is still relevant and has not been deleted / reused.
* @param grpId Cache group id.
* @param pageMem Page memory instance.
* @param rootPageId Root page identifier.
* @return {@code true} if root page was deleted/reused, {@code false} otherwise.
*/
private boolean skipDeletedRoot(int grpId, PageMemory pageMem, long rootPageId) {
try {
long page = pageMem.acquirePage(grpId, rootPageId);
try {
long pageAddr = pageMem.readLock(grpId, rootPageId, page);
try {
return pageAddr == 0;
}
finally {
if (pageAddr != 0)
pageMem.readUnlock(grpId, rootPageId, page);
}
}
finally {
pageMem.releasePage(grpId, rootPageId, page);
}
}
catch (IgniteCheckedException e) {
throw new IgniteException("Cannot acquire tree root page.", e);
}
}
/** {@inheritDoc} */
@Override public void cancel() {
trees = null;
GridWorker w = worker;
if (w != null) {
worker = null;
U.awaitForWorkersStop(singleton(w), true, log);
}
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(DurableBackgroundCleanupIndexTreeTask.class, this);
}
/** */
private static class NoopRowHandlerFactory implements InlineIndexRowHandlerFactory {
/** {@inheritDoc} */
@Override public InlineIndexRowHandler create(SortedIndexDefinition sdef, IndexKeyTypeSettings keyTypeSettings) {
return new InlineIndexRowHandler() {
/** {@inheritDoc} */
@Override public IndexKey indexKey(int idx, CacheDataRow row) {
return null;
}
/** {@inheritDoc} */
@Override public List<InlineIndexKeyType> inlineIndexKeyTypes() {
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override public List<IndexKeyDefinition> indexKeyDefinitions() {
return Collections.emptyList();
}
@Override public IndexKeyTypeSettings indexKeyTypeSettings() {
return null;
}
/** {@inheritDoc} */
@Override public int partition(CacheDataRow row) {
return 0;
}
/** {@inheritDoc} */
@Override public Object cacheKey(CacheDataRow row) {
return null;
}
/** {@inheritDoc} */
@Override public Object cacheValue(CacheDataRow row) {
return null;
}
};
}
}
}