Skip to content

Commit

Permalink
TreeNodeDynamicSize impl part 4 - splitLeaf
Browse files Browse the repository at this point in the history
Add possibility to defragment a leaf.
When removing keys we only mark them as dead.
If we can avoid splitting a leaf during insert by removing
those dead keys, defragmenting the leaf, we will do that instead.

Defragmenting is a no-op for fixed size implementation.

Explicit changes:
- (new method) TreeNode.defragmentLeaf
- TreeNode.doSplitLeaf takes responsibility of finding 'middle'
- TreeNode.leafOverflow return Overflow.{YES,NO,NEED_DEFRAG} instead of boolean
- PageAwareByteArrayCursor does not throw when writing zero bytes.
- (new method) PrimitiveIntStack.peek()
  • Loading branch information
burqen committed Jan 16, 2018
1 parent ba001a9 commit 87e77b2
Show file tree
Hide file tree
Showing 12 changed files with 670 additions and 123 deletions.
Expand Up @@ -108,4 +108,9 @@ private static int withTombstoneFlag( int keySize )
assert (keySize & FLAG_TOMBSTONE) == 0 : "Key size " + keySize + " is to large to fit tombstone.";
return keySize | FLAG_TOMBSTONE;
}

static int stripTombstone( int keySize )
{
return keySize & ~FLAG_TOMBSTONE;
}
}
Expand Up @@ -32,6 +32,8 @@
import static org.neo4j.index.internal.gbptree.StructurePropagation.KeyReplaceStrategy.REPLACE;
import static org.neo4j.index.internal.gbptree.StructurePropagation.UPDATE_MID_CHILD;
import static org.neo4j.index.internal.gbptree.StructurePropagation.UPDATE_RIGHT_CHILD;
import static org.neo4j.index.internal.gbptree.TreeNode.Overflow.NEED_DEFRAG;
import static org.neo4j.index.internal.gbptree.TreeNode.Overflow.YES;
import static org.neo4j.index.internal.gbptree.TreeNode.Type.INTERNAL;
import static org.neo4j.index.internal.gbptree.TreeNode.Type.LEAF;

Expand Down Expand Up @@ -555,13 +557,19 @@ private void insertInLeaf( PageCursor cursor, StructurePropagation<KEY> structur
createSuccessorIfNeeded( cursor, structurePropagation, UPDATE_MID_CHILD,
stableGeneration, unstableGeneration );

if ( bTreeNode.leafOverflow( cursor, keyCount, key, value ) )
TreeNode.Overflow overflow = bTreeNode.leafOverflow( cursor, keyCount, key, value );
if ( overflow == YES )
{
// Overflow, split leaf
splitLeaf( cursor, structurePropagation, key, value, keyCount, stableGeneration, unstableGeneration );
return;
}

if ( overflow == NEED_DEFRAG )
{
bTreeNode.defragmentLeaf( cursor );
}

// No overflow, insert key and value
bTreeNode.insertKeyValueAt( cursor, key, value, pos, keyCount );
TreeNode.setKeyCount( cursor, keyCount + 1 );
Expand Down Expand Up @@ -642,33 +650,21 @@ private void splitLeaf( PageCursor cursor, StructurePropagation<KEY> structurePr

// Position where newKey / newValue is to be inserted
int pos = positionOf( search( cursor, LEAF, newKey, readKey, keyCount ) );
int keyCountAfterInsert = keyCount + 1;
int middlePos = middle( keyCountAfterInsert );

structurePropagation.hasRightKeyInsert = true;
structurePropagation.midChild = current;
structurePropagation.rightChild = newRight;

if ( middlePos == pos )
{
layout.copyKey( newKey, structurePropagation.rightKey );
}
else
{
bTreeNode.keyAt( cursor, structurePropagation.rightKey, pos < middlePos ? middlePos - 1 : middlePos, LEAF );
}

try ( PageCursor rightCursor = cursor.openLinkedCursor( newRight ) )
{
// Initialize new right
TreeNode.goTo( rightCursor, "new right sibling in split", newRight );
bTreeNode.initializeLeaf( rightCursor, stableGeneration, unstableGeneration );
TreeNode.setRightSibling( rightCursor, oldRight, stableGeneration, unstableGeneration );
TreeNode.setLeftSibling( rightCursor, current, stableGeneration, unstableGeneration );
int rightKeyCount = keyCountAfterInsert - middlePos;

// Do split
bTreeNode.doSplitLeaf( cursor, keyCount, rightCursor, rightKeyCount, pos, newKey, newValue, middlePos );
bTreeNode.doSplitLeaf( cursor, keyCount, rightCursor, pos, newKey, newValue, structurePropagation );
}

// Update old right with new left sibling (newRight)
Expand Down
Expand Up @@ -72,6 +72,13 @@ enum Type
INTERNAL
}

enum Overflow
{
YES,
NO,
NEED_DEFRAG
}

// Shared between all node types: TreeNode and FreelistNode
static final int BYTE_POS_NODE_TYPE = 0;
static final byte NODE_TYPE_TREE_NODE = 1;
Expand Down Expand Up @@ -320,7 +327,12 @@ static void goTo( PageCursor cursor, String messageOnError, long nodeId )
* Will leaf overflow if inserting new key and value?
* @return true if leaf will overflow, else false.
*/
abstract boolean leafOverflow( PageCursor cursor, int currentKeyCount, KEY newKey, VALUE newValue );
abstract Overflow leafOverflow( PageCursor cursor, int currentKeyCount, KEY newKey, VALUE newValue );

/**
* Clean page from garbage to make room for further insert without having to split.
*/
abstract void defragmentLeaf( PageCursor cursor );

abstract boolean leafUnderflow( int keyCount );

Expand All @@ -329,15 +341,14 @@ static void goTo( PageCursor cursor, String messageOnError, long nodeId )
abstract boolean canMergeLeaves( int leftKeyCount, int rightKeyCount );

/**
* Performs the entry moving part of split in leaf.
* Calculate where split should be done and move entries between leaves participating in split.
*
* Keys and values from left are divide between left and right and the new key and value is inserted where it belongs.
*
* Key count is updated.
*/
abstract void doSplitLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, int insertPos,
KEY newKey,
VALUE newValue, int middlePos );
abstract void doSplitLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int insertPos, KEY newKey, VALUE newValue,
StructurePropagation<KEY> structurePropagation );

/**
* Performs the entry moving part of split in internal.
Expand Down

0 comments on commit 87e77b2

Please sign in to comment.