diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/GenSafePointerPair.java b/community/index/src/main/java/org/neo4j/index/gbptree/GenSafePointerPair.java
index 322c3cab0a8c7..111657d5fdee5 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/GenSafePointerPair.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/GenSafePointerPair.java
@@ -262,71 +262,8 @@ public static long write( PageCursor cursor, long pointer, long stableGeneration
GenSafePointer.write( cursor, unstableGeneration, pointer );
}
return writeResult;
-
-// TODO ANOTHER APPROACH, keep here for reference and pursuit later
-// // Select correct slot
-// boolean writeToSlotA;
-//
-// boolean aIsValid = correctChecksumA && (generationA <= stableGeneration || generationA == unstableGeneration);
-// boolean bIsValid = correctChecksumB && (generationB <= stableGeneration || generationB == unstableGeneration);
-//
-// // Failure cases
-// // - both invalid
-// if ( !aIsValid && !bIsValid )
-// {
-// return false;
-// }
-// // - both valid and same generation, but not empty
-// if ( aIsValid && bIsValid && generationA == generationB && generationA >= MIN_GENERATION )
-// {
-// return false;
-// }
-//
-// // Prioritized selection
-// // - one with unstable generation
-// if ( (aIsValid && generationA == unstableGeneration) || (bIsValid && generationB == unstableGeneration) )
-// {
-// writeToSlotA = aIsValid && generationA == unstableGeneration;
-// }
-// // - exactly one invalid
-// else if ( !aIsValid || !bIsValid )
-// {
-// writeToSlotA = !aIsValid;
-// }
-// // - empty
-// else if ( isEmpty( generationA, pointerA ) || isEmpty( generationB, pointerB ) )
-// {
-// writeToSlotA = isEmpty( generationA, pointerA );
-// }
-// // - lowest generation
-// else
-// {
-// writeToSlotA = generationA < generationB;
-// }
-//
-// // And write
-// int writeOffset = writeToSlotA ? offset : offset + SIZE;
-// cursor.setOffset( writeOffset );
-// GenSafePointer.write( cursor, unstableGeneration, pointer );
-// return true;
}
- // All different ordered combinations of pointer states and if they are considered to be an ok state to see
- // STABLE,STABLE - OK if different generation
- // STABLE,UNSTABLE - OK
- // STABLE,CRASH - OK
- // STABLE,BROKEN - OK
- // STABLE,EMPTY - OK
- // UNSTABLE,UNSTABLE - NOT OK
- // UNSTABLE,CRASH - NOT OK
- // UNSTABLE,BROKEN - NOT OK
- // UNSTABLE,EMPTY - OK
- // CRASH,CRASH - NOT OK
- // CRASH,BROKEN - NOT OK
- // CRASH,EMPTY - NOT OK
- // BROKEN,BROKEN - NOT OK
- // BROKEN,EMPTY - NOT OK
- // EMPTY,EMPTY - OK if writing, not if reading
private static long writeResult( byte pointerStateA, byte pointerStateB, long generationA, long generationB )
{
if ( pointerStateA == STABLE )
@@ -387,6 +324,23 @@ private static long generationState( long generationA, long generationB )
return generationA > generationB ? FLAG_GEN_A_BIG : generationB > generationA ? FLAG_GEN_B_BIG : FLAG_GEN_EQUAL;
}
+ /**
+ * Pointer state of a GSP (generation, pointer, checksum). Can be any of:
+ *
+ * - {@link #STABLE}
+ * - {@link #UNSTABLE}
+ * - {@link #CRASH}
+ * - {@link #BROKEN}
+ * - {@link #EMPTY}
+ *
+ *
+ * @param stableGeneration stable generation.
+ * @param unstableGeneration unstable generation.
+ * @param generation GSP generation.
+ * @param pointer GSP pointer.
+ * @param checksumIsCorrect whether or not GSP checksum matches checksum of {@code generation} and {@code pointer}.
+ * @return one of the available pointer states.
+ */
static byte pointerState( long stableGeneration, long unstableGeneration,
long generation, long pointer, boolean checksumIsCorrect )
{
@@ -498,6 +452,12 @@ else if ( bits == FLAG_GEN_B_BIG )
}
}
+ /**
+ * Name of the provided {@code pointerState} gotten from {@link #pointerState(long, long, long, long, boolean)}.
+ *
+ * @param pointerState pointer state to get name for.
+ * @return name of {@code pointerState}.
+ */
static String pointerStateName( byte pointerState )
{
switch ( pointerState )
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/Generation.java b/community/index/src/main/java/org/neo4j/index/gbptree/Generation.java
index b0946dda3f7ba..93d6632999c6e 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/Generation.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/Generation.java
@@ -32,6 +32,13 @@ class Generation
private static final long UNSTABLE_GENERATION_MASK = 0xFFFFFFFFL;
private static final int STABLE_GENERATION_SHIFT = Integer.SIZE;
+ /**
+ * Takes one stable and one unstable generation (both unsigned ints) and crams them into one {@code long}.
+ *
+ * @param stableGeneration stable generation.
+ * @param unstableGeneration unstable generation.
+ * @return the two generation numbers as one {@code long}.
+ */
public static long generation( long stableGeneration, long unstableGeneration )
{
GenSafePointer.assertGenerationOnWrite( stableGeneration );
@@ -40,13 +47,25 @@ public static long generation( long stableGeneration, long unstableGeneration )
return (stableGeneration << STABLE_GENERATION_SHIFT) | unstableGeneration;
}
- public static long unstableGeneration( long rawGeneration )
+ /**
+ * Extracts and returns unstable generation from generation {@code long}.
+ *
+ * @param generation generation variable containing both stable and unstable generations.
+ * @return unstable generation from generation.
+ */
+ public static long unstableGeneration( long generation )
{
- return rawGeneration & UNSTABLE_GENERATION_MASK;
+ return generation & UNSTABLE_GENERATION_MASK;
}
- public static long stableGeneration( long rawGeneration )
+ /**
+ * Extracts and returns stable generation from generation {@code long}.
+ *
+ * @param generation generation variable containing both stable and unstable generations.
+ * @return stable generation from generation.
+ */
+ public static long stableGeneration( long generation )
{
- return rawGeneration >>> STABLE_GENERATION_SHIFT;
+ return generation >>> STABLE_GENERATION_SHIFT;
}
}
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/IdProvider.java b/community/index/src/main/java/org/neo4j/index/gbptree/IdProvider.java
index 4029f9b1a736d..f101897692985 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/IdProvider.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/IdProvider.java
@@ -21,13 +21,33 @@
import java.io.IOException;
+import org.neo4j.io.pagecache.PageCursor;
+
/**
* Provide tree node (page) ids which can be used for storing tree node data.
* Bytes on returned page ids must be empty (all zeros).
*/
interface IdProvider
{
+ /**
+ * Acquires a page id, guaranteed to currently not be used. The bytes on the page at this id
+ * are all guaranteed to be zero at the point of returning from this method.
+ *
+ * @param stableGeneration current stable generation.
+ * @param unstableGeneration current unstable generation.
+ * @return page id guaranteed to current not be used and whose bytes are all zeros.
+ * @throws IOException on {@link PageCursor} error.
+ */
long acquireNewId( long stableGeneration, long unstableGeneration ) throws IOException;
+ /**
+ * Releases a page id which has previously been used, but isn't anymore, effectively allowing
+ * it to be reused and returned from {@link #acquireNewId(long, long)}.
+ *
+ * @param stableGeneration current stable generation.
+ * @param unstableGeneration current unstable generation.
+ * @param id page id to release.
+ * @throws IOException on {@link PageCursor} error.
+ */
void releaseId( long stableGeneration, long unstableGeneration, long id ) throws IOException;
}
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/KeySearch.java b/community/index/src/main/java/org/neo4j/index/gbptree/KeySearch.java
index 629fc2b6e2a81..bd69f17b78f57 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/KeySearch.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/KeySearch.java
@@ -23,6 +23,9 @@
import org.neo4j.io.pagecache.PageCursor;
+/**
+ * Methods for (binary-)searching keys in a tree node.
+ */
class KeySearch
{
private static final int POSITION_MASK = 0x7FFFFFFF;
@@ -45,8 +48,11 @@ class KeySearch
* @param key KEY to search for
* @param readKey KEY to use as temporary storage during calculation.
* @param keyCount number of keys in node when starting search
- * @return first position i for which bTreeNode.keyComparator().compare( key, bTreeNode.keyAt( i ) <= 0,
- * or keyCount if no such key exists.
+ * @return search result where least significant 31 bits are first position i for which
+ * bTreeNode.keyComparator().compare( key, bTreeNode.keyAt( i ) <= 0, or keyCount if no such key exists.
+ * highest bit (sign bit) says whether or not the exact key was found in the node, if so set to 1, otherwise 0.
+ * To extract position from the returned search result, then use {@link #positionOf(int)}.
+ * To extract whether or not the exact key was found, then use {@link #isHit(int)}.
*/
static int search( PageCursor cursor, TreeNode bTreeNode, KEY key,
KEY readKey, int keyCount )
@@ -115,11 +121,24 @@ private static int searchResult( int pos, boolean hit )
return (pos & POSITION_MASK) | ((hit ? 1 : 0) << 31);
}
+ /**
+ * Extracts the position from a search result from {@link #search(PageCursor, TreeNode, Object, Object, int)}.
+ *
+ * @param searchResult search result from {@link #search(PageCursor, TreeNode, Object, Object, int)}.
+ * @return position of the search result.
+ */
static int positionOf( int searchResult )
{
return searchResult & POSITION_MASK;
}
+ /**
+ * Extracts whether or not the searched key was found from search result from
+ * {@link #search(PageCursor, TreeNode, Object, Object, int)}.
+ *
+ * @param searchResult search result form {@link #search(PageCursor, TreeNode, Object, Object, int)}.
+ * @return whether or not the searched key was found.
+ */
static boolean isHit( int searchResult )
{
return (searchResult & HIT_MASK) != 0;
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/PointerChecking.java b/community/index/src/main/java/org/neo4j/index/gbptree/PointerChecking.java
index 0b9937bf452d7..8071459241739 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/PointerChecking.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/PointerChecking.java
@@ -21,6 +21,9 @@
import org.neo4j.io.pagecache.PageCursor;
+/**
+ * Methods for ensuring a read {@link GenSafePointer GSP pointer} is valid.
+ */
class PointerChecking
{
/**
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/TreeState.java b/community/index/src/main/java/org/neo4j/index/gbptree/TreeState.java
index 59ccef90878a5..541a202270a40 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/TreeState.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/TreeState.java
@@ -24,11 +24,38 @@
import org.neo4j.io.pagecache.PageCache;
import org.neo4j.io.pagecache.PageCursor;
+/**
+ * Tree state is defined as top level tree meta data which changes as the tree and its constructs changes, such as:
+ *
+ * - stable/unstable generation numbers
+ * - root id, the page id containing the root of the tree
+ * - last id, the page id which is the highest allocated in the store
+ * - pointers into free-list (page id + offset)
+ *
+ * This class also knows how to {@link #write(PageCursor, long, long, long, long, long, long, long, int, int)}
+ * and {@link #read(PageCursor)} tree state to and from a {@link PageCursor}, although doesn't care where
+ * in the store that is.
+ */
class TreeState
{
+ /**
+ * Page id this tree state has been read from.
+ */
private final long pageId;
+
+ /**
+ * Stable generation of the tree.
+ */
private final long stableGeneration;
+
+ /**
+ * Unstable generation of the tree.
+ */
private final long unstableGeneration;
+
+ /**
+ * Page id which is the root of the tree.
+ */
private final long rootId;
/**
@@ -41,10 +68,32 @@ class TreeState
* since {@link PageCache} doesn't allow shrinking files.
*/
private final long lastId;
+
+ /**
+ * Page id to write new released tree node ids into.
+ */
private final long freeListWritePageId;
+
+ /**
+ * Page id to read released tree node ids from, when acquiring ids.
+ */
private final long freeListReadPageId;
+
+ /**
+ * Offset in page {@link #freeListWritePageId} to write new released tree node ids at.
+ */
private final int freeListWritePos;
+
+ /**
+ * Offset in page {@link #freeListReadPageId} to read released tree node ids from, when acquiring ids.
+ */
private final int freeListReadPos;
+
+ /**
+ * Due to writing with potential concurrent page flushing tree state is written twice, the second
+ * state acting as checksum. If both states match this variable should be set to {@code true},
+ * otherwise to {@code false}.
+ */
private boolean valid;
TreeState( long pageId, long stableGeneration, long unstableGeneration, long rootId, long rootGen, long lastId,
@@ -147,6 +196,13 @@ static void write( PageCursor cursor, long stableGeneration, long unstableGenera
freeListWritePageId, freeListReadPageId, freeListWritePos, freeListReadPos ); // Write checksum
}
+ /**
+ * Reads tree state from {@code cursor} at its current offset. If checksum matches then {@link #valid}
+ * is set to {@code true}, otherwise {@code false}.
+ *
+ * @param cursor {@link PageCursor} to read tree state from, at its current offset.
+ * @return {@link TreeState} instance containing read tree state.
+ */
static TreeState read( PageCursor cursor )
{
TreeState state = readStateOnce( cursor );
diff --git a/community/index/src/main/java/org/neo4j/index/gbptree/TreeStatePair.java b/community/index/src/main/java/org/neo4j/index/gbptree/TreeStatePair.java
index 8410898762eb7..b60dd6a29344b 100644
--- a/community/index/src/main/java/org/neo4j/index/gbptree/TreeStatePair.java
+++ b/community/index/src/main/java/org/neo4j/index/gbptree/TreeStatePair.java
@@ -26,8 +26,23 @@
import org.neo4j.io.pagecache.PageCursor;
+/**
+ * Pair of {@link TreeState}, ability to make decision about which of the two to read and write respectively,
+ * depending on the {@link TreeState#isValid() validity} and {@link TreeState#stableGeneration()} of each.
+ */
class TreeStatePair
{
+ /**
+ * Reads the tree state pair, one from each of {@code pageIdA} and {@code pageIdB}, deciding their validity
+ * and returning them as a {@link Pair}.
+ *
+ * @param cursor {@link PageCursor} to use when reading. This cursor will be moved to the two pages
+ * one after the other, to read their states.
+ * @param pageIdA page id containing the first state.
+ * @param pageIdB page id containing the second state.
+ * @return {@link Pair} of both tree states.
+ * @throws IOException on {@link PageCursor} reading error.
+ */
static Pair readStatePages( PageCursor cursor, long pageIdA, long pageIdB ) throws IOException
{
TreeState stateA;
@@ -43,6 +58,12 @@ static Pair readStatePages( PageCursor cursor, long pageIdA
return Pair.of( stateA, stateB );
}
+ /**
+ * @param states the two states to compare.
+ * @return newest (w/ regards to {@link TreeState#stableGeneration()}) {@link TreeState#isValid() valid}
+ * {@link TreeState} of the two.
+ * @throws IllegalStateException if none were valid.
+ */
static TreeState selectNewestValidState( Pair states )
{
return selectNewestValidStateOptionally( states ).orElseThrow( () ->
@@ -50,6 +71,11 @@ static TreeState selectNewestValidState( Pair states )
states.getLeft(), states.getRight() ) );
}
+ /**
+ * @param states the two states to compare.
+ * @return oldest (w/ regards to {@link TreeState#stableGeneration()}) {@link TreeState#isValid() invalid}
+ * {@link TreeState} of the two. If both are invalid then the {@link Pair#getLeft() first one} is returned.
+ */
static TreeState selectOldestOrInvalid( Pair states )
{
TreeState newestValidState = selectNewestValidStateOptionally( states ).orElse( states.getRight() );