use of org.cojen.tupl.Transaction in project Tupl by cojen.
the class BasicRowUpdater method doDelete.
protected void doDelete(Trigger<R> trigger, R row) throws IOException {
Cursor c = mCursor;
Transaction txn = ViewUtils.enterScope(mTable.mSource, c.link());
try {
byte[] oldValue = c.value();
if (oldValue != null) {
// Don't pass the row in case the key columns were modified.
trigger.delete(txn, c.key(), oldValue);
c.commit(null);
}
} finally {
txn.exit();
}
}
use of org.cojen.tupl.Transaction 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.Transaction in project Tupl by cojen.
the class AutoCommitRowUpdater method storeValue.
@Override
protected void storeValue(Trigger<R> trigger, R row, Cursor c, byte[] value) throws IOException {
Transaction txn = c.link();
byte[] oldValue = c.value();
if (oldValue == null) {
trigger.insertP(txn, row, c.key(), value);
} else {
trigger.storeP(txn, row, c.key(), oldValue, value);
}
c.commit(value);
mLockResult = null;
}
use of org.cojen.tupl.Transaction in project Tupl by cojen.
the class BasicRowScanner method doStep.
protected final R doStep(R row) throws IOException {
Cursor c = mCursor;
try {
a: while (true) {
LockResult result = toNext(c);
b: while (true) {
while (c.key() == null) {
if (!mController.next()) {
break a;
}
setEvaluator(mController.evaluator());
Transaction txn = c.link();
mCursor = c = mController.newCursor(mTable.mSource, txn);
toFirst(c);
}
try {
R decoded = decodeRow(c, result, row);
if (decoded != null) {
mRow = decoded;
return decoded;
}
} catch (StoppedCursorException e) {
if (result == LockResult.ACQUIRED) {
c.link().unlock();
unlocked();
}
continue b;
}
if (result == LockResult.ACQUIRED) {
c.link().unlock();
unlocked();
}
continue a;
}
}
} catch (UnpositionedCursorException e) {
} catch (Throwable e) {
throw RowUtils.fail(this, e);
}
finished();
return null;
}
use of org.cojen.tupl.Transaction 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;
}
Aggregations