Skip to content

Commit

Permalink
Added javadocs and some removal of unnecessary code
Browse files Browse the repository at this point in the history
  • Loading branch information
tinwelint committed Dec 16, 2016
1 parent 64ee456 commit d7f99f4
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 69 deletions.
Expand Up @@ -262,71 +262,8 @@ public static long write( PageCursor cursor, long pointer, long stableGeneration
GenSafePointer.write( cursor, unstableGeneration, pointer ); GenSafePointer.write( cursor, unstableGeneration, pointer );
} }
return writeResult; 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 ) private static long writeResult( byte pointerStateA, byte pointerStateB, long generationA, long generationB )
{ {
if ( pointerStateA == STABLE ) if ( pointerStateA == STABLE )
Expand Down Expand Up @@ -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; 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:
* <ul>
* <li>{@link #STABLE}</li>
* <li>{@link #UNSTABLE}</li>
* <li>{@link #CRASH}</li>
* <li>{@link #BROKEN}</li>
* <li>{@link #EMPTY}</li>
* </ul>
*
* @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, static byte pointerState( long stableGeneration, long unstableGeneration,
long generation, long pointer, boolean checksumIsCorrect ) long generation, long pointer, boolean checksumIsCorrect )
{ {
Expand Down Expand Up @@ -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 ) static String pointerStateName( byte pointerState )
{ {
switch ( pointerState ) switch ( pointerState )
Expand Down
Expand Up @@ -32,6 +32,13 @@ class Generation
private static final long UNSTABLE_GENERATION_MASK = 0xFFFFFFFFL; private static final long UNSTABLE_GENERATION_MASK = 0xFFFFFFFFL;
private static final int STABLE_GENERATION_SHIFT = Integer.SIZE; 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 ) public static long generation( long stableGeneration, long unstableGeneration )
{ {
GenSafePointer.assertGenerationOnWrite( stableGeneration ); GenSafePointer.assertGenerationOnWrite( stableGeneration );
Expand All @@ -40,13 +47,25 @@ public static long generation( long stableGeneration, long unstableGeneration )
return (stableGeneration << STABLE_GENERATION_SHIFT) | 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;
} }
} }
Expand Up @@ -21,13 +21,33 @@


import java.io.IOException; import java.io.IOException;


import org.neo4j.io.pagecache.PageCursor;

/** /**
* Provide tree node (page) ids which can be used for storing tree node data. * Provide tree node (page) ids which can be used for storing tree node data.
* Bytes on returned page ids must be empty (all zeros). * Bytes on returned page ids must be empty (all zeros).
*/ */
interface IdProvider 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; 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; void releaseId( long stableGeneration, long unstableGeneration, long id ) throws IOException;
} }
Expand Up @@ -23,6 +23,9 @@


import org.neo4j.io.pagecache.PageCursor; import org.neo4j.io.pagecache.PageCursor;


/**
* Methods for (binary-)searching keys in a tree node.
*/
class KeySearch class KeySearch
{ {
private static final int POSITION_MASK = 0x7FFFFFFF; private static final int POSITION_MASK = 0x7FFFFFFF;
Expand All @@ -45,8 +48,11 @@ class KeySearch
* @param key KEY to search for * @param key KEY to search for
* @param readKey KEY to use as temporary storage during calculation. * @param readKey KEY to use as temporary storage during calculation.
* @param keyCount number of keys in node when starting search * @param keyCount number of keys in node when starting search
* @return first position i for which bTreeNode.keyComparator().compare( key, bTreeNode.keyAt( i ) <= 0, * @return search result where least significant 31 bits are first position i for which
* or keyCount if no such key exists. * 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 <KEY,VALUE> int search( PageCursor cursor, TreeNode<KEY,VALUE> bTreeNode, KEY key, static <KEY,VALUE> int search( PageCursor cursor, TreeNode<KEY,VALUE> bTreeNode, KEY key,
KEY readKey, int keyCount ) KEY readKey, int keyCount )
Expand Down Expand Up @@ -115,11 +121,24 @@ private static int searchResult( int pos, boolean hit )
return (pos & POSITION_MASK) | ((hit ? 1 : 0) << 31); 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 ) static int positionOf( int searchResult )
{ {
return searchResult & POSITION_MASK; 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 ) static boolean isHit( int searchResult )
{ {
return (searchResult & HIT_MASK) != 0; return (searchResult & HIT_MASK) != 0;
Expand Down
Expand Up @@ -21,6 +21,9 @@


import org.neo4j.io.pagecache.PageCursor; import org.neo4j.io.pagecache.PageCursor;


/**
* Methods for ensuring a read {@link GenSafePointer GSP pointer} is valid.
*/
class PointerChecking class PointerChecking
{ {
/** /**
Expand Down
Expand Up @@ -24,11 +24,38 @@
import org.neo4j.io.pagecache.PageCache; import org.neo4j.io.pagecache.PageCache;
import org.neo4j.io.pagecache.PageCursor; 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:
* <ul>
* <li>stable/unstable generation numbers</li>
* <li>root id, the page id containing the root of the tree</li>
* <li>last id, the page id which is the highest allocated in the store</li>
* <li>pointers into free-list (page id + offset)</li>
* </ul>
* 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 class TreeState
{ {
/**
* Page id this tree state has been read from.
*/
private final long pageId; private final long pageId;

/**
* Stable generation of the tree.
*/
private final long stableGeneration; private final long stableGeneration;

/**
* Unstable generation of the tree.
*/
private final long unstableGeneration; private final long unstableGeneration;

/**
* Page id which is the root of the tree.
*/
private final long rootId; private final long rootId;


/** /**
Expand All @@ -41,10 +68,32 @@ class TreeState
* since {@link PageCache} doesn't allow shrinking files. * since {@link PageCache} doesn't allow shrinking files.
*/ */
private final long lastId; private final long lastId;

/**
* Page id to write new released tree node ids into.
*/
private final long freeListWritePageId; private final long freeListWritePageId;

/**
* Page id to read released tree node ids from, when acquiring ids.
*/
private final long freeListReadPageId; private final long freeListReadPageId;

/**
* Offset in page {@link #freeListWritePageId} to write new released tree node ids at.
*/
private final int freeListWritePos; private final int freeListWritePos;

/**
* Offset in page {@link #freeListReadPageId} to read released tree node ids from, when acquiring ids.
*/
private final int freeListReadPos; 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; private boolean valid;


TreeState( long pageId, long stableGeneration, long unstableGeneration, long rootId, long rootGen, long lastId, TreeState( long pageId, long stableGeneration, long unstableGeneration, long rootId, long rootGen, long lastId,
Expand Down Expand Up @@ -147,6 +196,13 @@ static void write( PageCursor cursor, long stableGeneration, long unstableGenera
freeListWritePageId, freeListReadPageId, freeListWritePos, freeListReadPos ); // Write checksum 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 ) static TreeState read( PageCursor cursor )
{ {
TreeState state = readStateOnce( cursor ); TreeState state = readStateOnce( cursor );
Expand Down
Expand Up @@ -26,8 +26,23 @@


import org.neo4j.io.pagecache.PageCursor; 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 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<TreeState,TreeState> readStatePages( PageCursor cursor, long pageIdA, long pageIdB ) throws IOException static Pair<TreeState,TreeState> readStatePages( PageCursor cursor, long pageIdA, long pageIdB ) throws IOException
{ {
TreeState stateA; TreeState stateA;
Expand All @@ -43,13 +58,24 @@ static Pair<TreeState,TreeState> readStatePages( PageCursor cursor, long pageIdA
return Pair.of( stateA, stateB ); 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<TreeState,TreeState> states ) static TreeState selectNewestValidState( Pair<TreeState,TreeState> states )
{ {
return selectNewestValidStateOptionally( states ).orElseThrow( () -> return selectNewestValidStateOptionally( states ).orElseThrow( () ->
new TreeInconsistencyException( "Unexpected combination of state.%n STATE_A[%s]%n STATE_B[%s]", new TreeInconsistencyException( "Unexpected combination of state.%n STATE_A[%s]%n STATE_B[%s]",
states.getLeft(), states.getRight() ) ); 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<TreeState,TreeState> states ) static TreeState selectOldestOrInvalid( Pair<TreeState,TreeState> states )
{ {
TreeState newestValidState = selectNewestValidStateOptionally( states ).orElse( states.getRight() ); TreeState newestValidState = selectNewestValidStateOptionally( states ).orElse( states.getRight() );
Expand Down

0 comments on commit d7f99f4

Please sign in to comment.