use of herddb.utils.LockHandle in project herddb by diennea.
the class TableManager method accessTableData.
private void accessTableData(ScanStatement statement, StatementEvaluationContext context, ScanResultOperation consumer, Transaction transaction, boolean lockRequired, boolean forWrite) throws StatementExecutionException {
statement.validateContext(context);
Predicate predicate = statement.getPredicate();
long _start = System.currentTimeMillis();
boolean acquireLock = transaction != null || forWrite || lockRequired;
LocalScanPageCache lastPageRead = acquireLock ? null : new LocalScanPageCache();
try {
IndexOperation indexOperation = predicate != null ? predicate.getIndexOperation() : null;
boolean primaryIndexSeek = indexOperation instanceof PrimaryIndexSeek;
AbstractIndexManager useIndex = getIndexForTbleAccess(indexOperation);
BatchOrderedExecutor.Executor<Map.Entry<Bytes, Long>> scanExecutor = (List<Map.Entry<Bytes, Long>> batch) -> {
for (Map.Entry<Bytes, Long> entry : batch) {
Bytes key = entry.getKey();
boolean keep_lock = false;
boolean already_locked = transaction != null && transaction.lookupLock(table.name, key) != null;
LockHandle lock = acquireLock ? (forWrite ? lockForWrite(key, transaction) : lockForRead(key, transaction)) : null;
try {
if (transaction != null) {
if (transaction.recordDeleted(table.name, key)) {
// skip this record. inside current transaction it has been deleted
continue;
}
Record record = transaction.recordUpdated(table.name, key);
if (record != null) {
// use current transaction version of the record
if (predicate == null || predicate.evaluate(record, context)) {
consumer.accept(record);
keep_lock = true;
}
continue;
}
}
Long pageId = entry.getValue();
if (pageId != null) {
boolean pkFilterCompleteMatch = false;
if (!primaryIndexSeek && predicate != null) {
Predicate.PrimaryKeyMatchOutcome outcome = predicate.matchesRawPrimaryKey(key, context);
if (outcome == Predicate.PrimaryKeyMatchOutcome.FAILED) {
continue;
} else if (outcome == Predicate.PrimaryKeyMatchOutcome.FULL_CONDITION_VERIFIED) {
pkFilterCompleteMatch = true;
}
}
Record record = fetchRecord(key, pageId, lastPageRead);
if (record != null && (pkFilterCompleteMatch || predicate == null || predicate.evaluate(record, context))) {
consumer.accept(record);
keep_lock = true;
}
}
} finally {
// release the lock on the key if it did not match scan criteria
if (transaction == null) {
if (lock != null) {
if (forWrite) {
locksManager.releaseWriteLockForKey(key, lock);
} else {
locksManager.releaseReadLockForKey(key, lock);
}
}
} else if (!keep_lock && !already_locked) {
transaction.releaseLockOnKey(table.name, key, locksManager);
}
}
}
};
BatchOrderedExecutor<Map.Entry<Bytes, Long>> executor = new BatchOrderedExecutor<>(SORTED_PAGE_ACCESS_WINDOW_SIZE, scanExecutor, SORTED_PAGE_ACCESS_COMPARATOR);
Stream<Map.Entry<Bytes, Long>> scanner = keyToPage.scanner(indexOperation, context, tableContext, useIndex);
boolean exit = false;
try {
scanner.forEach(executor);
executor.finish();
} catch (ExitLoop exitLoop) {
exit = !exitLoop.continueWithTransactionData;
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "exit loop during scan {0}, started at {1}: {2}", new Object[] { statement, new java.sql.Timestamp(_start), exitLoop.toString() });
}
} catch (final HerdDBInternalException error) {
LOGGER.log(Level.SEVERE, "error during scan", error);
if (error.getCause() instanceof StatementExecutionException) {
throw (StatementExecutionException) error.getCause();
} else if (error.getCause() instanceof DataStorageManagerException) {
throw (DataStorageManagerException) error.getCause();
} else if (error instanceof StatementExecutionException) {
throw (StatementExecutionException) error;
} else if (error instanceof DataStorageManagerException) {
throw (DataStorageManagerException) error;
} else {
throw new StatementExecutionException(error);
}
}
if (!exit && transaction != null) {
consumer.beginNewRecordsInTransactionBlock();
Collection<Record> newRecordsForTable = transaction.getNewRecordsForTable(table.name);
for (Record record : newRecordsForTable) {
if (!transaction.recordDeleted(table.name, record.key) && (predicate == null || predicate.evaluate(record, context))) {
consumer.accept(record);
}
}
}
} catch (ExitLoop exitLoop) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.log(Level.FINEST, "exit loop during scan {0}, started at {1}: {2}", new Object[] { statement, new java.sql.Timestamp(_start), exitLoop.toString() });
}
} catch (StatementExecutionException err) {
LOGGER.log(Level.SEVERE, "error during scan {0}, started at {1}: {2}", new Object[] { statement, new java.sql.Timestamp(_start), err.toString() });
throw err;
} catch (HerdDBInternalException err) {
LOGGER.log(Level.SEVERE, "error during scan {0}, started at {1}: {2}", new Object[] { statement, new java.sql.Timestamp(_start), err.toString() });
throw new StatementExecutionException(err);
}
}
use of herddb.utils.LockHandle in project herddb by diennea.
the class TableManager method executeInsert.
private StatementExecutionResult executeInsert(InsertStatement insert, Transaction transaction, StatementEvaluationContext context) throws StatementExecutionException, DataStorageManagerException {
/*
an insert can succeed only if the row is valid and the "keys" structure does not contain the requested key
the insert will add the row in the 'buffer' without assigning a page to it
locks: the insert uses global 'insert' lock on the table
the insert will update the 'maxKey' for auto_increment primary keys
*/
Bytes key = new Bytes(insert.getKeyFunction().computeNewValue(null, context, tableContext));
byte[] value = insert.getValuesFunction().computeNewValue(new Record(key, null), context, tableContext);
final long size = DataPage.estimateEntrySize(key, value);
if (size > maxLogicalPageSize) {
throw new RecordTooBigException("New record " + key + " is to big to be inserted: size " + size + ", max size " + maxLogicalPageSize);
}
LockHandle lock = lockForWrite(key, transaction);
try {
if (transaction != null) {
if (transaction.recordDeleted(table.name, key)) {
// OK, INSERT on a DELETED record inside this transaction
} else if (transaction.recordInserted(table.name, key) != null) {
// ERROR, INSERT on a INSERTED record inside this transaction
throw new DuplicatePrimaryKeyException(key, "key " + key + ", decoded as " + RecordSerializer.deserializePrimaryKey(key.data, table) + ", already exists in table " + table.name + " inside transaction " + transaction.transactionId);
} else if (keyToPage.containsKey(key)) {
throw new DuplicatePrimaryKeyException(key, "key " + key + ", decoded as " + RecordSerializer.deserializePrimaryKey(key.data, table) + ", already exists in table " + table.name + " during transaction " + transaction.transactionId);
}
} else if (keyToPage.containsKey(key)) {
throw new DuplicatePrimaryKeyException(key, "key " + key + ", decoded as " + RecordSerializer.deserializePrimaryKey(key.data, table) + ", already exists in table " + table.name);
}
LogEntry entry = LogEntryFactory.insert(table, key.data, value, transaction);
CommitLogResult pos = log.log(entry, entry.transactionId <= 0);
apply(pos, entry, false);
return new DMLStatementExecutionResult(entry.transactionId, 1, key, insert.isReturnValues() ? Bytes.from_array(value) : null);
} catch (LogNotAvailableException err) {
throw new StatementExecutionException(err);
} finally {
if (transaction == null) {
locksManager.releaseWriteLockForKey(key, lock);
}
}
}
use of herddb.utils.LockHandle in project herddb by diennea.
the class TableManager method lockForRead.
private LockHandle lockForRead(Bytes key, Transaction transaction) {
if (transaction != null) {
LockHandle lock = transaction.lookupLock(table.name, key);
if (lock != null) {
// transaction already locked the key
return lock;
} else {
lock = locksManager.acquireReadLockForKey(key);
transaction.registerLockOnTable(this.table.name, lock);
return lock;
}
} else {
return locksManager.acquireReadLockForKey(key);
}
}
use of herddb.utils.LockHandle in project herddb by diennea.
the class TableManager method scanForIndexRebuild.
@Override
public void scanForIndexRebuild(Consumer<Record> records) throws DataStorageManagerException {
LocalScanPageCache localPageCache = new LocalScanPageCache();
Consumer<Map.Entry<Bytes, Long>> scanExecutor = (Map.Entry<Bytes, Long> entry) -> {
Bytes key = entry.getKey();
LockHandle lock = lockForRead(key, null);
try {
Long pageId = entry.getValue();
if (pageId != null) {
Record record = fetchRecord(key, pageId, localPageCache);
if (record != null) {
records.accept(record);
}
}
} catch (DataStorageManagerException | StatementExecutionException error) {
throw new RuntimeException(error);
} finally {
locksManager.releaseReadLockForKey(key, lock);
}
};
try {
Stream<Map.Entry<Bytes, Long>> scanner = keyToPage.scanner(null, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), tableContext, null);
scanner.forEach(scanExecutor);
} catch (StatementExecutionException impossible) {
throw new DataStorageManagerException(impossible);
}
}
use of herddb.utils.LockHandle in project herddb by diennea.
the class TableManager method executeGet.
private StatementExecutionResult executeGet(GetStatement get, Transaction transaction, StatementEvaluationContext context) throws StatementExecutionException, DataStorageManagerException {
Bytes key = new Bytes(get.getKey().computeNewValue(null, context, tableContext));
Predicate predicate = get.getPredicate();
boolean requireLock = get.isRequireLock();
long transactionId = transaction != null ? transaction.transactionId : 0;
LockHandle lock = (transaction != null || requireLock) ? lockForRead(key, transaction) : null;
try {
if (transaction != null) {
if (transaction.recordDeleted(table.name, key)) {
return GetResult.NOT_FOUND(transactionId);
}
Record loadedInTransaction = transaction.recordUpdated(table.name, key);
if (loadedInTransaction != null) {
if (predicate != null && !predicate.evaluate(loadedInTransaction, context)) {
return GetResult.NOT_FOUND(transactionId);
}
return new GetResult(transactionId, loadedInTransaction, table);
}
loadedInTransaction = transaction.recordInserted(table.name, key);
if (loadedInTransaction != null) {
if (predicate != null && !predicate.evaluate(loadedInTransaction, context)) {
return GetResult.NOT_FOUND(transactionId);
}
return new GetResult(transactionId, loadedInTransaction, table);
}
}
Long pageId = keyToPage.get(key);
if (pageId == null) {
return GetResult.NOT_FOUND(transactionId);
}
Record loaded = fetchRecord(key, pageId, null);
if (loaded == null || (predicate != null && !predicate.evaluate(loaded, context))) {
return GetResult.NOT_FOUND(transactionId);
}
return new GetResult(transactionId, loaded, table);
} finally {
if (transaction == null && lock != null) {
locksManager.releaseReadLockForKey(key, lock);
}
}
}
Aggregations