Skip to content

Commit

Permalink
GBPTree - Merge implemented
Browse files Browse the repository at this point in the history
An underflow in some node has occured and we want to merge
node L into node R. L is left sibling to R and vice versa.
It is done like this:

Phase 1 (Move data)
1. Left sibling of L is LL. Set right sibling of LL to be R and
   left sibling of R to be LL. Effectively removing L from the leaf chain.
2. Set new generation of L to be R.
3. Add L to freelist.
4. Copy all keys and values from L to R.
5. Update partent pointers to match changes, new pointer to both L and R.

Phase 2 (Update splitter key in internal nodes)
   Parent of L and R (potentially same internal node) now both hold pointer
to the same leaf, R. This is ok for now, because it just means we have to
different paths to the same leaf. We do however want to remove one of those paths
together with some key to not store more than what we need among the
internal nodes.
   L and R are part of different subtrees, subL and subR.
(Note that it is possible that subL and subR only contain L och R respectively.)
Because L and R are sibling, we know that subL and subR are splitted by exactly
one key, the 'splitter', which is located in the closest common parent of
subL and subR. subL is located just to the left of splitter and subR just to the right.
splitter should be replaced by the key that previously would guide a search towards L.
This key is the rightmost key in subL. If no such key can be found, if subL only
contains L or if none of the internal nodes in subL has any keys, then splitter
is removed together with subL.
   So:
6. Bubble up rightmost key from subL.
7. Every internal node on the bubble path that contains zero keys can be
   added to freelist.
8. Replace splitter with bubble key or remove if no bubble key could be found.
9. If root end up with zero keys, shrink the tree.
  • Loading branch information
burqen committed Feb 14, 2017
1 parent 68769c6 commit 7bf66b2
Show file tree
Hide file tree
Showing 9 changed files with 392 additions and 60 deletions.
Expand Up @@ -795,7 +795,7 @@ private class SingleWriter implements Writer<KEY,VALUE>

SingleWriter( InternalTreeLogic<KEY,VALUE> treeLogic )
{
this.structurePropagation = new StructurePropagation<>( layout.newKey(), layout.newKey() );
this.structurePropagation = new StructurePropagation<>( layout.newKey(), layout.newKey(), layout.newKey() );
this.treeLogic = treeLogic;
}

Expand Down Expand Up @@ -843,8 +843,9 @@ else if ( structurePropagation.hasMidChildUpdate )
checkOutOfBounds( cursor );
}

private void setRoot( long rootId )
private void setRoot( long rootPointer )
{
long rootId = GenSafePointerPair.pointer( rootPointer );
GBPTree.this.setRoot( rootId, unstableGeneration );
treeLogic.initialize( cursor );
}
Expand Down

Large diffs are not rendered by default.

Expand Up @@ -560,7 +560,7 @@ else if ( !saneRead() )
continue; // in the read loop above so that we can continue reading from next sibling
}
}
else if ( insideEndRange() )
else if ( 0 <= pos && pos < keyCount && insideEndRange() )
{
if ( isResultKey() )
{
Expand Down
Expand Up @@ -59,20 +59,26 @@
*/
class StructurePropagation<KEY>
{
// First level of updates, used when bubbling up the tree
boolean hasLeftChildUpdate;
boolean hasRightChildUpdate;
boolean hasMidChildUpdate;
boolean hasRightKeyInsert;
boolean hasLeftKeyReplace;
boolean hasRightKeyReplace;
final KEY leftKey;
final KEY rightKey;
final KEY bubbleKey;
long leftChild;
long midChild;
long rightChild;
KeyReplaceStrategy keyReplaceStrategy;

StructurePropagation( KEY leftKey, KEY rightKey )
StructurePropagation( KEY leftKey, KEY rightKey, KEY bubbleKey )
{
this.leftKey = leftKey;
this.rightKey = rightKey;
this.bubbleKey = bubbleKey;
}

/**
Expand All @@ -81,9 +87,11 @@ class StructurePropagation<KEY>
void clear()
{
hasLeftChildUpdate = false;
hasRightChildUpdate = false;
hasMidChildUpdate = false;
hasRightKeyInsert = false;
hasLeftKeyReplace = false;
hasRightKeyReplace = false;
}

interface StructureUpdate
Expand All @@ -100,4 +108,14 @@ interface StructureUpdate
sp.hasMidChildUpdate = true;
sp.midChild = childId;
};

static final StructureUpdate UPDATE_RIGHT_CHILD = ( sp, childId ) -> {
sp.hasRightChildUpdate = true;
sp.rightChild = childId;
};

enum KeyReplaceStrategy
{
REPLACE, BUBBLE
}
}
Expand Up @@ -247,10 +247,10 @@ void removeKeyAt( PageCursor cursor, int pos, int keyCount )
removeSlotAt( cursor, pos, keyCount, keyOffset( 0 ), keySize );
}

private void removeSlotAt( PageCursor cursor, int pos, int keyCount, int baseOffset, int itemSize )
private void removeSlotAt( PageCursor cursor, int pos, int itemCount, int baseOffset, int itemSize )
{
for ( int posToMoveLeft = pos + 1, offset = baseOffset + posToMoveLeft * itemSize;
posToMoveLeft < keyCount; posToMoveLeft++, offset += itemSize )
posToMoveLeft < itemCount; posToMoveLeft++, offset += itemSize )
{
cursor.copyTo( offset, cursor, offset - itemSize, itemSize );
}
Expand Down Expand Up @@ -299,6 +299,11 @@ void insertChildAt( PageCursor cursor, long child, int pos, int keyCount,
setChildAt( cursor, child, pos, stableGeneration, unstableGeneration );
}

void removeChildAt( PageCursor cursor, int pos, int keyCount )
{
removeSlotAt( cursor, pos, keyCount + 1, childOffset( 0 ), childSize() );
}

void setChildAt( PageCursor cursor, long child, int pos, long stableGeneration, long unstableGeneration )
{
cursor.setOffset( childOffset( pos ) );
Expand Down
Expand Up @@ -88,7 +88,8 @@ public void shouldDetectUnusedPages() throws Exception
cursor.next( idProvider.acquireNewId( stableGeneration, unstableGeneration ) );
node.initializeLeaf( cursor, stableGeneration, unstableGeneration );
logic.initialize( cursor );
StructurePropagation<MutableLong> structure = new StructurePropagation<>( layout.newKey(), layout.newKey() );
StructurePropagation<MutableLong> structure = new StructurePropagation<>( layout.newKey(), layout.newKey(),
layout.newKey() );
MutableLong key = layout.newKey();
for ( int g = 0, k = 0; g < 3; g++ )
{
Expand Down
Expand Up @@ -21,7 +21,6 @@

import org.apache.commons.lang3.mutable.MutableLong;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
Expand Down Expand Up @@ -138,8 +137,6 @@ public void shouldReadForwardCorrectlyWithConcurrentRemove() throws Throwable
shouldReadCorrectlyWithConcurrentUpdates( testCoordinator );
}

@Ignore( "Test will leave tree nodes empty because merge is still missing in implementation. " +
"When seeking backwards this will cause infinite loops in SeekCursor." )
@Test
public void shouldReadBackwardCorrectlyWithConcurrentRemove() throws Throwable
{
Expand Down Expand Up @@ -331,7 +328,7 @@ private List<UpdateOperation> generateUpdatesForNextIteration()
}
else if ( toRemove.isEmpty() )
{
operation = new WriteOperation( toAdd.poll() );
operation = new PutOperation( toAdd.poll() );
}
else
{
Expand All @@ -342,7 +339,7 @@ else if ( toRemove.isEmpty() )
}
else
{
operation = new WriteOperation( toAdd.poll() );
operation = new PutOperation( toAdd.poll() );
}
}
updateOperations.add( operation );
Expand Down Expand Up @@ -394,9 +391,9 @@ private abstract class UpdateOperation
abstract boolean isInsert();
}

private class WriteOperation extends UpdateOperation
private class PutOperation extends UpdateOperation
{
WriteOperation( long key )
PutOperation( long key )
{
super( key );
}
Expand Down
Expand Up @@ -71,7 +71,7 @@ public class InternalTreeLogicTest
private final MutableLong readKey = new MutableLong();
private final MutableLong readValue = new MutableLong();
private final StructurePropagation<MutableLong> structurePropagation = new StructurePropagation<>(
layout.newKey(), layout.newKey() );
layout.newKey(), layout.newKey(), layout.newKey() );

private static long stableGen = GenSafePointer.MIN_GENERATION;
private static long unstableGen = stableGen + 1;
Expand Down Expand Up @@ -913,15 +913,23 @@ public void shouldCreateNewVersionWhenRemoveInStableLeaf() throws Exception
initialize();
long targetLastId = id.lastId() + 3; // 2 splits and 1 new allocated root
long i = 0;
for ( ; id.lastId() < targetLastId; i++ )
for ( ; id.lastId() < targetLastId; i += 2 )
{
insert( i, i );
}
goTo( readCursor, rootId );
assertEquals( 2, keyCount() );

long leftChild = childAt( readCursor, 0, stableGen, unstableGen );
long middleChild = childAt( readCursor, 1, stableGen, unstableGen );
long rightChild = childAt( readCursor, 2, stableGen, unstableGen );

// add some more keys to middleChild to not have remove trigger a merge
goTo( readCursor, middleChild );
Long firstKeyInMiddleChild = keyAt( 0 );
insert( firstKeyInMiddleChild + 1, firstKeyInMiddleChild + 1 );
goTo( readCursor, rootId );

assertSiblings( leftChild, middleChild, rightChild );

// WHEN
Expand Down
Expand Up @@ -66,7 +66,7 @@ public long getAsLong()
private final TreeNode<MutableLong,MutableLong> node = new TreeNode<>( PAGE_SIZE, layout );
private final InternalTreeLogic<MutableLong,MutableLong> treeLogic = new InternalTreeLogic<>( id, node, layout );
private final StructurePropagation<MutableLong> structurePropagation =
new StructurePropagation<>( layout.newKey(), layout.newKey() );
new StructurePropagation<>( layout.newKey(), layout.newKey(), layout.newKey() );
private final PageAwareByteArrayCursor cursor = new PageAwareByteArrayCursor( PAGE_SIZE );
private final int maxKeyCount = node.leafMaxKeyCount();

Expand Down

0 comments on commit 7bf66b2

Please sign in to comment.