blob: 5f78dccc0cdde71bd5e624cfd9e5a8ca61f4b43e [file] [log] [blame]
/*
* Copyright 2009-2010 by The Regents of the University of California
* Licensed 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 from
*
* 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 edu.uci.ics.hyracks.storage.am.rtree.impls;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import edu.uci.ics.hyracks.api.dataflow.value.ISerializerDeserializer;
import edu.uci.ics.hyracks.api.exceptions.HyracksDataException;
import edu.uci.ics.hyracks.dataflow.common.data.accessors.ITupleReference;
import edu.uci.ics.hyracks.storage.am.common.api.IFreePageManager;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexBulkLoadContext;
import edu.uci.ics.hyracks.storage.am.common.api.IIndexOpContext;
import edu.uci.ics.hyracks.storage.am.common.api.ISearchPredicate;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndex;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexAccessor;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexCursor;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexFrame;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexFrameFactory;
import edu.uci.ics.hyracks.storage.am.common.api.ITreeIndexMetaDataFrame;
import edu.uci.ics.hyracks.storage.am.common.api.IndexType;
import edu.uci.ics.hyracks.storage.am.common.api.PageAllocationException;
import edu.uci.ics.hyracks.storage.am.common.api.TreeIndexException;
import edu.uci.ics.hyracks.storage.am.common.frames.FrameOpSpaceStatus;
import edu.uci.ics.hyracks.storage.am.common.impls.TreeDiskOrderScanCursor;
import edu.uci.ics.hyracks.storage.am.common.ophelpers.IndexOp;
import edu.uci.ics.hyracks.storage.am.common.ophelpers.MultiComparator;
import edu.uci.ics.hyracks.storage.am.common.util.TreeIndexUtils;
import edu.uci.ics.hyracks.storage.am.rtree.api.IRTreeFrame;
import edu.uci.ics.hyracks.storage.am.rtree.api.IRTreeInteriorFrame;
import edu.uci.ics.hyracks.storage.am.rtree.api.IRTreeLeafFrame;
import edu.uci.ics.hyracks.storage.am.rtree.frames.RTreeNSMFrame;
import edu.uci.ics.hyracks.storage.common.buffercache.IBufferCache;
import edu.uci.ics.hyracks.storage.common.buffercache.ICachedPage;
import edu.uci.ics.hyracks.storage.common.file.BufferedFileHandle;
public class RTree implements ITreeIndex {
private boolean created = false;
private boolean loaded = false;
private final int rootPage = 1; // the root page never changes
private final AtomicLong globalNsn; // Global node sequence number
private int numOfPages = 1;
private final ReadWriteLock treeLatch;
private final IFreePageManager freePageManager;
private final IBufferCache bufferCache;
private int fileId;
private final SearchPredicate diskOrderScanPredicate;
private final ITreeIndexFrameFactory interiorFrameFactory;
private final ITreeIndexFrameFactory leafFrameFactory;
private final int fieldCount;
private final MultiComparator cmp;
public int rootSplits = 0;
public int[] splitsByLevel = new int[500];
public AtomicLong readLatchesAcquired = new AtomicLong();
public AtomicLong readLatchesReleased = new AtomicLong();
public AtomicLong writeLatchesAcquired = new AtomicLong();
public AtomicLong writeLatchesReleased = new AtomicLong();
public AtomicLong pins = new AtomicLong();
public AtomicLong unpins = new AtomicLong();
public byte currentLevel = 0;
// TODO: is MultiComparator needed at all?
public RTree(IBufferCache bufferCache, int fieldCount, MultiComparator cmp, IFreePageManager freePageManager,
ITreeIndexFrameFactory interiorFrameFactory, ITreeIndexFrameFactory leafFrameFactory) {
this.bufferCache = bufferCache;
this.fieldCount = fieldCount;
this.cmp = cmp;
this.freePageManager = freePageManager;
this.interiorFrameFactory = interiorFrameFactory;
this.leafFrameFactory = leafFrameFactory;
globalNsn = new AtomicLong();
this.treeLatch = new ReentrantReadWriteLock(true);
this.diskOrderScanPredicate = new SearchPredicate(null, cmp);
}
public void incrementGlobalNsn() {
globalNsn.incrementAndGet();
}
public long getGlobalNsn() {
return globalNsn.get();
}
public void incrementReadLatchesAcquired() {
readLatchesAcquired.incrementAndGet();
}
public void incrementReadLatchesReleased() {
readLatchesReleased.incrementAndGet();
}
public void incrementWriteLatchesAcquired() {
writeLatchesAcquired.incrementAndGet();
}
public void incrementWriteLatchesReleased() {
writeLatchesReleased.incrementAndGet();
}
public void incrementPins() {
pins.incrementAndGet();
}
public void incrementUnpins() {
unpins.incrementAndGet();
}
public String printStats() {
StringBuilder strBuilder = new StringBuilder();
strBuilder.append("\n");
strBuilder.append("ROOTSPLITS: " + rootSplits + "\n");
strBuilder.append("SPLITS BY LEVEL\n");
for (int i = 0; i < currentLevel; i++) {
strBuilder.append(String.format("%3d ", i) + String.format("%8d ", splitsByLevel[i]) + "\n");
}
strBuilder.append(String.format("READ LATCHES: %10d %10d\n", readLatchesAcquired.get(),
readLatchesReleased.get()));
strBuilder.append(String.format("WRITE LATCHES: %10d %10d\n", writeLatchesAcquired.get(),
writeLatchesReleased.get()));
strBuilder.append(String.format("PINS: %10d %10d\n", pins.get(), unpins.get()));
strBuilder.append(String.format("Num of Pages: %10d\n", numOfPages));
return strBuilder.toString();
}
public void printTree(IRTreeFrame leafFrame, IRTreeFrame interiorFrame, ISerializerDeserializer[] keySerdes)
throws Exception {
printTree(rootPage, null, false, leafFrame, interiorFrame, keySerdes);
}
public void printTree(int pageId, ICachedPage parent, boolean unpin, IRTreeFrame leafFrame,
IRTreeFrame interiorFrame, ISerializerDeserializer[] keySerdes) throws Exception {
ICachedPage node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
node.acquireReadLatch();
incrementReadLatchesAcquired();
try {
if (parent != null && unpin == true) {
parent.releaseReadLatch();
incrementReadLatchesReleased();
bufferCache.unpin(parent);
incrementUnpins();
}
interiorFrame.setPage(node);
int level = interiorFrame.getLevel();
System.out.format("%1d ", level);
System.out.format("%3d ", pageId);
for (int i = 0; i < currentLevel - level; i++)
System.out.format(" ");
String keyString;
if (interiorFrame.isLeaf()) {
leafFrame.setPage(node);
keyString = TreeIndexUtils.printFrameTuples(leafFrame, keySerdes);
} else {
keyString = TreeIndexUtils.printFrameTuples(interiorFrame, keySerdes);
}
System.out.format(keyString);
if (!interiorFrame.isLeaf()) {
ArrayList<Integer> children = ((RTreeNSMFrame) (interiorFrame)).getChildren(cmp);
for (int i = 0; i < children.size(); i++) {
printTree(children.get(i), node, i == children.size() - 1, leafFrame, interiorFrame, keySerdes);
}
} else {
node.releaseReadLatch();
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
} catch (Exception e) {
node.releaseReadLatch();
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
throw e;
}
}
@Override
public void create(int fileId) throws HyracksDataException {
treeLatch.writeLock().lock();
try {
if (created) {
return;
}
ITreeIndexFrame leafFrame = leafFrameFactory.createFrame();
ITreeIndexMetaDataFrame metaFrame = freePageManager.getMetaDataFrameFactory().createFrame();
freePageManager.init(metaFrame, rootPage);
// initialize root page
ICachedPage rootNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, rootPage), true);
incrementPins();
rootNode.acquireWriteLatch();
incrementWriteLatchesAcquired();
try {
leafFrame.setPage(rootNode);
leafFrame.initBuffer((byte) 0);
} finally {
rootNode.releaseWriteLatch();
incrementWriteLatchesReleased();
bufferCache.unpin(rootNode);
incrementUnpins();
}
currentLevel = 0;
created = true;
} finally {
treeLatch.writeLock().unlock();
}
}
public void open(int fileId) {
this.fileId = fileId;
}
public void close() {
fileId = -1;
}
private RTreeOpContext createOpContext() {
return new RTreeOpContext((IRTreeLeafFrame) leafFrameFactory.createFrame(),
(IRTreeInteriorFrame) interiorFrameFactory.createFrame(), freePageManager.getMetaDataFrameFactory()
.createFrame(), 8);
}
private void insert(ITupleReference tuple, IIndexOpContext ictx) throws HyracksDataException, TreeIndexException, PageAllocationException {
RTreeOpContext ctx = (RTreeOpContext) ictx;
ctx.reset();
ctx.setTuple(tuple);
ctx.splitKey.reset();
ctx.splitKey.getLeftTuple().setFieldCount(cmp.getKeyFieldCount());
ctx.splitKey.getRightTuple().setFieldCount(cmp.getKeyFieldCount());
int maxFieldPos = cmp.getKeyFieldCount() / 2;
for (int i = 0; i < maxFieldPos; i++) {
int j = maxFieldPos + i;
int c = cmp.getComparators()[i].compare(tuple.getFieldData(i), tuple.getFieldStart(i),
tuple.getFieldLength(i), tuple.getFieldData(j), tuple.getFieldStart(j), tuple.getFieldLength(j));
if (c > 0) {
throw new IllegalArgumentException("The low key point has larger coordinates than the high key point.");
}
}
ICachedPage leafNode = findLeaf(ctx);
int pageId = ctx.pathList.getLastPageId();
ctx.pathList.moveLast();
insertTuple(leafNode, pageId, ctx.getTuple(), ctx, true);
while (true) {
if (ctx.splitKey.getLeftPageBuffer() != null) {
updateParentForInsert(ctx);
} else {
break;
}
}
leafNode.releaseWriteLatch();
incrementWriteLatchesReleased();
bufferCache.unpin(leafNode);
incrementUnpins();
}
public ICachedPage findLeaf(RTreeOpContext ctx) throws HyracksDataException {
int pageId = rootPage;
boolean writeLatched = false;
boolean readLatched = false;
boolean succeed = false;
ICachedPage node = null;
boolean isLeaf = false;
long pageLsn = 0, parentLsn = 0;
try {
while (true) {
if (!writeLatched) {
node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
ctx.interiorFrame.setPage(node);
isLeaf = ctx.interiorFrame.isLeaf();
if (isLeaf) {
node.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
if (!ctx.interiorFrame.isLeaf()) {
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
continue;
}
} else {
// Be optimistic and grab read latch first. We will swap
// it
// to write latch if we need to enlarge the best child
// tuple.
node.acquireReadLatch();
readLatched = true;
incrementReadLatchesAcquired();
}
}
if (pageId != rootPage && parentLsn < ctx.interiorFrame.getPageNsn()) {
// Concurrent split detected, go back to parent and
// re-choose
// the best child
if (writeLatched) {
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
} else {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
pageId = ctx.pathList.getLastPageId();
if (pageId != rootPage) {
parentLsn = ctx.pathList.getPageLsn(ctx.pathList.size() - 2);
}
ctx.pathList.moveLast();
continue;
}
pageLsn = ctx.interiorFrame.getPageLsn();
ctx.pathList.add(pageId, pageLsn, -1);
if (!isLeaf) {
// findBestChild must be called *before* getBestChildPageId
ctx.interiorFrame.findBestChild(ctx.getTuple(), cmp);
int childPageId = ctx.interiorFrame.getBestChildPageId();
if (!writeLatched) {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
// TODO: do we need to un-pin and pin again?
bufferCache.unpin(node);
incrementUnpins();
node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
node.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.interiorFrame.setPage(node);
if (ctx.interiorFrame.getPageLsn() != pageLsn) {
// The page was changed while we unlocked it; thus,
// retry (re-choose best child)
ctx.pathList.moveLast();
continue;
}
}
// We don't need to reset the frameTuple because it is
// already pointing to the best child
ctx.interiorFrame.enlarge(ctx.getTuple(), cmp);
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
pageId = childPageId;
parentLsn = pageLsn;
} else {
ctx.leafFrame.setPage(node);
succeed = true;
return node;
}
}
} finally {
if (!succeed) {
if (readLatched) {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
} else if (writeLatched) {
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
}
}
}
private void insertTuple(ICachedPage node, int pageId, ITupleReference tuple, RTreeOpContext ctx, boolean isLeaf)
throws HyracksDataException, TreeIndexException, PageAllocationException {
FrameOpSpaceStatus spaceStatus;
if (!isLeaf) {
spaceStatus = ctx.interiorFrame.hasSpaceInsert(tuple);
} else {
spaceStatus = ctx.leafFrame.hasSpaceInsert(tuple);
}
switch (spaceStatus) {
case SUFFICIENT_CONTIGUOUS_SPACE: {
if (!isLeaf) {
ctx.interiorFrame.insert(tuple, -1);
incrementGlobalNsn();
ctx.interiorFrame.setPageLsn(getGlobalNsn());
} else {
ctx.leafFrame.insert(tuple, -1);
incrementGlobalNsn();
ctx.leafFrame.setPageLsn(getGlobalNsn());
}
ctx.splitKey.reset();
break;
}
case SUFFICIENT_SPACE: {
if (!isLeaf) {
ctx.interiorFrame.compact();
ctx.interiorFrame.insert(tuple, -1);
incrementGlobalNsn();
ctx.interiorFrame.setPageLsn(getGlobalNsn());
} else {
ctx.leafFrame.compact();
ctx.leafFrame.insert(tuple, -1);
incrementGlobalNsn();
ctx.leafFrame.setPageLsn(getGlobalNsn());
}
ctx.splitKey.reset();
break;
}
case INSUFFICIENT_SPACE: {
int rightPageId = freePageManager.getFreePage(ctx.metaFrame);
ICachedPage rightNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, rightPageId), true);
incrementPins();
rightNode.acquireWriteLatch();
incrementWriteLatchesAcquired();
try {
IRTreeFrame rightFrame;
numOfPages++; // debug
if (!isLeaf) {
splitsByLevel[ctx.interiorFrame.getLevel()]++; // debug
rightFrame = (IRTreeFrame) interiorFrameFactory.createFrame();
rightFrame.setPage(rightNode);
rightFrame.initBuffer((byte) ctx.interiorFrame.getLevel());
ctx.interiorFrame.split(rightFrame, tuple, ctx.splitKey);
ctx.interiorFrame.setRightPage(rightPageId);
rightFrame.setPageNsn(ctx.interiorFrame.getPageNsn());
incrementGlobalNsn();
long newNsn = getGlobalNsn();
rightFrame.setPageLsn(newNsn);
ctx.interiorFrame.setPageNsn(newNsn);
ctx.interiorFrame.setPageLsn(newNsn);
} else {
splitsByLevel[0]++; // debug
rightFrame = (IRTreeFrame) leafFrameFactory.createFrame();
rightFrame.setPage(rightNode);
rightFrame.initBuffer((byte) 0);
ctx.leafFrame.split(rightFrame, tuple, ctx.splitKey);
ctx.leafFrame.setRightPage(rightPageId);
rightFrame.setPageNsn(ctx.leafFrame.getPageNsn());
incrementGlobalNsn();
long newNsn = getGlobalNsn();
rightFrame.setPageLsn(newNsn);
ctx.leafFrame.setPageNsn(newNsn);
ctx.leafFrame.setPageLsn(newNsn);
}
ctx.splitKey.setPages(pageId, rightPageId);
if (pageId == rootPage) {
rootSplits++; // debug
splitsByLevel[currentLevel]++;
currentLevel++;
int newLeftId = freePageManager.getFreePage(ctx.metaFrame);
ICachedPage newLeftNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, newLeftId),
true);
incrementPins();
newLeftNode.acquireWriteLatch();
incrementWriteLatchesAcquired();
try {
// copy left child to new left child
System.arraycopy(node.getBuffer().array(), 0, newLeftNode.getBuffer().array(), 0,
newLeftNode.getBuffer().capacity());
// initialize new root (leftNode becomes new root)
ctx.interiorFrame.setPage(node);
ctx.interiorFrame.initBuffer((byte) (ctx.interiorFrame.getLevel() + 1));
ctx.splitKey.setLeftPage(newLeftId);
ctx.interiorFrame.insert(ctx.splitKey.getLeftTuple(), -1);
ctx.interiorFrame.insert(ctx.splitKey.getRightTuple(), -1);
incrementGlobalNsn();
long newNsn = getGlobalNsn();
ctx.interiorFrame.setPageLsn(newNsn);
ctx.interiorFrame.setPageNsn(newNsn);
} finally {
newLeftNode.releaseWriteLatch();
incrementWriteLatchesReleased();
bufferCache.unpin(newLeftNode);
incrementUnpins();
}
ctx.splitKey.reset();
}
} finally {
rightNode.releaseWriteLatch();
incrementWriteLatchesReleased();
bufferCache.unpin(rightNode);
incrementUnpins();
}
break;
}
}
}
public void updateParentForInsert(RTreeOpContext ctx) throws HyracksDataException, TreeIndexException, PageAllocationException {
boolean writeLatched = false;
int parentId = ctx.pathList.getLastPageId();
ICachedPage parentNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, parentId), false);
incrementPins();
parentNode.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.interiorFrame.setPage(parentNode);
boolean foundParent = true;
try {
if (ctx.interiorFrame.getPageLsn() != ctx.pathList.getLastPageLsn()) {
foundParent = false;
while (true) {
if (ctx.interiorFrame.findTupleByPointer(ctx.splitKey.getLeftTuple(), cmp) != -1) {
// found the parent
foundParent = true;
break;
}
int rightPage = ctx.interiorFrame.getRightPage();
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
if (rightPage == -1) {
break;
}
parentId = rightPage;
parentNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, parentId), false);
incrementPins();
parentNode.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.interiorFrame.setPage(parentNode);
}
}
if (foundParent) {
ctx.interiorFrame.adjustKey(ctx.splitKey.getLeftTuple(), -1, cmp);
insertTuple(parentNode, parentId, ctx.splitKey.getRightTuple(), ctx, ctx.interiorFrame.isLeaf());
ctx.pathList.moveLast();
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
return;
}
} finally {
if (writeLatched) {
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
}
}
// very rare situation when the there is a root split, do an
// exhaustive
// breadth-first traversal looking for the parent tuple
ctx.pathList.clear();
ctx.traverseList.clear();
findPath(ctx);
updateParentForInsert(ctx);
}
public void findPath(RTreeOpContext ctx) throws HyracksDataException {
boolean readLatched = false;
int pageId = rootPage;
int parentIndex = -1;
long parentLsn = 0;
long pageLsn;
int pageIndex;
ICachedPage node = null;
ctx.traverseList.add(pageId, -1, parentIndex);
try {
while (!ctx.traverseList.isLast()) {
pageId = ctx.traverseList.getFirstPageId();
parentIndex = ctx.traverseList.getFirstPageIndex();
node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
node.acquireReadLatch();
readLatched = true;
incrementReadLatchesAcquired();
ctx.interiorFrame.setPage(node);
pageLsn = ctx.interiorFrame.getPageLsn();
pageIndex = ctx.traverseList.first();
ctx.traverseList.setPageLsn(pageIndex, pageLsn);
ctx.traverseList.moveFirst();
if (pageId != rootPage && parentLsn < ctx.interiorFrame.getPageNsn()) {
int rightPage = ctx.interiorFrame.getRightPage();
if (rightPage != -1) {
ctx.traverseList.add(rightPage, -1, parentIndex);
}
}
parentLsn = pageLsn;
if (ctx.interiorFrame.findTupleByPointer(ctx.splitKey.getLeftTuple(), ctx.traverseList, pageIndex, cmp) != -1) {
fillPath(ctx, pageIndex);
return;
}
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
} finally {
if (readLatched) {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
}
}
public void fillPath(RTreeOpContext ctx, int pageIndex) {
if (pageIndex != -1) {
fillPath(ctx, ctx.traverseList.getPageIndex(pageIndex));
ctx.pathList.add(ctx.traverseList.getPageId(pageIndex), ctx.traverseList.getPageLsn(pageIndex), -1);
}
}
public void delete(ITupleReference tuple, RTreeOpContext ctx) throws HyracksDataException, TreeIndexException {
ctx.reset();
ctx.setTuple(tuple);
ctx.splitKey.reset();
ctx.splitKey.getLeftTuple().setFieldCount(cmp.getKeyFieldCount());
int tupleIndex = findTupleToDelete(ctx);
if (tupleIndex != -1) {
int pageId = ctx.pathList.getLastPageId();
ctx.pathList.moveLast();
deleteTuple(pageId, tupleIndex, ctx);
while (true) {
if (ctx.splitKey.getLeftPageBuffer() != null) {
updateParentForDelete(ctx);
} else {
break;
}
}
ctx.leafFrame.getPage().releaseWriteLatch();
incrementWriteLatchesReleased();
bufferCache.unpin(ctx.leafFrame.getPage());
incrementUnpins();
}
}
public void updateParentForDelete(RTreeOpContext ctx) throws HyracksDataException {
boolean writeLatched = false;
int parentId = ctx.pathList.getLastPageId();
ICachedPage parentNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, parentId), false);
incrementPins();
parentNode.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.interiorFrame.setPage(parentNode);
boolean foundParent = true;
int tupleIndex = -1;
try {
if (ctx.interiorFrame.getPageLsn() != ctx.pathList.getLastPageLsn()) {
foundParent = false;
while (true) {
tupleIndex = ctx.interiorFrame.findTupleByPointer(ctx.splitKey.getLeftTuple(), cmp);
if (tupleIndex != -1) {
// found the parent
foundParent = true;
break;
}
int rightPage = ctx.interiorFrame.getRightPage();
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
if (rightPage == -1) {
break;
}
parentId = rightPage;
parentNode = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, parentId), false);
incrementPins();
parentNode.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.interiorFrame.setPage(parentNode);
}
}
if (foundParent) {
if (tupleIndex == -1) {
tupleIndex = ctx.interiorFrame.findTupleByPointer(ctx.splitKey.getLeftTuple(), cmp);
}
boolean recomputeMBR = ctx.interiorFrame.recomputeMBR(ctx.splitKey.getLeftTuple(), tupleIndex, cmp);
if (recomputeMBR) {
ctx.interiorFrame.adjustKey(ctx.splitKey.getLeftTuple(), tupleIndex, cmp);
ctx.pathList.moveLast();
incrementGlobalNsn();
ctx.interiorFrame.setPageLsn(getGlobalNsn());
ctx.splitKey.reset();
if (!ctx.pathList.isEmpty()) {
ctx.interiorFrame.computeMBR(ctx.splitKey);
ctx.splitKey.setLeftPage(parentId);
}
} else {
ctx.pathList.moveLast();
ctx.splitKey.reset();
}
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
return;
}
} finally {
if (writeLatched) {
parentNode.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(parentNode);
incrementUnpins();
}
}
// very rare situation when the there is a root split, do an exhaustive
// breadth-first traversal looking for the parent tuple
ctx.pathList.clear();
ctx.traverseList.clear();
findPath(ctx);
updateParentForDelete(ctx);
}
public int findTupleToDelete(RTreeOpContext ctx) throws HyracksDataException {
boolean writeLatched = false;
boolean readLatched = false;
boolean succeed = false;
ICachedPage node = null;
ctx.traverseList.add(rootPage, -1, -1);
ctx.pathList.add(rootPage, -1, ctx.traverseList.size() - 1);
try {
while (!ctx.pathList.isEmpty()) {
int pageId = ctx.pathList.getLastPageId();
long parentLsn = ctx.pathList.getLastPageLsn();
int pageIndex = ctx.pathList.getLastPageIndex();
ctx.pathList.moveLast();
node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
node.acquireReadLatch();
readLatched = true;
incrementReadLatchesAcquired();
ctx.interiorFrame.setPage(node);
boolean isLeaf = ctx.interiorFrame.isLeaf();
long pageLsn = ctx.interiorFrame.getPageLsn();
int parentIndex = ctx.traverseList.getPageIndex(pageIndex);
ctx.traverseList.setPageLsn(pageIndex, pageLsn);
if (pageId != rootPage && parentLsn < ctx.interiorFrame.getPageNsn()) {
// Concurrent split detected, we need to visit the right
// page
int rightPage = ctx.interiorFrame.getRightPage();
if (rightPage != -1) {
ctx.traverseList.add(rightPage, -1, parentIndex);
ctx.pathList.add(rightPage, parentLsn, ctx.traverseList.size() - 1);
}
}
if (!isLeaf) {
for (int i = 0; i < ctx.interiorFrame.getTupleCount(); i++) {
int childPageId = ctx.interiorFrame.getChildPageIdIfIntersect(ctx.tuple, i, cmp);
if (childPageId != -1) {
ctx.traverseList.add(childPageId, -1, pageIndex);
ctx.pathList.add(childPageId, pageLsn, ctx.traverseList.size() - 1);
}
}
} else {
ctx.leafFrame.setPage(node);
int tupleIndex = ctx.leafFrame.findTupleIndex(ctx.tuple, cmp);
if (tupleIndex != -1) {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
node = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, pageId), false);
incrementPins();
node.acquireWriteLatch();
writeLatched = true;
incrementWriteLatchesAcquired();
ctx.leafFrame.setPage(node);
if (ctx.leafFrame.getPageLsn() != pageLsn) {
// The page was changed while we unlocked it
tupleIndex = ctx.leafFrame.findTupleIndex(ctx.tuple, cmp);
if (tupleIndex == -1) {
ctx.traverseList.add(pageId, -1, parentIndex);
ctx.pathList.add(pageId, parentLsn, ctx.traverseList.size() - 1);
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
continue;
} else {
ctx.pathList.clear();
fillPath(ctx, pageIndex);
succeed = true;
return tupleIndex;
}
} else {
ctx.pathList.clear();
fillPath(ctx, pageIndex);
succeed = true;
return tupleIndex;
}
}
}
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
} finally {
if (!succeed) {
if (readLatched) {
node.releaseReadLatch();
readLatched = false;
incrementReadLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
} else if (writeLatched) {
node.releaseWriteLatch();
writeLatched = false;
incrementWriteLatchesReleased();
bufferCache.unpin(node);
incrementUnpins();
}
}
}
return -1;
}
public void deleteTuple(int pageId, int tupleIndex, RTreeOpContext ctx) throws HyracksDataException {
ctx.leafFrame.delete(tupleIndex, cmp);
incrementGlobalNsn();
ctx.leafFrame.setPageLsn(getGlobalNsn());
// if the page is empty, just leave it there for future inserts
if (pageId != rootPage && ctx.leafFrame.getTupleCount() > 0) {
ctx.leafFrame.computeMBR(ctx.splitKey);
ctx.splitKey.setLeftPage(pageId);
}
}
private void search(ITreeIndexCursor cursor, ISearchPredicate searchPred, RTreeOpContext ctx) throws HyracksDataException, TreeIndexException {
ctx.reset();
ctx.cursor = cursor;
cursor.setBufferCache(bufferCache);
cursor.setFileId(fileId);
ctx.cursorInitialState.setRootPage(rootPage);
ctx.cursor.open(ctx.cursorInitialState, (SearchPredicate)searchPred);
}
public ITreeIndexFrameFactory getInteriorFrameFactory() {
return interiorFrameFactory;
}
public ITreeIndexFrameFactory getLeafFrameFactory() {
return leafFrameFactory;
}
public MultiComparator getCmp() {
return cmp;
}
public IFreePageManager getFreePageManager() {
return freePageManager;
}
private void update(ITupleReference tuple, RTreeOpContext ctx) {
throw new UnsupportedOperationException("RTree Update not implemented.");
}
public final class BulkLoadContext implements IIndexBulkLoadContext {
public ITreeIndexAccessor indexAccessor;
public BulkLoadContext(float fillFactor, IRTreeFrame leafFrame, IRTreeFrame interiorFrame,
ITreeIndexMetaDataFrame metaFrame) throws HyracksDataException {
indexAccessor = createAccessor();
}
}
@Override
public IIndexBulkLoadContext beginBulkLoad(float fillFactor) throws HyracksDataException {
if (loaded) {
throw new HyracksDataException("Trying to bulk-load RTree but RTree has already been loaded.");
}
BulkLoadContext ctx = new BulkLoadContext(fillFactor, (IRTreeFrame) leafFrameFactory.createFrame(),
(IRTreeFrame) interiorFrameFactory.createFrame(), freePageManager.getMetaDataFrameFactory()
.createFrame());
return ctx;
}
@Override
public void bulkLoadAddTuple(ITupleReference tuple, IIndexBulkLoadContext ictx) throws HyracksDataException {
try {
((BulkLoadContext) ictx).indexAccessor.insert(tuple);
} catch (Exception e) {
throw new HyracksDataException("BulkLoad Error");
}
}
@Override
public void endBulkLoad(IIndexBulkLoadContext ictx) throws HyracksDataException {
loaded = true;
}
private void diskOrderScan(ITreeIndexCursor icursor, RTreeOpContext ctx) throws HyracksDataException {
TreeDiskOrderScanCursor cursor = (TreeDiskOrderScanCursor) icursor;
ctx.reset();
int currentPageId = rootPage + 1;
int maxPageId = freePageManager.getMaxPage(ctx.metaFrame);
ICachedPage page = bufferCache.pin(BufferedFileHandle.getDiskPageId(fileId, currentPageId), false);
page.acquireReadLatch();
try {
cursor.setBufferCache(bufferCache);
cursor.setFileId(fileId);
cursor.setCurrentPageId(currentPageId);
cursor.setMaxPageId(maxPageId);
ctx.cursorInitialState.setPage(page);
cursor.open(ctx.cursorInitialState, diskOrderScanPredicate);
} catch (Exception e) {
page.releaseReadLatch();
bufferCache.unpin(page);
throw new HyracksDataException(e);
}
}
@Override
public int getRootPageId() {
return rootPage;
}
@Override
public int getFieldCount() {
return fieldCount;
}
@Override
public IndexType getIndexType() {
return IndexType.RTREE;
}
@Override
public ITreeIndexAccessor createAccessor() {
return new RTreeAccessor(this);
}
private class RTreeAccessor implements ITreeIndexAccessor {
private RTree rtree;
private RTreeOpContext ctx;
public RTreeAccessor(RTree rtree) {
this.rtree = rtree;
this.ctx = rtree.createOpContext();
}
@Override
public void insert(ITupleReference tuple) throws HyracksDataException, TreeIndexException, PageAllocationException {
ctx.reset(IndexOp.INSERT);
rtree.insert(tuple, ctx);
}
@Override
public void update(ITupleReference tuple) throws HyracksDataException, TreeIndexException {
ctx.reset(IndexOp.UPDATE);
rtree.update(tuple, ctx);
}
@Override
public void delete(ITupleReference tuple) throws HyracksDataException, TreeIndexException {
ctx.reset(IndexOp.DELETE);
rtree.delete(tuple, ctx);
}
@Override
public void search(ITreeIndexCursor cursor, ISearchPredicate searchPred) throws HyracksDataException,
TreeIndexException {
ctx.reset(IndexOp.SEARCH);
rtree.search(cursor, searchPred, ctx);
}
@Override
public void diskOrderScan(ITreeIndexCursor cursor) throws HyracksDataException {
ctx.reset(IndexOp.DISKORDERSCAN);
rtree.diskOrderScan(cursor, ctx);
}
}
}