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."; assert (keySize & FLAG_TOMBSTONE) == 0 : "Key size " + keySize + " is to large to fit tombstone.";
return keySize | FLAG_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.KeyReplaceStrategy.REPLACE;
import static org.neo4j.index.internal.gbptree.StructurePropagation.UPDATE_MID_CHILD; 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.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.INTERNAL;
import static org.neo4j.index.internal.gbptree.TreeNode.Type.LEAF; 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, createSuccessorIfNeeded( cursor, structurePropagation, UPDATE_MID_CHILD,
stableGeneration, unstableGeneration ); stableGeneration, unstableGeneration );


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


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

// No overflow, insert key and value // No overflow, insert key and value
bTreeNode.insertKeyValueAt( cursor, key, value, pos, keyCount ); bTreeNode.insertKeyValueAt( cursor, key, value, pos, keyCount );
TreeNode.setKeyCount( cursor, keyCount + 1 ); 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 // Position where newKey / newValue is to be inserted
int pos = positionOf( search( cursor, LEAF, newKey, readKey, keyCount ) ); int pos = positionOf( search( cursor, LEAF, newKey, readKey, keyCount ) );
int keyCountAfterInsert = keyCount + 1;
int middlePos = middle( keyCountAfterInsert );


structurePropagation.hasRightKeyInsert = true; structurePropagation.hasRightKeyInsert = true;
structurePropagation.midChild = current; structurePropagation.midChild = current;
structurePropagation.rightChild = newRight; 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 ) ) try ( PageCursor rightCursor = cursor.openLinkedCursor( newRight ) )
{ {
// Initialize new right // Initialize new right
TreeNode.goTo( rightCursor, "new right sibling in split", newRight ); TreeNode.goTo( rightCursor, "new right sibling in split", newRight );
bTreeNode.initializeLeaf( rightCursor, stableGeneration, unstableGeneration ); bTreeNode.initializeLeaf( rightCursor, stableGeneration, unstableGeneration );
TreeNode.setRightSibling( rightCursor, oldRight, stableGeneration, unstableGeneration ); TreeNode.setRightSibling( rightCursor, oldRight, stableGeneration, unstableGeneration );
TreeNode.setLeftSibling( rightCursor, current, stableGeneration, unstableGeneration ); TreeNode.setLeftSibling( rightCursor, current, stableGeneration, unstableGeneration );
int rightKeyCount = keyCountAfterInsert - middlePos;


// Do split // 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) // Update old right with new left sibling (newRight)
Expand Down
Expand Up @@ -72,6 +72,13 @@ enum Type
INTERNAL INTERNAL
} }


enum Overflow
{
YES,
NO,
NEED_DEFRAG
}

// Shared between all node types: TreeNode and FreelistNode // Shared between all node types: TreeNode and FreelistNode
static final int BYTE_POS_NODE_TYPE = 0; static final int BYTE_POS_NODE_TYPE = 0;
static final byte NODE_TYPE_TREE_NODE = 1; 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? * Will leaf overflow if inserting new key and value?
* @return true if leaf will overflow, else false. * @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 ); 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 ); 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. * 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. * Key count is updated.
*/ */
abstract void doSplitLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, int insertPos, abstract void doSplitLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int insertPos, KEY newKey, VALUE newValue,
KEY newKey, StructurePropagation<KEY> structurePropagation );
VALUE newValue, int middlePos );


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

0 comments on commit 87e77b2

Please sign in to comment.