com.palantir.atlasdb.transaction.api.TransactionManager Maven / Gradle / Ivy
Show all versions of atlasdb-api Show documentation
/*
* (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.palantir.atlasdb.transaction.api;
import com.palantir.atlasdb.cell.api.DdlManager;
import com.palantir.atlasdb.cleaner.api.Cleaner;
import com.palantir.atlasdb.keyvalue.api.KeyValueService;
import com.palantir.atlasdb.keyvalue.api.watch.LockWatchManager;
import com.palantir.atlasdb.metrics.Timed;
import com.palantir.atlasdb.transaction.service.TransactionService;
import com.palantir.exception.NotInitializedException;
import com.palantir.lock.HeldLocksToken;
import com.palantir.lock.LockRequest;
import com.palantir.lock.LockService;
import com.palantir.lock.v2.TimelockService;
import com.palantir.processors.AutoDelegate;
import com.palantir.processors.DoDelegate;
import com.palantir.processors.DoNotDelegate;
import com.palantir.timestamp.TimestampManagementService;
import com.palantir.timestamp.TimestampService;
import java.util.List;
import java.util.function.Supplier;
@AutoDelegate
public interface TransactionManager extends AutoCloseable {
/**
* Whether this transaction manager has established a connection to the backing store and timestamp/lock services,
* and is ready to service transactions.
*
* If an attempt is made to execute a transaction when this method returns {@code false}, a
* {@link NotInitializedException} will be thrown.
*
* This method is used for TransactionManagers that can be initializeppd asynchronously (i.e. those extending
* {@link com.palantir.async.initializer.AsyncInitializer}; other TransactionManagers can keep the default
* implementation, and return true (they're trivially fully initialized).
*
* @return true if and only if the TransactionManager has been fully initialized
*/
@DoDelegate
default boolean isInitialized() {
return true;
}
/**
* Runs the given {@link TransactionTask}. If the task completes successfully
* and does not call {@link Transaction#commit()} or {@link Transaction#abort()},
* {@link Transaction#commit()} is called automatically.
*
* The task is re-run if a conflict is detected (if a {@link TransactionConflictException} is thrown)
*
* If runTaskWithRetry
completes successfully (no exception is thrown)
* and the task did not explicitly
* abort the transaction, then the transaction was successfully committed.
* If an exception is thrown by the TransactionTask
and the task did not call
* {@link Transaction#commit()}, then the transaction will be rolled back.
*
* NOTE: If an exception is thrown by {@link Transaction#commit()}, the transaction might have
* been committed.
*
* It is important that the {@link TransactionTask} does not modify any of its input state
* in any non-idempotent way. If this task gets retried, and if you modified your input, then the
* second try might not do the right thing. For example: if you are passed a list of objects
* and at the end of the {@link TransactionTask}, you clear the list. If your task gets retried
* it will have no work to do, because the list was cleared.
*
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithRetry(TransactionTask task) throws E;
/**
* {@link #runTaskWithRetry(TransactionTask)} should be preferred over
* {@link #runTaskThrowOnConflict(TransactionTask)}.
* This method should be used unless {@link #runTaskWithRetry(TransactionTask)} cannot be used because the arguments
* passed are not immutable and will be modified by the transaction so doing automatic retry is unsafe.
*
* Runs the given {@link TransactionTask}. If the task completes successfully
* and does not call {@link Transaction#commit()} or {@link Transaction#abort()},
* {@link Transaction#commit()} is called automatically.
*
* If runTaskThrowOnConflict()
completes successfully (no exception is thrown)
* and the task did not explicitly
* abort the transaction, then the transaction was successfully committed.
* If an exception is thrown by the TransactionTask
and the task did not call
* {@link Transaction#commit()}, then the transaction will be rolled back.
*
* NOTE: If an exception is thrown by {@link Transaction#commit()}, the transaction might have
* been committed.
*
* @param task task to run
*
* @return value returned by task
*
* @throws TransactionConflictException if a write-write conflict occurs
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskThrowOnConflict(TransactionTask task)
throws E, TransactionFailedRetriableException;
/**
* This will open and run a read-only transaction. Read-only transactions are similar to other
* transactions, but will throw if any write operations are called. Furthermore, they often
* make fewer network calls than their read/write counterparts so should be used where possible.
*
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskReadOnly(TransactionTask task) throws E;
/**
* This method is basically the same as {@link #runTaskWithRetry(TransactionTask)} but it will
* acquire locks right before the transaction is created and release them after the task is complete.
* If the lock request provided has a blocking mode of type {@link com.palantir.lock.BlockingMode#DO_NOT_BLOCK},
* the lock request will NOT be retried if it fails. Otherwise, the lock request will be retried on failure for a
* finite number of times (though users must of course be prepared for the case where all retries fail).
*
* The created transaction will not commit successfully if these locks are invalid by the time commit is run.
*
* @param lockSupplier supplier for the lock request
* @param task task to run
*
* @return value returned by task
*
* @throws LockAcquisitionException If the supplied lock request is not successfully acquired.
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithLocksWithRetry(
Supplier lockSupplier, LockAwareTransactionTask task)
throws E, InterruptedException, LockAcquisitionException;
/**
* This is the same as {@link #runTaskWithLocksWithRetry(Supplier, LockAwareTransactionTask)}, but instead
* takes in a Guava supplier. This is deprecated in favour of the aforementioned method.
*
* @deprecated use {@link #runTaskWithLocksWithRetry(Supplier, LockAwareTransactionTask)} instead.
* @see #runTaskWithLocksWithRetry(Supplier, LockAwareTransactionTask)
*/
@DoNotDelegate
@Deprecated
@Timed
default T runTaskWithLocksWithRetry(
com.google.common.base.Supplier guavaSupplier, LockAwareTransactionTask task)
throws E, InterruptedException, LockAcquisitionException {
Supplier javaSupplier = guavaSupplier;
return runTaskWithLocksWithRetry(javaSupplier, task);
}
/**
* This method is the same as {@link #runTaskWithLocksWithRetry(Supplier, LockAwareTransactionTask)}
* but it will also ensure that the existing lock tokens passed are still valid before committing.
*
* @param lockTokens lock tokens to acquire while transaction executes
* @param task task to run
*
* @return value returned by task
*
* @throws LockAcquisitionException If the supplied lock request is not successfully acquired.
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithLocksWithRetry(
Iterable lockTokens,
Supplier lockSupplier,
LockAwareTransactionTask task)
throws E, InterruptedException, LockAcquisitionException;
/**
* This is the same as {@link #runTaskWithLocksWithRetry(Iterable, Supplier, LockAwareTransactionTask)}, but instead
* takes in a Guava supplier. This is deprecated in favour of the aforementioned method.
*
* @deprecated use {@link #runTaskWithLocksWithRetry(Iterable, Supplier, LockAwareTransactionTask)} instead.
* @see #runTaskWithLocksWithRetry(Iterable, Supplier, LockAwareTransactionTask)
*/
@DoNotDelegate
@Deprecated
@Timed
default T runTaskWithLocksWithRetry(
Iterable lockTokens,
com.google.common.base.Supplier guavaSupplier,
LockAwareTransactionTask task)
throws E, InterruptedException, LockAcquisitionException {
Supplier javaSupplier = guavaSupplier;
return runTaskWithLocksWithRetry(lockTokens, javaSupplier, task);
}
/**
* This method is the same as {@link #runTaskThrowOnConflict(TransactionTask)} except the created transaction
* will not commit successfully if these locks are invalid by the time commit is run.
*
* @param lockTokens lock tokens to refresh while transaction executes
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithLocksThrowOnConflict(
Iterable lockTokens, LockAwareTransactionTask task)
throws E, TransactionFailedRetriableException;
/**
* This method is basically the same as {@link #runTaskWithRetry(TransactionTask)}, but it will
* acquire a {@link PreCommitCondition} right before the transaction is created and check it
* immediately before the transaction commits.
*
* The created transaction will not commit successfully if the check fails.
*
* @param conditionSupplier supplier for the condition
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithConditionWithRetry(
Supplier conditionSupplier, ConditionAwareTransactionTask task) throws E;
/**
* This is the same as {@link #runTaskWithConditionWithRetry(Supplier, ConditionAwareTransactionTask)}, but instead
* takes in a Guava supplier. This is deprecated in favour of the aforementioned method.
*
* @deprecated use {@link #runTaskWithConditionWithRetry(Supplier, ConditionAwareTransactionTask)} instead.
* @see #runTaskWithConditionWithRetry(Supplier, ConditionAwareTransactionTask)
*/
@DoNotDelegate
@Deprecated
@Timed
default T runTaskWithConditionWithRetry(
com.google.common.base.Supplier guavaSupplier, ConditionAwareTransactionTask task) throws E {
Supplier javaSupplier = guavaSupplier;
return runTaskWithConditionWithRetry(javaSupplier, task);
}
/**
* This method is basically the same as {@link #runTaskThrowOnConflict(TransactionTask)}, but it takes
* a {@link PreCommitCondition} and checks it immediately before the transaction commits.
*
* The created transaction will not commit successfully if the check fails.
*
* @param condition condition associated with the transaction
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithConditionThrowOnConflict(
C condition, ConditionAwareTransactionTask task) throws E, TransactionFailedRetriableException;
/**
* This method is basically the same as {@link #runTaskReadOnly(TransactionTask)}, but it takes
* a {@link PreCommitCondition} and checks it for validity before executing reads.
*
* The created transaction will fail if the check is no longer valid after fetching the read
* timestamp.
*
* @param condition condition associated with the transaction
* @param task task to run
*
* @return value returned by task
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
T runTaskWithConditionReadOnly(
C condition, ConditionAwareTransactionTask task) throws E;
/**
* Most AtlasDB TransactionManagers will provide {@link Transaction} objects that have less than full
* serializability. The most common is snapshot isolation (SI). SI has a start timestamp and a commit timestamp
* and an open transaction can only read values that were committed before its start timestamp.
*
* This method will return a timestamp that is before any uncommited/aborted open start timestamps.
*
* Subsequent calls to this method will always be monotonically increasing for a single client.
*
* You are only allowed to open historic/read-only transactions at a timestamp less than or equal to the
* immutableTimestamp
*
* @return the latest timestamp for which there are no open transactions
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
long getImmutableTimestamp();
/**
* Returns the lock service used by this transaction manager.
*
* @return the lock service for this transaction manager
*/
LockService getLockService();
/**
* Returns the timelock service used by this transaction manager.
*
* @return the timelock service for this transaction manager
*/
TimelockService getTimelockService();
LockWatchManager getLockWatchManager();
/**
* Returns the timestamp service used by this transaction manager.
*
* @return the timestamp service for this transaction manager
*/
TimestampService getTimestampService();
/**
* The timestamp management service is used by libraries providing additional functionality
* around AtlasDB. End-user clients probably should not require it.
*
* @return the timestamp management service for this transaction manager
*/
TimestampManagementService getTimestampManagementService();
/**
* The transaction service is used by libraries providing additional functionality around AtlasDB.
* End-user clients probably should not require it.
* Abuse of the transaction service, especially involving putting new records in, may result in severe and
* irrecoverable data corruption.
*
* @return the transaction service for this transaction manager
*/
TransactionService getTransactionService();
/**
* Returns the cleaner used by this transaction manager.
*
* @return the cleaner for this transaction manager
*/
Cleaner getCleaner();
/**
* Returns the KVS used by this transaction manager. In general, this should not be used by clients, as
* direct reads and writes to the KVS will bypass the Atlas transaction protocol.
*
* Some implementations cannot support this interface and may choose to throw here.
*
* @return the key value service for this transaction manager.
*/
KeyValueService getKeyValueService();
/**
* Returns the DdlManager used by this transaction manager. This should be used in place of the KeyValueService
* when creating tables or making other schema changes.
*
* @return the DdlManager for this transaction manager.
*/
DdlManager getDdlManager();
/**
* Provides a {@link KeyValueServiceStatus}, indicating the current availability of the key value store.
* This can be used to infer product health - in the usual, conservative case, products can call
* {@link KeyValueServiceStatus#isHealthy()}, which returns true only if all KVS nodes are up.
*
* Products that use AtlasDB only for reads and writes (no schema mutations or deletes, including having sweep and
* scrub disabled) can also treat {@link KeyValueServiceStatus#HEALTHY_BUT_NO_SCHEMA_MUTATIONS_OR_DELETES} as
* healthy.
*
* This call must be implemented so that it completes synchronously.
*/
KeyValueServiceStatus getKeyValueServiceStatus();
/**
* Provides a {@link TimelockServiceStatus}, indicating the current availability of the timelock service.
* This can be used to infer product health - in the usual, conservative case, products can call
* {@link TimelockServiceStatus#isHealthy()}, which returns true only a healthy connection to timelock
* service is established.
*
* @return status of the timelock service
*/
TimelockServiceStatus getTimelockServiceStatus();
/**
* Returns the timestamp that is before any open start timestamps. This is different from the immutable
* timestamp, because it takes into account open read-only transactions. There is likely to be NO
* running transactions open at a timestamp before the unreadable timestamp, however this cannot be guaranteed.
*
* When using the unreadable timestamp for cleanup it is important to leave a sentinel value behind at a negative
* timestamp so any transaction that is open will fail out if reading a value that is cleaned up instead of just
* getting back no data. This is needed to ensure that all transactions either produce correct values or fail.
* It is not an option to return incorrect data.
*
* @return the timestamp that is before any open start timestamps
*
* @throws IllegalStateException if the transaction manager has been closed.
*/
@Timed
long getUnreadableTimestamp();
/**
* Clear the timestamp cache. This is mostly useful for tests that perform operations that would invalidate
* the cache, although this can also be used to free up some memory.
*/
void clearTimestampCache();
/**
* Registers a Runnable that will be run after the transaction manager is closed.
*
* Concurrency: If this method races with close(), then closingCallback may not be called.
*/
void registerClosingCallback(Runnable closingCallback);
/**
* This method can be used for direct control over the lifecycle of a batch of transactions. For example, if the
* work done in each given transaction is interactive and cannot be expressed as a {@link TransactionTask} ahead of
* time, this method allows for a long lived transaction object. For any data read or written to the transaction to
* be valid, the transaction must be committed by calling {@link OpenTransaction#finish(TransactionTask)} to
* also perform additional cleanup. Note that this does not clean up the pre commit condition associated with that
* task. The order of transactions returned corresponds with the pre commit conditions passed in, however there are
* no guarantees on the ordering of transactions returned with respect to their start timestamp.
*
* @return a batch of transactions with associated immutable timestamp locks
* @deprecated Similar functionality will exist, but this method is likely to change in the future
*/
@Deprecated
@Timed
List startTransactions(List extends PreCommitCondition> condition);
/**
* Frees resources used by this TransactionManager, and invokes any callbacks registered to run on close.
* This includes the cleaner, the key value service (and attendant thread pools), and possibly the lock service.
*
* All callbacks will execute in the reverse order of registration, regardless
* of any exceptions thrown. If any exceptions occur, they will be collected and rethrown as a new exception
* with any exceptions that occurred set as suppressed exceptions.
*
* Concurrency: If this method races with registerClosingCallback(closingCallback), then closingCallback
* may be called (but is not necessarily called). Callbacks registered before the invocation of close() are
* guaranteed to be executed.
*/
@Override
void close();
}