Search in sources :

Example 1 with Sorter

use of org.cojen.tupl.Sorter in project Tupl by cojen.

the class IndexBackfill method addBatch.

/**
 * @return false if closed
 */
private boolean addBatch(byte[][] primaryBatch, byte[][] secondaryBatch, int length) throws IOException {
    Sorter sorter = mSorter;
    if (sorter == null) {
        // Closed.
        return false;
    }
    for (int i = 0; i < length; i += 2) {
        encode(primaryBatch[i], primaryBatch[i + 1], secondaryBatch, i);
    }
    sorter.addBatch(secondaryBatch, 0, length >> 1);
    if (mSorter != null) {
        // Still in use.
        return true;
    }
    // Closed. Clean up the mess.
    try {
        sorter.reset();
    } catch (Exception e) {
    // Ignore.
    }
    return false;
}
Also used : Sorter(org.cojen.tupl.Sorter) IOException(java.io.IOException) ClosedIndexException(org.cojen.tupl.ClosedIndexException) InterruptedIOException(java.io.InterruptedIOException)

Example 2 with Sorter

use of org.cojen.tupl.Sorter in project Tupl by cojen.

the class IndexBackfill method doRun.

/**
 * @return false if closed
 */
private boolean doRun() throws IOException {
    var primaryBatch = new byte[100 * 2][];
    var secondaryBatch = new byte[primaryBatch.length][];
    Transaction txn = mRowStore.mDatabase.newTransaction();
    txn.lockMode(LockMode.READ_COMMITTED);
    txn.lockTimeout(-1, null);
    txn.durabilityMode(DurabilityMode.NO_REDO);
    try (Cursor c = mManager.mPrimaryIndex.newCursor(txn)) {
        c.autoload(mAutoload);
        c.first();
        int length = 0;
        while (true) {
            byte[] key = c.key();
            if (key == null) {
                if (length > 0) {
                    if (!addBatch(primaryBatch, secondaryBatch, length)) {
                        return false;
                    }
                }
                break;
            }
            primaryBatch[length++] = key;
            primaryBatch[length++] = c.value();
            if (length >= primaryBatch.length) {
                if (!addBatch(primaryBatch, secondaryBatch, length)) {
                    return false;
                }
                length = 0;
            }
            c.next();
        }
    }
    Sorter sorter = mSorter;
    if (sorter == null) {
        return false;
    }
    Index newIndex;
    try {
        newIndex = sorter.finish();
    } catch (InterruptedIOException e) {
        if (mSorter != null) {
            throw e;
        }
        return false;
    }
    Index deleted;
    synchronized (this) {
        deleted = mDeleted;
        if (deleted == null) {
            return false;
        }
        // Lock all triggers to prevent concurrent modifications, and change their behavior
        // now that backfill is finishing.
        withTriggerLock(() -> {
            mNewSecondaryIndex = newIndex;
        });
    }
    txn.lockMode(LockMode.UPGRADABLE_READ);
    try (Cursor secondaryCursor = mSecondaryIndex.newCursor(txn);
        // secondary index entry suffices.
        Cursor deletedCursor = deleted.newCursor(Transaction.BOGUS)) {
        secondaryCursor.first();
        for (byte[] key; (key = secondaryCursor.key()) != null; secondaryCursor.next()) {
            deletedCursor.findNearby(key);
            if (deletedCursor.value() == null) {
                newIndex.store(txn, key, secondaryCursor.value());
            } else {
                deletedCursor.delete();
            }
            secondaryCursor.commit(null);
        }
    }
    // deadlocks. Proper lock acquisition order is mSecondaryIndex and then mDeleted.
    try (Cursor deletedCursor = deleted.newCursor(Transaction.BOGUS)) {
        deletedCursor.first();
        for (byte[] key; (key = deletedCursor.key()) != null; deletedCursor.next()) {
            newIndex.lockUpgradable(txn, key);
            try {
                deletedCursor.load();
                if (deletedCursor.value() != null) {
                    newIndex.store(txn, key, null);
                    deletedCursor.store(null);
                    txn.commit();
                }
            } finally {
                txn.exit();
            }
        }
    }
    // Swap the fully populated new secondary index with the original, which is now
    // empty. As a side-effect, the contents of the new secondary index which was temporary
    // are now associated with a real index, and the original secondary is associated with
    // the temporary index.
    withTriggerLock(() -> {
        try {
            mRowStore.mDatabase.rootSwap(newIndex, mSecondaryIndex);
        } catch (IOException e) {
            throw RowUtils.rethrow(e);
        }
        // Backfill is finished, so stop tracking changes.
        mDeleted = null;
        mNewSecondaryIndex = null;
    });
    try {
        // Eagerly delete the temporary index.
        mRowStore.mDatabase.deleteIndex(newIndex).run();
    } catch (Exception e) {
    // Ignore.
    }
    try {
        // Eagerly delete the temporary index.
        mRowStore.mDatabase.deleteIndex(deleted).run();
    } catch (Exception e) {
    // Ignore.
    }
    return true;
}
Also used : InterruptedIOException(java.io.InterruptedIOException) Transaction(org.cojen.tupl.Transaction) Sorter(org.cojen.tupl.Sorter) Index(org.cojen.tupl.Index) IOException(java.io.IOException) InterruptedIOException(java.io.InterruptedIOException) Cursor(org.cojen.tupl.Cursor) IOException(java.io.IOException) ClosedIndexException(org.cojen.tupl.ClosedIndexException) InterruptedIOException(java.io.InterruptedIOException)

Example 3 with Sorter

use of org.cojen.tupl.Sorter in project Tupl by cojen.

the class IndexBackfill method unused.

private void unused(Trigger<R> trigger, boolean close) {
    Sorter sorter;
    Index deleted;
    synchronized (this) {
        if (mTriggers == null) {
            return;
        }
        mTriggers.remove(trigger);
        if (!close && !mTriggers.isEmpty()) {
            return;
        }
        mTriggers = null;
        sorter = mSorter;
        mSorter = null;
        deleted = mDeleted;
        mDeleted = null;
    }
    if (sorter == null && deleted == null) {
        return;
    }
    CoreDatabase db = mRowStore.mDatabase;
    db.removeRedoListener(this);
    Runner.start(() -> {
        Runnable deleteTask = null;
        if (deleted != null) {
            try {
                deleteTask = db.deleteIndex(deleted);
            } catch (Exception e) {
            // Ignore.
            }
        }
        if (sorter != null) {
            try {
                sorter.reset();
            } catch (Exception e) {
            // Ignore.
            }
        }
        if (deleteTask != null) {
            deleteTask.run();
        }
    });
}
Also used : CoreDatabase(org.cojen.tupl.core.CoreDatabase) Sorter(org.cojen.tupl.Sorter) Index(org.cojen.tupl.Index) IOException(java.io.IOException) ClosedIndexException(org.cojen.tupl.ClosedIndexException) InterruptedIOException(java.io.InterruptedIOException)

Aggregations

IOException (java.io.IOException)3 InterruptedIOException (java.io.InterruptedIOException)3 ClosedIndexException (org.cojen.tupl.ClosedIndexException)3 Sorter (org.cojen.tupl.Sorter)3 Index (org.cojen.tupl.Index)2 Cursor (org.cojen.tupl.Cursor)1 Transaction (org.cojen.tupl.Transaction)1 CoreDatabase (org.cojen.tupl.core.CoreDatabase)1