diff --git a/community/index/src/main/java/org/neo4j/index/internal/gbptree/InternalTreeLogic.java b/community/index/src/main/java/org/neo4j/index/internal/gbptree/InternalTreeLogic.java index df91f70545eec..d54fe1fa9171d 100644 --- a/community/index/src/main/java/org/neo4j/index/internal/gbptree/InternalTreeLogic.java +++ b/community/index/src/main/java/org/neo4j/index/internal/gbptree/InternalTreeLogic.java @@ -478,111 +478,31 @@ private void splitInternal( PageCursor cursor, StructurePropagation structu bTreeNode.keyAt( cursor, structurePropagation.rightKey, pos < middlePos ? middlePos - 1 : middlePos, INTERNAL ); } - // Update new right try ( PageCursor rightCursor = cursor.openLinkedCursor( newRight ) ) { + // Initialize new right TreeNode.goTo( rightCursor, "new right sibling in split", newRight ); TreeNode.initializeInternal( rightCursor, stableGeneration, unstableGeneration ); TreeNode.setRightSibling( rightCursor, oldRight, stableGeneration, unstableGeneration ); TreeNode.setLeftSibling( rightCursor, current, stableGeneration, unstableGeneration ); int rightKeyCount = keyCountAfterInsert - middlePos - 1; // -1 because don't keep prim key in internal - if ( pos < middlePos ) - { - // v-------v copy - // before key _,_,_,_,_,_,_,_,_,_ - // before child -,-,-,-,-,-,-,-,-,-,- - // insert key _,_,X,_,_,_,_,_,_,_,_ - // insert child -,-,-,x,-,-,-,-,-,-,-,- - // middle key ^ - - // children - cursor.copyTo( bTreeNode.keyOffset( middlePos ), rightCursor, bTreeNode.keyOffset( 0 ), - rightKeyCount * bTreeNode.keySize() ); - cursor.copyTo( bTreeNode.childOffset( middlePos ), rightCursor, bTreeNode.childOffset( 0 ), - (rightKeyCount + 1) * TreeNode.childSize() ); - } - else - { - // pos > middlePos - // v-v first copy - // v-v-v second copy - // before key _,_,_,_,_,_,_,_,_,_ - // before child -,-,-,-,-,-,-,-,-,-,- - // insert key _,_,_,_,_,_,_,X,_,_,_ - // insert child -,-,-,-,-,-,-,-,x,-,-,- - // middle key ^ - - // pos == middlePos - // first copy - // v-v-v-v-v second copy - // before key _,_,_,_,_,_,_,_,_,_ - // before child -,-,-,-,-,-,-,-,-,-,- - // insert key _,_,_,_,_,X,_,_,_,_,_ - // insert child -,-,-,-,-,-,x,-,-,-,-,- - // middle key ^ - - // Keys - int countBeforePos = pos - (middlePos + 1); - // ... first copy - if ( countBeforePos > 0 ) - { - cursor.copyTo( bTreeNode.keyOffset( middlePos + 1 ), rightCursor, bTreeNode.keyOffset( 0 ), - countBeforePos * bTreeNode.keySize() ); - } - // ... insert - if ( countBeforePos >= 0 ) - { - bTreeNode.insertKeyAt( rightCursor, newKey, countBeforePos, countBeforePos, INTERNAL ); - } - // ... second copy - int countAfterPos = keyCount - pos; - if ( countAfterPos > 0 ) - { - cursor.copyTo( bTreeNode.keyOffset( pos ), rightCursor, - bTreeNode.keyOffset( countBeforePos + 1 ), countAfterPos * bTreeNode.keySize() ); - } - - // Children - countBeforePos = pos - middlePos; - // ... first copy - if ( countBeforePos > 0 ) - { - // first copy - cursor.copyTo( bTreeNode.childOffset( middlePos + 1 ), rightCursor, bTreeNode.childOffset( 0 ), - countBeforePos * TreeNode.childSize() ); - } - // ... insert - bTreeNode.insertChildAt( rightCursor, newRightChild, countBeforePos, countBeforePos, - stableGeneration, unstableGeneration ); - // ... second copy - if ( countAfterPos > 0 ) - { - cursor.copyTo( bTreeNode.childOffset( pos + 1 ), rightCursor, - bTreeNode.childOffset( countBeforePos + 1 ), countAfterPos * TreeNode.childSize() ); - } - } - TreeNode.setKeyCount( rightCursor, rightKeyCount ); + // Do split + bTreeNode.doSplitInternal( cursor, keyCount, rightCursor, rightKeyCount, pos, newKey, newRightChild, middlePos, stableGeneration, + unstableGeneration ); } // Update old right with new left sibling (newRight) if ( TreeNode.isNode( oldRight ) ) { - TreeNode.goTo( cursor, "old right sibling", oldRight ); - TreeNode.setLeftSibling( cursor, newRight, stableGeneration, unstableGeneration ); - } - - // Update left node - // Move cursor back to left - TreeNode.goTo( cursor, "left", current ); - TreeNode.setKeyCount( cursor, middlePos ); - if ( pos < middlePos ) - { - bTreeNode.insertKeyAt( cursor, newKey, pos, middlePos - 1, INTERNAL ); - bTreeNode.insertChildAt( cursor, newRightChild, pos + 1, middlePos - 1, - stableGeneration, unstableGeneration ); + try ( PageCursor oldRightCursor = cursor.openLinkedCursor( oldRight ) ) + { + TreeNode.goTo( oldRightCursor, "old right sibling", oldRight ); + TreeNode.setLeftSibling( oldRightCursor, newRight, stableGeneration, unstableGeneration ); + } } + // Update left node with new right sibling TreeNode.setRightSibling( cursor, newRight, stableGeneration, unstableGeneration ); } @@ -656,7 +576,6 @@ private void splitLeaf( PageCursor cursor, StructurePropagation structurePr throws IOException { // To avoid moving cursor between pages we do all operations on left node first. - // Save data that needs transferring and then add it to right node. // UPDATE SIBLINGS // @@ -719,13 +638,6 @@ private void splitLeaf( PageCursor cursor, StructurePropagation structurePr int keyCountAfterInsert = keyCount + 1; int middlePos = middle( keyCountAfterInsert ); - // allKeysIncludingNewKey should now contain all keys in sorted order and - // allValuesIncludingNewValue should now contain all values in same order as corresponding keys - // and are ready to be split between left and newRight. - - // We now have everything we need to start working on newRight - // and everything that needs to be updated in left has been so. - structurePropagation.hasRightKeyInsert = true; structurePropagation.midChild = current; structurePropagation.rightChild = newRight; @@ -739,45 +651,17 @@ private void splitLeaf( PageCursor cursor, StructurePropagation structurePr bTreeNode.keyAt( cursor, structurePropagation.rightKey, pos < middlePos ? middlePos - 1 : middlePos, LEAF ); } - // Update new right try ( PageCursor rightCursor = cursor.openLinkedCursor( newRight ) ) { + // Initialize new right TreeNode.goTo( rightCursor, "new right sibling in split", newRight ); TreeNode.initializeLeaf( rightCursor, stableGeneration, unstableGeneration ); TreeNode.setRightSibling( rightCursor, oldRight, stableGeneration, unstableGeneration ); TreeNode.setLeftSibling( rightCursor, current, stableGeneration, unstableGeneration ); int rightKeyCount = keyCountAfterInsert - middlePos; - if ( pos < middlePos ) - { - // v-------v copy - // before _,_,_,_,_,_,_,_,_,_ - // insert _,_,_,X,_,_,_,_,_,_,_ - // middle ^ - copyKeysAndValues( cursor, middlePos - 1, rightCursor, 0, rightKeyCount ); - } - else - { - // v---v first copy - // v-v second copy - // before _,_,_,_,_,_,_,_,_,_ - // insert _,_,_,_,_,_,_,_,X,_,_ - // middle ^ - int countBeforePos = pos - middlePos; - if ( countBeforePos > 0 ) - { - // first copy - copyKeysAndValues( cursor, middlePos, rightCursor, 0, countBeforePos ); - } - bTreeNode.insertKeyValueAt( rightCursor, newKey, newValue, countBeforePos, countBeforePos ); - int countAfterPos = keyCount - pos; - if ( countAfterPos > 0 ) - { - // second copy - copyKeysAndValues( cursor, pos, rightCursor, countBeforePos + 1, countAfterPos ); - } - } - TreeNode.setKeyCount( rightCursor, rightKeyCount ); + // Do split + bTreeNode.doSplitLeaf( cursor, keyCount, rightCursor, rightKeyCount, pos, newKey, newValue, middlePos ); } // Update old right with new left sibling (newRight) @@ -791,23 +675,9 @@ private void splitLeaf( PageCursor cursor, StructurePropagation structurePr } // Update left child - // If pos < middle. Write shifted values to left node. Else, don't write anything. - if ( pos < middlePos ) - { - bTreeNode.insertKeyValueAt( cursor, newKey, newValue, pos, middlePos - 1 ); - } - TreeNode.setKeyCount( cursor, middlePos ); TreeNode.setRightSibling( cursor, newRight, stableGeneration, unstableGeneration ); } - private void copyKeysAndValues( PageCursor fromCursor, int fromPos, PageCursor toCursor, int toPos, int count ) - { - fromCursor.copyTo( bTreeNode.keyOffset( fromPos ), toCursor, bTreeNode.keyOffset( toPos ), - count * bTreeNode.keySize() ); - fromCursor.copyTo( bTreeNode.valueOffset( fromPos ), toCursor, bTreeNode.valueOffset( toPos ), - count * bTreeNode.valueSize() ); - } - /** * Remove given {@code key} and associated value from tree if it exists. The removed value will be stored in * provided {@code into} which will be returned for convenience. @@ -1169,7 +1039,7 @@ private void underflowInLeaf( PageCursor cursor, StructurePropagation struc { createSuccessorIfNeeded( leftSiblingCursor, structurePropagation, StructurePropagation.UPDATE_LEFT_CHILD, stableGeneration, unstableGeneration ); - rebalanceLeaf( cursor, leftSiblingCursor, structurePropagation, keyCount, leftSiblingKeyCount ); + rebalanceLeaf( leftSiblingCursor, leftSiblingKeyCount, cursor, keyCount, structurePropagation ); } else { @@ -1260,11 +1130,8 @@ private void mergeFromLeftSiblingLeaf( PageCursor cursor, PageCursor leftSibling private void merge( PageCursor leftSiblingCursor, int leftSiblingKeyCount, PageCursor rightSiblingCursor, int rightSiblingKeyCount, long stableGeneration, long unstableGeneration ) throws IOException { - // Push keys and values in right sibling to the right - bTreeNode.insertKeyValueSlotsAt( rightSiblingCursor, 0, leftSiblingKeyCount, rightSiblingKeyCount ); + bTreeNode.moveKeyValuesFromLeftToRight( leftSiblingCursor, leftSiblingKeyCount, rightSiblingCursor, rightSiblingKeyCount, 0 ); - // Move keys and values from left sibling to right sibling - copyKeysAndValues( leftSiblingCursor, 0, rightSiblingCursor, 0, leftSiblingKeyCount ); TreeNode.setKeyCount( rightSiblingCursor, rightSiblingKeyCount + leftSiblingKeyCount ); // Update successor of left sibling to be right sibling @@ -1276,25 +1143,22 @@ private void merge( PageCursor leftSiblingCursor, int leftSiblingKeyCount, PageC idProvider.releaseId( stableGeneration, unstableGeneration, leftSiblingCursor.getCurrentPageId() ); } - private void rebalanceLeaf( PageCursor cursor, PageCursor leftSiblingCursor, - StructurePropagation structurePropagation, int keyCount, int leftSiblingKeyCount ) + private void rebalanceLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, + StructurePropagation structurePropagation ) { - int totalKeyCount = keyCount + leftSiblingKeyCount; - int keyCountInLeftSiblingAfterRebalance = totalKeyCount / 2; - int numberOfKeysToMove = leftSiblingKeyCount - keyCountInLeftSiblingAfterRebalance; + int totalKeyCount = rightKeyCount + leftKeyCount; + int moveFromPosition = totalKeyCount / 2; + int numberOfKeysToMove = leftKeyCount - moveFromPosition; - // Push keys and values in right sibling to the right - bTreeNode.insertKeyValueSlotsAt( cursor, 0, numberOfKeysToMove, keyCount ); + bTreeNode.moveKeyValuesFromLeftToRight( leftCursor, leftKeyCount, rightCursor, rightKeyCount, moveFromPosition ); - // Move keys and values from left sibling to right sibling - copyKeysAndValues( leftSiblingCursor, keyCountInLeftSiblingAfterRebalance, cursor, 0, numberOfKeysToMove ); - TreeNode.setKeyCount( cursor, keyCount + numberOfKeysToMove ); - TreeNode.setKeyCount( leftSiblingCursor, leftSiblingKeyCount - numberOfKeysToMove ); + TreeNode.setKeyCount( leftCursor, moveFromPosition ); + TreeNode.setKeyCount( rightCursor, rightKeyCount + numberOfKeysToMove ); // Propagate change structurePropagation.hasLeftKeyReplace = true; structurePropagation.keyReplaceStrategy = REPLACE; - bTreeNode.keyAt( cursor, structurePropagation.leftKey, 0, LEAF ); + bTreeNode.keyAt( rightCursor, structurePropagation.leftKey, 0, LEAF ); } /** diff --git a/community/index/src/main/java/org/neo4j/index/internal/gbptree/TreeNode.java b/community/index/src/main/java/org/neo4j/index/internal/gbptree/TreeNode.java index 1a62fd410febe..b7811f1dc2cf4 100644 --- a/community/index/src/main/java/org/neo4j/index/internal/gbptree/TreeNode.java +++ b/community/index/src/main/java/org/neo4j/index/internal/gbptree/TreeNode.java @@ -26,6 +26,7 @@ import static org.neo4j.index.internal.gbptree.GenerationSafePointerPair.NO_LOGICAL_POS; import static org.neo4j.index.internal.gbptree.GenerationSafePointerPair.read; +import static org.neo4j.index.internal.gbptree.TreeNode.Type.INTERNAL; /** * Methods to manipulate single tree node such as set and get header fields, @@ -393,12 +394,12 @@ int leafMaxKeyCount() // HELPERS - int keyOffset( int pos ) + private int keyOffset( int pos ) { return HEADER_LENGTH + pos * keySize; } - int valueOffset( int pos ) + private int valueOffset( int pos ) { return HEADER_LENGTH + leafMaxKeyCount * keySize + pos * valueSize; } @@ -413,17 +414,17 @@ static boolean isNode( long node ) return GenerationSafePointerPair.pointer( node ) != NO_NODE_FLAG; } - int keySize() + private int keySize() { return keySize; } - int valueSize() + private int valueSize() { return valueSize; } - static int childSize() + private static int childSize() { return SIZE_PAGE_REFERENCE; } @@ -445,4 +446,157 @@ public String toString() return "TreeNode[pageSize:" + pageSize + ", internalMax:" + internalMaxKeyCount + ", leafMax:" + leafMaxKeyCount + ", keySize:" + keySize + ", valueSize:" + valueSize + "]"; } + + /* SPLIT, MERGE AND REBALANCE */ + + /** + * Performs the entry moving part of split in leaf. + * + * 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. + */ + void doSplitLeaf( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, int insertPos, KEY newKey, + VALUE newValue, int middlePos ) + { + if ( insertPos < middlePos ) + { + // v-------v copy + // before _,_,_,_,_,_,_,_,_,_ + // insert _,_,_,X,_,_,_,_,_,_,_ + // middle ^ + copyKeysAndValues( leftCursor, middlePos - 1, rightCursor, 0, rightKeyCount ); + insertKeyValueAt( leftCursor, newKey, newValue, insertPos, middlePos - 1 ); + } + else + { + // v---v first copy + // v-v second copy + // before _,_,_,_,_,_,_,_,_,_ + // insert _,_,_,_,_,_,_,_,X,_,_ + // middle ^ + int countBeforePos = insertPos - middlePos; + if ( countBeforePos > 0 ) + { + // first copy + copyKeysAndValues( leftCursor, middlePos, rightCursor, 0, countBeforePos ); + } + insertKeyValueAt( rightCursor, newKey, newValue, countBeforePos, countBeforePos ); + int countAfterPos = leftKeyCount - insertPos; + if ( countAfterPos > 0 ) + { + // second copy + copyKeysAndValues( leftCursor, insertPos, rightCursor, countBeforePos + 1, countAfterPos ); + } + } + TreeNode.setKeyCount( leftCursor, middlePos ); + TreeNode.setKeyCount( rightCursor, rightKeyCount ); + } + + /** + * Performs the entry moving part of split in internal. + * + * Keys and children from left is divided between left and right and the new key and child is inserted where it belongs. + * + * Key count is updated. + */ + void doSplitInternal( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, int insertPos, KEY newKey, + long newRightChild, int middlePos, long stableGeneration, long unstableGeneration ) + { + if ( insertPos < middlePos ) + { + // v-------v copy + // before key _,_,_,_,_,_,_,_,_,_ + // before child -,-,-,-,-,-,-,-,-,-,- + // insert key _,_,X,_,_,_,_,_,_,_,_ + // insert child -,-,-,x,-,-,-,-,-,-,-,- + // middle key ^ + + leftCursor.copyTo( keyOffset( middlePos ), rightCursor, keyOffset( 0 ), rightKeyCount * keySize() ); + leftCursor.copyTo( childOffset( middlePos ), rightCursor, childOffset( 0 ), (rightKeyCount + 1) * childSize() ); + insertKeyAt( leftCursor, newKey, insertPos, middlePos - 1, INTERNAL ); + insertChildAt( leftCursor, newRightChild, insertPos + 1, middlePos - 1, stableGeneration, unstableGeneration ); + } + else + { + // pos > middlePos + // v-v first copy + // v-v-v second copy + // before key _,_,_,_,_,_,_,_,_,_ + // before child -,-,-,-,-,-,-,-,-,-,- + // insert key _,_,_,_,_,_,_,X,_,_,_ + // insert child -,-,-,-,-,-,-,-,x,-,-,- + // middle key ^ + + // pos == middlePos + // first copy + // v-v-v-v-v second copy + // before key _,_,_,_,_,_,_,_,_,_ + // before child -,-,-,-,-,-,-,-,-,-,- + // insert key _,_,_,_,_,X,_,_,_,_,_ + // insert child -,-,-,-,-,-,x,-,-,-,-,- + // middle key ^ + + // Keys + int countBeforePos = insertPos - (middlePos + 1); + // ... first copy + if ( countBeforePos > 0 ) + { + leftCursor.copyTo( keyOffset( middlePos + 1 ), rightCursor, keyOffset( 0 ), countBeforePos * keySize() ); + } + // ... insert + if ( countBeforePos >= 0 ) + { + insertKeyAt( rightCursor, newKey, countBeforePos, countBeforePos, INTERNAL ); + } + // ... second copy + int countAfterPos = leftKeyCount - insertPos; + if ( countAfterPos > 0 ) + { + leftCursor.copyTo( keyOffset( insertPos ), rightCursor, keyOffset( countBeforePos + 1 ), countAfterPos * keySize() ); + } + + // Children + countBeforePos = insertPos - middlePos; + // ... first copy + if ( countBeforePos > 0 ) + { + // first copy + leftCursor.copyTo( childOffset( middlePos + 1 ), rightCursor, childOffset( 0 ), countBeforePos * childSize() ); + } + // ... insert + insertChildAt( rightCursor, newRightChild, countBeforePos, countBeforePos, stableGeneration, unstableGeneration ); + // ... second copy + if ( countAfterPos > 0 ) + { + leftCursor.copyTo( childOffset( insertPos + 1 ), rightCursor, childOffset( countBeforePos + 1 ), + countAfterPos * childSize() ); + } + } + TreeNode.setKeyCount( leftCursor, middlePos ); + TreeNode.setKeyCount( rightCursor, rightKeyCount ); + } + + /** + * Move all rightmost keys and values in left node from given position to right node. + * + * Key count is NOT updated. + */ + void moveKeyValuesFromLeftToRight( PageCursor leftCursor, int leftKeyCount, PageCursor rightCursor, int rightKeyCount, + int fromPosInLeftNode ) + { + int numberOfKeysToMove = leftKeyCount - fromPosInLeftNode; + + // Push keys and values in right sibling to the right + insertKeyValueSlotsAt( rightCursor, 0, numberOfKeysToMove, rightKeyCount ); + + // Move keys and values from left sibling to right sibling + copyKeysAndValues( leftCursor, fromPosInLeftNode, rightCursor, 0, numberOfKeysToMove ); + } + + private void copyKeysAndValues( PageCursor fromCursor, int fromPos, PageCursor toCursor, int toPos, int count ) + { + fromCursor.copyTo( keyOffset( fromPos ), toCursor, keyOffset( toPos ), count * keySize() ); + fromCursor.copyTo( valueOffset( fromPos ), toCursor, valueOffset( toPos ),count * valueSize() ); + } }