Search in sources :

Example 11 with LockHandle

use of herddb.utils.LockHandle in project herddb by diennea.

the class TableManager method lockForWrite.

private LockHandle lockForWrite(Bytes key, Transaction transaction) {
    if (transaction != null) {
        LockHandle lock = transaction.lookupLock(table.name, key);
        if (lock != null) {
            if (lock.write) {
                // transaction already locked the key for writes
                return lock;
            } else {
                // transaction already locked the key, but we need to upgrade the lock
                locksManager.releaseLock(lock);
                transaction.unregisterUpgradedLocksOnTable(table.name, lock);
                lock = locksManager.acquireWriteLockForKey(key);
                transaction.registerLockOnTable(this.table.name, lock);
                return lock;
            }
        } else {
            lock = locksManager.acquireWriteLockForKey(key);
            transaction.registerLockOnTable(this.table.name, lock);
            return lock;
        }
    } else {
        return locksManager.acquireWriteLockForKey(key);
    }
}
Also used : LockHandle(herddb.utils.LockHandle)

Example 12 with LockHandle

use of herddb.utils.LockHandle in project herddb by diennea.

the class TableManager method lockForWrite.

private static LockHandle lockForWrite(Bytes key, Transaction transaction, String lockKey, ILocalLockManager locksManager) {
    // LOGGER.log(Level.SEVERE, "lockForWrite for " + key + " tx " + transaction);
    try {
        if (transaction != null) {
            LockHandle lock = transaction.lookupLock(lockKey, key);
            if (lock != null) {
                if (lock.write) {
                    // transaction already locked the key for writes
                    return lock;
                } else {
                    // transaction already locked the key, but we need to upgrade the lock
                    locksManager.releaseLock(lock);
                    transaction.unregisterUpgradedLocksOnTable(lockKey, lock);
                    lock = locksManager.acquireWriteLockForKey(key);
                    transaction.registerLockOnTable(lockKey, lock);
                    return lock;
                }
            } else {
                lock = locksManager.acquireWriteLockForKey(key);
                transaction.registerLockOnTable(lockKey, lock);
                return lock;
            }
        } else {
            return locksManager.acquireWriteLockForKey(key);
        }
    } catch (HerdDBInternalException err) {
        // locktimeout or other internal lockmanager error
        throw err;
    } catch (RuntimeException err) {
        // locktimeout or other internal lockmanager error
        throw new StatementExecutionException(err);
    }
}
Also used : LockHandle(herddb.utils.LockHandle) StatementExecutionException(herddb.model.StatementExecutionException)

Example 13 with LockHandle

use of herddb.utils.LockHandle in project herddb by diennea.

the class TableManager method scanNoStream.

private DataScanner scanNoStream(ScanStatement statement, StatementEvaluationContext context, Transaction transaction, boolean lockRequired, boolean forWrite) throws StatementExecutionException {
    if (transaction != null) {
        transaction.increaseRefcount();
    }
    try {
        boolean sorted = statement.getComparator() != null;
        boolean sortedByClusteredIndex = statement.getComparator() != null && statement.getComparator().isOnlyPrimaryKeyAndAscending() && keyToPageSortedAscending;
        final Projection projection = statement.getProjection();
        boolean applyProjectionDuringScan = !sorted && projection != null;
        MaterializedRecordSet recordSet;
        if (applyProjectionDuringScan) {
            recordSet = tableSpaceManager.getDbmanager().getRecordSetFactory().createRecordSet(projection.getFieldNames(), projection.getColumns());
        } else {
            recordSet = tableSpaceManager.getDbmanager().getRecordSetFactory().createRecordSet(table.columnNames, table.columns);
        }
        ScanLimits limits = statement.getLimits();
        int maxRows = limits == null ? 0 : limits.computeMaxRows(context);
        int offset = limits == null ? 0 : limits.computeOffset(context);
        boolean sortDone = false;
        if (maxRows > 0) {
            if (sortedByClusteredIndex) {
                // leverage the sorted nature of the clustered primary key index
                AtomicInteger remaining = new AtomicInteger(maxRows);
                if (offset > 0) {
                    remaining.getAndAdd(offset);
                }
                accessTableData(statement, context, new ScanResultOperation() {

                    private boolean inTransactionData;

                    @Override
                    public void beginNewRecordsInTransactionBlock() {
                        inTransactionData = true;
                    }

                    @Override
                    public void accept(Record record, LockHandle lockHandle) throws StatementExecutionException {
                        try {
                            if (applyProjectionDuringScan) {
                                DataAccessor tuple = projection.map(record.getDataAccessor(table), context);
                                recordSet.add(tuple);
                            } else {
                                recordSet.add(record.getDataAccessor(table));
                            }
                            if (!inTransactionData) {
                                // in the same order as the clustered index
                                if (remaining.decrementAndGet() == 0) {
                                    // we want to receive transaction data uncommitted records too
                                    throw new ExitLoop(true);
                                }
                            }
                        } finally {
                            locksManager.releaseLock(lockHandle);
                        }
                    }
                }, transaction, lockRequired, forWrite);
                // we have to sort data any way, because accessTableData will return partially sorted data
                sortDone = transaction == null;
            } else if (sorted) {
                InStreamTupleSorter sorter = new InStreamTupleSorter(offset + maxRows, statement.getComparator());
                accessTableData(statement, context, new ScanResultOperation() {

                    @Override
                    public void accept(Record record, LockHandle lockHandle) throws StatementExecutionException {
                        try {
                            if (applyProjectionDuringScan) {
                                DataAccessor tuple = projection.map(record.getDataAccessor(table), context);
                                sorter.collect(tuple);
                            } else {
                                sorter.collect(record.getDataAccessor(table));
                            }
                        } finally {
                            locksManager.releaseLock(lockHandle);
                        }
                    }
                }, transaction, lockRequired, forWrite);
                sorter.flushToRecordSet(recordSet);
                sortDone = true;
            } else {
                // if no sort is present the limits can be applying during the scan and perform an early exit
                AtomicInteger remaining = new AtomicInteger(maxRows);
                if (offset > 0) {
                    remaining.getAndAdd(offset);
                }
                accessTableData(statement, context, new ScanResultOperation() {

                    @Override
                    public void accept(Record record, LockHandle lockHandle) throws StatementExecutionException {
                        try {
                            if (applyProjectionDuringScan) {
                                DataAccessor tuple = projection.map(record.getDataAccessor(table), context);
                                recordSet.add(tuple);
                            } else {
                                recordSet.add(record.getDataAccessor(table));
                            }
                            if (remaining.decrementAndGet() == 0) {
                                throw new ExitLoop(false);
                            }
                        } finally {
                            locksManager.releaseLock(lockHandle);
                        }
                    }
                }, transaction, lockRequired, forWrite);
            }
        } else {
            accessTableData(statement, context, new ScanResultOperation() {

                @Override
                public void accept(Record record, LockHandle lockHandle) throws StatementExecutionException {
                    try {
                        if (applyProjectionDuringScan) {
                            DataAccessor tuple = projection.map(record.getDataAccessor(table), context);
                            recordSet.add(tuple);
                        } else {
                            recordSet.add(record.getDataAccessor(table));
                        }
                    } finally {
                        locksManager.releaseLock(lockHandle);
                    }
                }
            }, transaction, lockRequired, forWrite);
        }
        recordSet.writeFinished();
        if (!sortDone) {
            recordSet.sort(statement.getComparator());
        }
        recordSet.applyLimits(statement.getLimits(), context);
        if (!applyProjectionDuringScan) {
            recordSet.applyProjection(statement.getProjection(), context);
        }
        return new SimpleDataScanner(transaction, recordSet);
    } finally {
        if (transaction != null) {
            transaction.decreaseRefCount();
        }
    }
}
Also used : LockHandle(herddb.utils.LockHandle) ScanLimits(herddb.model.ScanLimits) DataAccessor(herddb.utils.DataAccessor) Projection(herddb.model.Projection) StatementExecutionException(herddb.model.StatementExecutionException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Record(herddb.model.Record)

Example 14 with LockHandle

use of herddb.utils.LockHandle in project herddb by diennea.

the class TableManager method accessRecord.

public Record accessRecord(Map.Entry<Bytes, Long> entry, Predicate predicate, StatementEvaluationContext context, Transaction transaction, LocalScanPageCache lastPageRead, boolean primaryIndexSeek, boolean forWrite, boolean acquireLock) {
    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) {
            transaction.touch();
            if (transaction.recordDeleted(table.name, key)) {
                // skip this record. inside current transaction it has been deleted
                return null;
            }
            Record record = transaction.recordUpdated(table.name, key);
            if (record != null) {
                // use current transaction version of the record
                if (predicate == null || predicate.evaluate(record, context)) {
                    keep_lock = context.isForceRetainReadLock() || (lock != null && lock.write);
                    return record;
                }
                return null;
            }
        }
        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) {
                    return null;
                } 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))) {
                keep_lock = context.isForceRetainReadLock() || (lock != null && lock.write);
                return record;
            }
        }
        return null;
    } finally {
        // release the lock on the key if it did not match scan criteria
        if (transaction == null) {
            if (lock != null) {
                locksManager.releaseLock(lock);
            }
        } else if (!keep_lock && !already_locked) {
            transaction.releaseLockOnKey(table.name, key, locksManager);
        }
    }
}
Also used : Bytes(herddb.utils.Bytes) LockHandle(herddb.utils.LockHandle) AtomicLong(java.util.concurrent.atomic.AtomicLong) Record(herddb.model.Record) Predicate(herddb.model.Predicate)

Example 15 with LockHandle

use of herddb.utils.LockHandle in project herddb by diennea.

the class TableManager method executeDeleteAsync.

private CompletableFuture<StatementExecutionResult> executeDeleteAsync(DeleteStatement delete, Transaction transaction, StatementEvaluationContext context) {
    AtomicInteger updateCount = new AtomicInteger();
    Holder<Bytes> lastKey = new Holder<>();
    Holder<Bytes> lastValue = new Holder<>();
    long transactionId = transaction != null ? transaction.transactionId : 0;
    Predicate predicate = delete.getPredicate();
    List<CompletableFuture<PendingLogEntryWork>> writes = new ArrayList<>();
    Map<String, AbstractIndexManager> indexes = tableSpaceManager.getIndexesOnTable(table.name);
    ScanStatement scan = new ScanStatement(table.tablespace, table, predicate);
    try {
        accessTableData(scan, context, new ScanResultOperation() {

            @Override
            public void accept(Record current, LockHandle lockHandle) throws StatementExecutionException, LogNotAvailableException, DataStorageManagerException {
                // ensure we are holding the write locks on every unique index
                List<UniqueIndexLockReference> uniqueIndexes = null;
                try {
                    if (indexes != null || childrenTables != null) {
                        DataAccessor dataAccessor = current.getDataAccessor(table);
                        if (childrenTables != null) {
                            for (Table childTable : childrenTables) {
                                executeForeignKeyConstraintsAsParentTable(childTable, dataAccessor, context, transaction, true);
                            }
                        }
                        if (indexes != null) {
                            for (AbstractIndexManager index : indexes.values()) {
                                if (index.isUnique()) {
                                    Bytes indexKey = RecordSerializer.serializeIndexKey(dataAccessor, index.getIndex(), index.getColumnNames());
                                    if (uniqueIndexes == null) {
                                        uniqueIndexes = new ArrayList<>(1);
                                    }
                                    UniqueIndexLockReference uniqueIndexLock = new UniqueIndexLockReference(index, indexKey);
                                    uniqueIndexes.add(uniqueIndexLock);
                                    LockHandle lockForIndex = lockForWrite(uniqueIndexLock.key, transaction, index.getIndexName(), index.getLockManager());
                                    if (transaction == null) {
                                        uniqueIndexLock.lockHandle = lockForIndex;
                                    }
                                }
                            }
                        }
                    }
                } catch (IllegalArgumentException | herddb.utils.IllegalDataAccessException | StatementExecutionException err) {
                    locksManager.releaseLock(lockHandle);
                    StatementExecutionException finalError;
                    if (!(err instanceof StatementExecutionException)) {
                        finalError = new StatementExecutionException(err.getMessage(), err);
                    } else {
                        finalError = (StatementExecutionException) err;
                    }
                    CompletableFuture<PendingLogEntryWork> res = Futures.exception(finalError);
                    if (uniqueIndexes != null) {
                        for (UniqueIndexLockReference lock : uniqueIndexes) {
                            res = releaseWriteLock(res, lockHandle, lock.indexManager.getLockManager());
                        }
                    }
                    writes.add(res);
                    return;
                }
                LogEntry entry = LogEntryFactory.delete(table, current.key, transaction);
                CommitLogResult pos = log.log(entry, entry.transactionId <= 0);
                final List<UniqueIndexLockReference> _uniqueIndexes = uniqueIndexes;
                writes.add(pos.logSequenceNumber.thenApply(lsn -> new PendingLogEntryWork(entry, pos, lockHandle, _uniqueIndexes)));
                lastKey.value = current.key;
                lastValue.value = current.value;
                updateCount.incrementAndGet();
            }
        }, transaction, true, true);
    } catch (HerdDBInternalException err) {
        LOGGER.log(Level.SEVERE, "bad error during a delete", err);
        return Futures.exception(err);
    }
    if (writes.isEmpty()) {
        return CompletableFuture.completedFuture(new DMLStatementExecutionResult(transactionId, 0, null, null));
    }
    if (writes.size() == 1) {
        return writes.get(0).whenCompleteAsync((pending, error) -> {
            try {
                // apply any of the DML operations
                if (error == null) {
                    apply(pending.pos, pending.entry, false);
                }
            } finally {
                releaseMultiplePendingLogEntryWorks(writes);
            }
        }, tableSpaceManager.getCallbacksExecutor()).thenApply((pending) -> {
            return new DMLStatementExecutionResult(transactionId, updateCount.get(), lastKey.value, delete.isReturnValues() ? lastValue.value : null);
        });
    } else {
        return Futures.collect(writes).whenCompleteAsync((pendings, error) -> {
            try {
                // apply any of the DML operations
                if (error == null) {
                    for (PendingLogEntryWork pending : pendings) {
                        apply(pending.pos, pending.entry, false);
                    }
                }
            } finally {
                releaseMultiplePendingLogEntryWorks(writes);
            }
        }, tableSpaceManager.getCallbacksExecutor()).thenApply((pendings) -> {
            return new DMLStatementExecutionResult(transactionId, updateCount.get(), lastKey.value, delete.isReturnValues() ? lastValue.value : null);
        });
    }
}
Also used : Arrays(java.util.Arrays) NullLockManager(herddb.utils.NullLockManager) Table(herddb.model.Table) TruncateTableStatement(herddb.model.commands.TruncateTableStatement) DuplicatePrimaryKeyException(herddb.model.DuplicatePrimaryKeyException) TableStatus(herddb.storage.TableStatus) Map(java.util.Map) DataAccessor(herddb.utils.DataAccessor) LogNotAvailableException(herddb.log.LogNotAvailableException) CommitLogResult(herddb.log.CommitLogResult) LogSequenceNumber(herddb.log.LogSequenceNumber) UniqueIndexContraintViolationException(herddb.model.UniqueIndexContraintViolationException) Set(java.util.Set) RecordSerializer(herddb.codec.RecordSerializer) JSQLParserPlanner.delimit(herddb.sql.JSQLParserPlanner.delimit) DataPageMetaData(herddb.core.PageSet.DataPageMetaData) ScanStatement(herddb.model.commands.ScanStatement) Stream(java.util.stream.Stream) StatsLogger(org.apache.bookkeeper.stats.StatsLogger) Bytes(herddb.utils.Bytes) Holder(herddb.utils.Holder) LockHandle(herddb.utils.LockHandle) ForeignKeyViolationException(herddb.model.ForeignKeyViolationException) LogEntry(herddb.log.LogEntry) ArrayList(java.util.ArrayList) TransactionContext(herddb.model.TransactionContext) Transaction(herddb.model.Transaction) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) Projection(herddb.model.Projection) ForeignKeyDef(herddb.model.ForeignKeyDef) EnsureLongIncrementAccumulator(herddb.utils.EnsureLongIncrementAccumulator) LogEntryType(herddb.log.LogEntryType) Record(herddb.model.Record) LogEntryFactory(herddb.log.LogEntryFactory) KeyToPageIndex(herddb.index.KeyToPageIndex) DataStorageManager(herddb.storage.DataStorageManager) ColumnTypes(herddb.model.ColumnTypes) ILocalLockManager(herddb.utils.ILocalLockManager) AtomicLong(java.util.concurrent.atomic.AtomicLong) Lock(java.util.concurrent.locks.Lock) IndexOperation(herddb.index.IndexOperation) Column(herddb.model.Column) StampedLock(java.util.concurrent.locks.StampedLock) UpdateStatement(herddb.model.commands.UpdateStatement) ScanLimitsImpl(herddb.model.ScanLimitsImpl) TupleComparator(herddb.model.TupleComparator) ServerConfiguration(herddb.server.ServerConfiguration) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RecordTooBigException(herddb.model.RecordTooBigException) DMLStatementExecutionResult(herddb.model.DMLStatementExecutionResult) LocalLockManager(herddb.utils.LocalLockManager) Futures(herddb.utils.Futures) DataStorageManagerException(herddb.storage.DataStorageManagerException) Index(herddb.model.Index) InsertStatement(herddb.model.commands.InsertStatement) DataScanner(herddb.model.DataScanner) DDLException(herddb.model.DDLException) RecordFunction(herddb.model.RecordFunction) StatementExecutionException(herddb.model.StatementExecutionException) TableContext(herddb.model.TableContext) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Logger(java.util.logging.Logger) Collectors(java.util.stream.Collectors) List(java.util.List) FullTableScanConsumer(herddb.storage.FullTableScanConsumer) GetStatement(herddb.model.commands.GetStatement) Entry(java.util.Map.Entry) Statement(herddb.model.Statement) LongAdder(java.util.concurrent.atomic.LongAdder) DataScannerException(herddb.model.DataScannerException) GetResult(herddb.model.GetResult) PrimaryIndexSeek(herddb.index.PrimaryIndexSeek) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) Function(java.util.function.Function) BatchOrderedExecutor(herddb.utils.BatchOrderedExecutor) ConcurrentMap(java.util.concurrent.ConcurrentMap) Level(java.util.logging.Level) HashSet(java.util.HashSet) BooleanHolder(herddb.utils.BooleanHolder) ScanLimits(herddb.model.ScanLimits) DeleteStatement(herddb.model.commands.DeleteStatement) Iterator(java.util.Iterator) ReentrantLock(java.util.concurrent.locks.ReentrantLock) Semaphore(java.util.concurrent.Semaphore) DataPageDoesNotExistException(herddb.storage.DataPageDoesNotExistException) Counter(org.apache.bookkeeper.stats.Counter) StatementExecutionResult(herddb.model.StatementExecutionResult) TimeUnit(java.util.concurrent.TimeUnit) Consumer(java.util.function.Consumer) CommitLog(herddb.log.CommitLog) AbstractMap(java.util.AbstractMap) TableConsistencyCheckStatement(herddb.model.commands.TableConsistencyCheckStatement) Predicate(herddb.model.Predicate) StatementEvaluationContext(herddb.model.StatementEvaluationContext) Comparator(java.util.Comparator) Collections(java.util.Collections) SECONDS(java.util.concurrent.TimeUnit.SECONDS) TableManagerStats(herddb.core.stats.TableManagerStats) SystemProperties(herddb.utils.SystemProperties) DataStorageManagerException(herddb.storage.DataStorageManagerException) DataAccessor(herddb.utils.DataAccessor) ArrayList(java.util.ArrayList) CommitLogResult(herddb.log.CommitLogResult) StatementExecutionException(herddb.model.StatementExecutionException) Predicate(herddb.model.Predicate) Bytes(herddb.utils.Bytes) CompletableFuture(java.util.concurrent.CompletableFuture) Record(herddb.model.Record) ArrayList(java.util.ArrayList) List(java.util.List) LogEntry(herddb.log.LogEntry) ScanStatement(herddb.model.commands.ScanStatement) LockHandle(herddb.utils.LockHandle) Table(herddb.model.Table) Holder(herddb.utils.Holder) BooleanHolder(herddb.utils.BooleanHolder) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) DMLStatementExecutionResult(herddb.model.DMLStatementExecutionResult) LogNotAvailableException(herddb.log.LogNotAvailableException)

Aggregations

LockHandle (herddb.utils.LockHandle)15 Record (herddb.model.Record)10 StatementExecutionException (herddb.model.StatementExecutionException)10 Bytes (herddb.utils.Bytes)9 AtomicLong (java.util.concurrent.atomic.AtomicLong)8 Predicate (herddb.model.Predicate)7 LogEntry (herddb.log.LogEntry)6 DMLStatementExecutionResult (herddb.model.DMLStatementExecutionResult)5 GetResult (herddb.model.GetResult)5 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)5 IndexOperation (herddb.index.IndexOperation)4 PrimaryIndexSeek (herddb.index.PrimaryIndexSeek)4 CommitLogResult (herddb.log.CommitLogResult)4 LogNotAvailableException (herddb.log.LogNotAvailableException)4 DuplicatePrimaryKeyException (herddb.model.DuplicatePrimaryKeyException)4 RecordTooBigException (herddb.model.RecordTooBigException)4 StatementExecutionResult (herddb.model.StatementExecutionResult)4 DataStorageManagerException (herddb.storage.DataStorageManagerException)4 AbstractMap (java.util.AbstractMap)4 HashMap (java.util.HashMap)4