use of org.cojen.tupl.Index in project Tupl by cojen.
the class VerificationObserver method indexNodeFailed.
/**
* Called after an index node fails verification. Implementation is free to
* report incremental progress or throttle verification. Default
* implementation prints a message to standard out and returns true.
*
* @param id ephemeral node identifier
* @param level index node level; root node is level one
* @param message failure message
* @return false if verification should stop
*/
public boolean indexNodeFailed(long id, int level, String message) {
var b = new StringBuilder("Verification failure: index=");
Index index = this.index;
if (index == null) {
b.append("null");
} else {
b.append(index.id());
}
b.append(", node=").append(id).append(", level=").append(level).append(": ").append(message);
reportFailure(b.toString());
return true;
}
use of org.cojen.tupl.Index in project Tupl by cojen.
the class BasicRowUpdater method doUpdateAsDeleteInsert.
/**
* Called when the primary key changed.
*
* @param c positioned at key to delete; position isn't changed
* @param key key to insert
* @param value value to insert
* @throws UniqueConstraintException
*/
private void doUpdateAsDeleteInsert(R row, Cursor c, byte[] key, byte[] value) throws IOException {
Index source = mTable.mSource;
Transaction txn = ViewUtils.enterScope(source, c.link());
try {
Trigger<R> trigger = mTable.getTrigger();
if (trigger != null)
while (true) {
trigger.acquireShared();
try {
int mode = trigger.mode();
if (mode == Trigger.SKIP) {
break;
}
if (mode != Trigger.DISABLED) {
byte[] oldValue = c.value();
if (oldValue != null) {
// Don't pass the row because the key columns were modified.
trigger.delete(txn, c.key(), oldValue);
}
trigger.insertP(txn, row, key, value);
break;
}
} finally {
trigger.releaseShared();
}
trigger = mTable.trigger();
}
boolean result;
RowPredicateLock<R> lock = mTable.mIndexLock;
if (lock == null) {
result = source.insert(txn, key, value);
} else {
lock.redoPredicateMode(txn);
try (RowPredicateLock.Closer closer = lock.openAcquireP(txn, row, key, value)) {
result = source.insert(txn, key, value);
}
}
if (!result) {
throw new UniqueConstraintException("Primary key");
}
c.commit(null);
} finally {
txn.exit();
}
postStoreKeyValue(txn);
}
use of org.cojen.tupl.Index in project Tupl by cojen.
the class BaseTable method store.
/**
* Called when no trigger is installed.
*/
protected final void store(Transaction txn, R row, byte[] key, byte[] value) throws IOException {
Index source = mSource;
// RowPredicateLock requires a non-null transaction.
txn = ViewUtils.enterScope(source, txn);
try {
redoPredicateMode(txn);
try (RowPredicateLock.Closer closer = mIndexLock.openAcquire(txn, row)) {
source.store(txn, key, value);
}
txn.commit();
} finally {
txn.exit();
}
}
use of org.cojen.tupl.Index in project Tupl by cojen.
the class BaseTable method insert.
/**
* Called when no trigger is installed.
*/
protected final boolean insert(Transaction txn, R row, byte[] key, byte[] value) throws IOException {
Index source = mSource;
// RowPredicateLock requires a non-null transaction.
txn = ViewUtils.enterScope(source, txn);
boolean result;
try {
redoPredicateMode(txn);
try (RowPredicateLock.Closer closer = mIndexLock.openAcquire(txn, row)) {
result = source.insert(txn, key, value);
}
txn.commit();
} finally {
txn.exit();
}
return result;
}
use of org.cojen.tupl.Index in project Tupl by cojen.
the class IndexBackfill method inserted.
/**
* Called by a trigger when an entry is inserted into the secondary index. It's expected
* that the transaction holds a lock on the secondary key within the secondary index.
*
* @param txn not null
*/
public void inserted(Transaction txn, byte[] secondaryKey, byte[] secondaryValue) throws IOException {
Index newIndex = mNewSecondaryIndex;
Index deleted = mDeleted;
if (newIndex == null || deleted == null) {
// Backfill hasn't reached the finishing phase or is finished.
return;
}
if (txn.isBogus()) {
// Need a transaction that supports rollback.
txn = mRowStore.mDatabase.newTransaction(DurabilityMode.NO_REDO);
txn.lockTimeout(-1, null);
} else {
txn.enter();
}
try {
// The other two indexes are used for tracking modifications which must be applied
// into the new index, but because the insert supersedes them, delete the
// corresponding entries. Note that the caller just inserted into mSecondaryIndex,
// and the delete undoes it. It would be more efficient to not insert in the first
// place, but this is simpler, and backfills are infrequent.
// lock first to avoid deadlock
mSecondaryIndex.delete(txn, secondaryKey);
deleted.delete(txn, secondaryKey);
// Insert into what will become the real secondary index.
newIndex.store(txn, secondaryKey, secondaryValue);
txn.commit();
} catch (ClosedIndexException e) {
// Assume that there was a race condition and the backfill now is closed. The
// scoped transaction allows the mSecondaryIndex delete to be rolled back.
} finally {
txn.exit();
}
}
Aggregations