Search in sources :

Example 76 with PageCursor

use of org.neo4j.io.pagecache.PageCursor in project neo4j by neo4j.

the class InternalTreeLogic method updateRightmostChildInLeftSibling.

private void updateRightmostChildInLeftSibling(PageCursor cursor, long childPointer, long stableGeneration, long unstableGeneration) throws IOException {
    long leftSibling = bTreeNode.leftSibling(cursor, stableGeneration, unstableGeneration);
    // Left sibling is not allowed to be NO_NODE here because that means there is a child node with no parent
    PointerChecking.checkPointer(leftSibling, false);
    try (PageCursor leftSiblingCursor = cursor.openLinkedCursor(leftSibling)) {
        bTreeNode.goTo(leftSiblingCursor, "left sibling", leftSibling);
        int keyCount = bTreeNode.keyCount(leftSiblingCursor);
        bTreeNode.setChildAt(leftSiblingCursor, childPointer, keyCount, stableGeneration, unstableGeneration);
    }
}
Also used : PageCursor(org.neo4j.io.pagecache.PageCursor)

Example 77 with PageCursor

use of org.neo4j.io.pagecache.PageCursor in project neo4j by neo4j.

the class InternalTreeLogic method splitLeaf.

/**
     * Leaves cursor at same page as when called. No guarantees on offset.
     * Cursor is expected to be pointing to full leaf.
     *
     * @param cursor cursor pointing into full (left) leaf that should be split in two.
     * @param structurePropagation {@link StructurePropagation} used to report structure changes between tree levels.
     * @param newKey key to be inserted
     * @param newValue value to be inserted (in association with key)
     * @param keyCount number of keys in this leaf (it was already read anyway)
     * @throws IOException on cursor failure
     */
private void splitLeaf(PageCursor cursor, StructurePropagation<KEY> structurePropagation, KEY newKey, VALUE newValue, int keyCount, long stableGeneration, long unstableGeneration) 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
    //
    // Before split
    // newRight is leaf node to be inserted between left and oldRight
    // [left] -> [oldRight]
    //
    //     [newRight]
    //
    // After split
    // [left] -> [newRight] -> [oldRight]
    //
    long current = cursor.getCurrentPageId();
    long oldRight = bTreeNode.rightSibling(cursor, stableGeneration, unstableGeneration);
    PointerChecking.checkPointer(oldRight, true);
    long newRight = idProvider.acquireNewId(stableGeneration, unstableGeneration);
    // BALANCE KEYS AND VALUES
    // Two different scenarios
    // Before split
    // [key1]<=[key2]<=[key3]<=[key4]<=[key5]   (<= greater than or equal to)
    //                           ^
    //                           |
    //                      pos  |
    // [newKey] -----------------
    //
    // After split
    // Left
    // [key1]<=[key2]<=[key3]
    //
    // Right
    // [newKey][key4][key5]
    //
    // Before split
    // [key1]<=[key2]<=[key3]<=[key4]<=[key5]   (<= greater than or equal to)
    //   ^
    //   | pos
    //   |
    // [newKey]
    //
    // After split
    // Left
    // [newKey]<=[key1]<=[key2]
    //
    // Right
    // [key3][key4][key5]
    //
    // CONCURRENCY
    // To have readers see correct state at all times, the order of updates must be:
    // 1. Acquire new page id R
    // 2. Copy "right-hand" keys/values to R and set key count
    // 3. Set L's right sibling to R
    // 4. Set key count of L to new "left-hand" key count
    // 5. Write new key/values into L
    // Position where newKey / newValue is to be inserted
    int pos = positionOf(search(cursor, newKey, readKey, keyCount));
    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;
    if (middlePos == pos) {
        layout.copyKey(newKey, structurePropagation.rightKey);
    } else {
        bTreeNode.keyAt(cursor, structurePropagation.rightKey, pos < middlePos ? middlePos - 1 : middlePos);
    }
    // Update new right
    try (PageCursor rightCursor = cursor.openLinkedCursor(newRight)) {
        bTreeNode.goTo(rightCursor, "new right sibling in split", newRight);
        bTreeNode.initializeLeaf(rightCursor, stableGeneration, unstableGeneration);
        bTreeNode.setRightSibling(rightCursor, oldRight, stableGeneration, unstableGeneration);
        bTreeNode.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.insertKeyAt(rightCursor, newKey, countBeforePos, countBeforePos);
            bTreeNode.insertValueAt(rightCursor, newValue, countBeforePos, countBeforePos);
            int countAfterPos = keyCount - pos;
            if (countAfterPos > 0) {
                // second copy
                copyKeysAndValues(cursor, pos, rightCursor, countBeforePos + 1, countAfterPos);
            }
        }
        bTreeNode.setKeyCount(rightCursor, rightKeyCount);
    }
    // Update old right with new left sibling (newRight)
    if (TreeNode.isNode(oldRight)) {
        try (PageCursor oldRightCursor = cursor.openLinkedCursor(oldRight)) {
            bTreeNode.goTo(oldRightCursor, "old right sibling", oldRight);
            bTreeNode.setLeftSibling(oldRightCursor, newRight, stableGeneration, unstableGeneration);
        }
    }
    // If pos < middle. Write shifted values to left node. Else, don't write anything.
    if (pos < middlePos) {
        bTreeNode.insertKeyAt(cursor, newKey, pos, middlePos - 1);
        bTreeNode.insertValueAt(cursor, newValue, pos, middlePos - 1);
    }
    bTreeNode.setKeyCount(cursor, middlePos);
    bTreeNode.setRightSibling(cursor, newRight, stableGeneration, unstableGeneration);
}
Also used : PageCursor(org.neo4j.io.pagecache.PageCursor)

Example 78 with PageCursor

use of org.neo4j.io.pagecache.PageCursor in project neo4j by neo4j.

the class SeekCursor method scoutNextSibling.

/**
     * Reads first key on next sibling, without moving the main {@link PageCursor} to that sibling.
     * This to be able to guard for, and retry read if, concurrent writes moving keys in the "wrong" direction.
     * The first key read here will be matched after actually moving the main {@link PageCursor} to
     * the next sibling.
     * <p>
     * May only be called if {@link #pointerId} points to next sibling.
     *
     * @return {@code true} if first key in next sibling was read successfully, otherwise {@code false},
     * which means that caller should retry most recent read.
     * @throws IOException on {@link PageCursor} error.
     */
private boolean scoutNextSibling() throws IOException {
    // Read header but to local variables and not global once
    byte nodeType;
    int keyCount = -1;
    try (PageCursor scout = this.cursor.openLinkedCursor(GenerationSafePointerPair.pointer(pointerId))) {
        scout.next();
        nodeType = TreeNode.nodeType(scout);
        if (nodeType == TreeNode.NODE_TYPE_TREE_NODE) {
            keyCount = bTreeNode.keyCount(scout);
            if (keyCountIsSane(keyCount)) {
                int firstPos = seekForward ? 0 : keyCount - 1;
                bTreeNode.keyAt(scout, expectedFirstAfterGoToNext, firstPos);
            }
        }
        if (this.cursor.shouldRetry()) {
            // on shouldRetry here.
            return false;
        }
        checkOutOfBounds(this.cursor);
    }
    if (nodeType != TreeNode.NODE_TYPE_TREE_NODE || !keyCountIsSane(keyCount)) {
        return false;
    }
    return true;
}
Also used : PageCursor(org.neo4j.io.pagecache.PageCursor)

Example 79 with PageCursor

use of org.neo4j.io.pagecache.PageCursor in project neo4j by neo4j.

the class ConsistencyCheckerTest method shouldThrowDescriptiveExceptionOnBrokenGSPP.

@Test
public void shouldThrowDescriptiveExceptionOnBrokenGSPP() throws Exception {
    // GIVEN
    int pageSize = 256;
    PageCursor cursor = new PageAwareByteArrayCursor(pageSize);
    Layout<MutableLong, MutableLong> layout = new SimpleLongLayout();
    TreeNode<MutableLong, MutableLong> treeNode = new TreeNode<>(pageSize, layout);
    long stableGeneration = MIN_GENERATION;
    long crashGeneration = stableGeneration + 1;
    long unstableGeneration = stableGeneration + 2;
    String pointerFieldName = "abc";
    long pointer = 123;
    cursor.next(0);
    treeNode.initializeInternal(cursor, stableGeneration, crashGeneration);
    treeNode.setSuccessor(cursor, pointer, stableGeneration, crashGeneration);
    // WHEN
    try {
        assertNoCrashOrBrokenPointerInGSPP(cursor, stableGeneration, unstableGeneration, pointerFieldName, TreeNode.BYTE_POS_SUCCESSOR, treeNode);
        cursor.checkAndClearCursorException();
        fail("Should have failed");
    } catch (CursorException e) {
        // THEN
        assertThat(e.getMessage(), containsString(pointerFieldName));
        assertThat(e.getMessage(), containsString(pointerFieldName));
        assertThat(e.getMessage(), containsString("state=CRASH"));
        assertThat(e.getMessage(), containsString("state=EMPTY"));
        assertThat(e.getMessage(), containsString(String.valueOf(pointer)));
    }
}
Also used : MutableLong(org.apache.commons.lang3.mutable.MutableLong) CursorException(org.neo4j.io.pagecache.CursorException) CoreMatchers.containsString(org.hamcrest.CoreMatchers.containsString) PageCursor(org.neo4j.io.pagecache.PageCursor) Test(org.junit.Test)

Example 80 with PageCursor

use of org.neo4j.io.pagecache.PageCursor in project neo4j by neo4j.

the class ConsistencyCheckerTest method shouldDetectUnusedPages.

@Test
public void shouldDetectUnusedPages() throws Exception {
    // GIVEN
    int pageSize = 256;
    Layout<MutableLong, MutableLong> layout = new SimpleLongLayout();
    TreeNode<MutableLong, MutableLong> node = new TreeNode<>(pageSize, layout);
    long stableGeneration = GenerationSafePointer.MIN_GENERATION;
    long unstableGeneration = stableGeneration + 1;
    SimpleIdProvider idProvider = new SimpleIdProvider();
    InternalTreeLogic<MutableLong, MutableLong> logic = new InternalTreeLogic<>(idProvider, node, layout);
    PageCursor cursor = new PageAwareByteArrayCursor(pageSize);
    cursor.next(idProvider.acquireNewId(stableGeneration, unstableGeneration));
    node.initializeLeaf(cursor, stableGeneration, unstableGeneration);
    logic.initialize(cursor);
    StructurePropagation<MutableLong> structure = new StructurePropagation<>(layout.newKey(), layout.newKey(), layout.newKey());
    MutableLong key = layout.newKey();
    for (int g = 0, k = 0; g < 3; g++) {
        for (int i = 0; i < 100; i++, k++) {
            key.setValue(k);
            logic.insert(cursor, structure, key, key, ValueMergers.overwrite(), stableGeneration, unstableGeneration);
            if (structure.hasRightKeyInsert) {
                goTo(cursor, "new root", idProvider.acquireNewId(stableGeneration, unstableGeneration));
                node.initializeInternal(cursor, stableGeneration, unstableGeneration);
                node.insertKeyAt(cursor, structure.rightKey, 0, 0);
                node.setKeyCount(cursor, 1);
                node.setChildAt(cursor, structure.midChild, 0, stableGeneration, unstableGeneration);
                node.setChildAt(cursor, structure.rightChild, 1, stableGeneration, unstableGeneration);
                logic.initialize(cursor);
            }
            if (structure.hasMidChildUpdate) {
                logic.initialize(cursor);
            }
            structure.clear();
        }
        stableGeneration = unstableGeneration;
        unstableGeneration++;
    }
    // WHEN
    ConsistencyChecker<MutableLong> cc = new ConsistencyChecker<>(node, layout, stableGeneration, unstableGeneration);
    try {
        cc.checkSpace(cursor, idProvider.lastId(), PrimitiveLongCollections.emptyIterator());
        fail("Should have failed");
    } catch (RuntimeException e) {
        // THEN good
        assertThat(e.getMessage(), containsString("unused pages"));
    }
}
Also used : PageCursor(org.neo4j.io.pagecache.PageCursor) MutableLong(org.apache.commons.lang3.mutable.MutableLong) Test(org.junit.Test)

Aggregations

PageCursor (org.neo4j.io.pagecache.PageCursor)184 Test (org.junit.Test)124 StubPageCursor (org.neo4j.io.pagecache.StubPageCursor)106 PagedFile (org.neo4j.io.pagecache.PagedFile)19 IOException (java.io.IOException)12 File (java.io.File)8 CursorException (org.neo4j.io.pagecache.CursorException)6 PageCacheTest (org.neo4j.io.pagecache.PageCacheTest)6 CompositePageCursor (org.neo4j.io.pagecache.impl.CompositePageCursor)6 DelegatingPageCursor (org.neo4j.io.pagecache.impl.DelegatingPageCursor)6 ByteBuffer (java.nio.ByteBuffer)5 DelegatingStoreChannel (org.neo4j.graphdb.mockfs.DelegatingStoreChannel)5 StoreChannel (org.neo4j.io.fs.StoreChannel)4 DelegatingPagedFile (org.neo4j.io.pagecache.DelegatingPagedFile)4 ConfigurablePageCursorTracerSupplier (org.neo4j.io.pagecache.tracing.ConfigurablePageCursorTracerSupplier)4 RecordingPageCacheTracer (org.neo4j.io.pagecache.tracing.recording.RecordingPageCacheTracer)4 Evict (org.neo4j.io.pagecache.tracing.recording.RecordingPageCacheTracer.Evict)4 RecordingPageCursorTracer (org.neo4j.io.pagecache.tracing.recording.RecordingPageCursorTracer)4 Fault (org.neo4j.io.pagecache.tracing.recording.RecordingPageCursorTracer.Fault)4 AtomicLong (java.util.concurrent.atomic.AtomicLong)3