com.bigdata.resources.SplitTailTask Maven / Gradle / Ivy
/*
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Feb 6, 2009
*/
package com.bigdata.resources;
import java.util.UUID;
import com.bigdata.journal.TimestampUtility;
import com.bigdata.service.DataService;
import com.bigdata.service.Event;
import com.bigdata.service.EventResource;
import com.bigdata.service.Split;
/**
* Splits the tail of an index partition and optionally submits a task to move
* the tail to a target data service specified by the caller.
*
* The split point is choosen by locating the right-most non-leaf node. The key
* range which would enter that node is placed within the new right-sibling
* index partition. The rest of the key range is placed within the new
* left-sibling index partition.
*
* The tail split operation is extremely fast since it only redefines the index
* partitions. However, it require RMI to the metadata index to do that and
* therefore should not be done during synchronous overflow in order to reduce
* the possibility for errors during that operation.
*
* The right-sibling of tail split is a prime candidate for a move since there
* is an expectation that it will continue to be hot for writes. Therefore the
* caller has an opportunity when specifying a tail split to also specify that
* the new right-sibling index partition will be moved onto a caller specified
* data service.
*
* @todo unit tests.
*
* @author Bryan Thompson
* @version $Id$
*/
public class SplitTailTask extends AbstractPrepareTask {
private final ViewMetadata vmd;
/**
* When non-null
the new right-sibling (the tail) will be
* moved to the specified data service after the split.
*/
private final UUID moveTarget;
/**
* @param vmd
* @param moveTarget
* When non-null
the new right-sibling (the tail)
* will be moved to the specified data service after the split.
*/
public SplitTailTask(final ViewMetadata vmd, final UUID moveTarget) {
super(vmd.resourceManager, TimestampUtility
.asHistoricalRead(vmd.commitTime), vmd.name);
this.vmd = vmd;
if (vmd.pmd == null) {
throw new IllegalStateException("Not an index partition.");
}
if (vmd.pmd.getSourcePartitionId() != -1) {
throw new IllegalStateException(
"Split not allowed during move: sourcePartitionId="
+ vmd.pmd.getSourcePartitionId());
}
if (resourceManager.getDataServiceUUID().equals(moveTarget)) {
throw new IllegalArgumentException("Move to self");
}
this.moveTarget = moveTarget;
}
@Override
protected void clearRefs() {
vmd.clearRef();
}
@Override
protected Object doTask() throws Exception {
final Event e = new Event(resourceManager.getFederation(),
new EventResource(vmd.indexMetadata),
OverflowActionEnum.TailSplit, vmd.getParams()).addDetail(
"summary", OverflowActionEnum.TailSplit
+ (moveTarget != null ? "+" + OverflowActionEnum.Move
: "") + "(" + vmd.name + ")");
if (moveTarget != null) {
e.addDetail("moveTarget", "" + moveTarget);
}
e.start();
SplitResult splitResult = null;
try {
try {
/*
* Split into head (most) and tail. both will be new index
* partitions.
*/
final Split[] splits = SplitUtility.tailSplit(resourceManager,
vmd.getBTree());
// validate the splits before processing them.
SplitUtility.validateSplits(vmd.getBTree(), splits);
splitResult = SplitUtility.buildSplits(vmd, splits, e);
} finally {
/*
* We are done building index segments from the source index
* partition view so we clear our references for that view.
*/
clearRefs();
}
// Do the atomic update
SplitIndexPartitionTask
.doSplitAtomicUpdate(
resourceManager,
vmd,
splitResult,
OverflowActionEnum.TailSplit,
resourceManager.overflowCounters.indexPartitionTailSplitCounter,
e);
if (moveTarget != null) {
/*
* Note: Unlike a normal move where there are writes on the old
* journal, all the data for the rightSibling is in an index
* segment that we just built and new writes MAY be buffered on
* the live journal. Therefore we use a different entry point
* into the MOVE operation, one which does not copy over the
* data from the old journal.
*/
/*
* Obtain a new partition identifier for the partition that will
* be created when we move the rightSibling to the target data
* service.
*/
final int newPartitionId = resourceManager
.nextPartitionId(vmd.indexMetadata.getName());
/*
* The name of the post-split rightSibling (this is the source
* index partition for the move operation).
*/
final String rightSiblingName = DataService
.getIndexPartitionName(vmd.indexMetadata.getName(),
splitResult.splits[1].pmd.getPartitionId());
/*
* Move.
*
* Note: We do not explicitly delete the source index segment
* for the rightSibling after the move. It will be required for
* historical views of the rightSibling in case any client
* gained access to the rightSibling after the split and before
* the move. It will eventually be released once the view of the
* rightSibling index partition becomes sufficiently aged that
* it falls off the head of the database history.
*/
MoveTask.doAtomicUpdate(resourceManager, rightSiblingName,
splitResult.buildResults[1]/*rightSibling*/, moveTarget,
newPartitionId, e);
}
// Done.
return splitResult;
} finally {
if (splitResult != null) {
for (BuildResult buildResult : splitResult.buildResults) {
if (buildResult != null) {
/*
* At this point the index segment was either incorporated into
* the new view in a restart safe manner or there was an error.
* Either way, we now remove the index segment store's UUID from
* the retentionSet so it will be subject to the release policy
* of the StoreManager.
*/
resourceManager
.retentionSetRemove(buildResult.segmentMetadata
.getUUID());
}
}
}
e.end();
}
}
}