Interface Transaction

  • All Superinterfaces:
    java.lang.AutoCloseable, ReadTransaction, ReadTransactionContext, TransactionContext

    public interface Transaction
    extends java.lang.AutoCloseable, ReadTransaction, TransactionContext
    A Transaction represents a FoundationDB database transaction. All operations on FoundationDB take place, explicitly or implicitly, through a Transaction.

    In FoundationDB, a transaction is a mutable snapshot of a database. All read and write operations on a transaction see and modify an otherwise-unchanging version of the database and only change the underlying database if and when the transaction is committed. Read operations do see the effects of previous write operations on the same transaction. Committing a transaction usually succeeds in the absence of conflicts.

    Transactions group operations into a unit with the properties of atomicity, isolation, and durability. Transactions also provide the ability to maintain an application's invariants or integrity constraints, supporting the property of consistency. Together these properties are known as ACID.

    Transactions are also causally consistent: once a transaction has been successfully committed, all subsequently created transactions will see the modifications made by it. The most convenient way for a developer to manage the lifecycle and retrying of a Transaction is to use Database.run(Function). Otherwise, the client must have retry logic for fatal failures, failures to commit, and other transient errors.

    Keys and values in FoundationDB are byte arrays. To encode other data types, see the Tuple API and tuple layer documentation.

    When used as a TransactionContext, the methods run() and runAsync() on a Transaction will simply attempt the operations without any retry loop.

    Note: Client must call commit() and wait on the result on all transactions, even ones that only read. This is done automatically when using the retry loops from Database.run(Function). This is because outstanding reads originating from a Transaction will be cancelled when a Transaction is garbage collected. Since the garbage collector reserves the right to collect an in-scope object if it determines that there are no subsequent references it it, this can happen in seemingly innocuous situations. CompletableFutures returned from commit() will block until all reads are complete, thereby saving the calling code from this potentially confusing situation.

    Note: All keys with a first byte of 0xff are reserved for internal use.

    Note: Java transactions automatically set the TransactionOptions.setUsedDuringCommitProtectionDisable() option. This is because the Java bindings disallow use of Transaction objects after onError(java.lang.Throwable) is called.

    Note: Transaction objects must be closed when no longer in use in order to free any associated resources.
    • Method Detail

      • addReadConflictRange

        void addReadConflictRange​(byte[] keyBegin,
                                  byte[] keyEnd)
        Adds a range of keys to the transaction's read conflict ranges as if you had read the range. As a result, other transactions that write a key in this range could cause the transaction to fail with a conflict.
        Parameters:
        keyBegin - the first key in the range (inclusive)
        keyEnd - the ending key for the range (exclusive)
      • addReadConflictKey

        void addReadConflictKey​(byte[] key)
        Adds a key to the transaction's read conflict ranges as if you had read the key. As a result, other transactions that concurrently write this key could cause the transaction to fail with a conflict.
        Parameters:
        key - the key to be added to the read conflict range set
      • addWriteConflictRange

        void addWriteConflictRange​(byte[] keyBegin,
                                   byte[] keyEnd)
        Adds a range of keys to the transaction's write conflict ranges as if you had cleared the range. As a result, other transactions that concurrently read a key in this range could fail with a conflict.
        Parameters:
        keyBegin - the first key in the range (inclusive)
        keyEnd - the ending key for the range (exclusive)
      • addWriteConflictKey

        void addWriteConflictKey​(byte[] key)
        Adds a key to the transaction's write conflict ranges as if you had written the key. As a result, other transactions that concurrently read this key could fail with a conflict.
        Parameters:
        key - the key to be added to the range
      • set

        void set​(byte[] key,
                 byte[] value)
        Sets the value for a given key. This will not affect the database until commit() is called.
        Parameters:
        key - the key whose value is to be set
        value - the value to set in the database
        Throws:
        java.lang.IllegalArgumentException - if key or value is null
        FDBException - if the set operation otherwise fails
      • clear

        void clear​(byte[] key)
        Clears a given key from the database. This will not affect the database until commit() is called.
        Parameters:
        key - the key whose value is to be cleared
        Throws:
        java.lang.IllegalArgumentException - if key is null
        FDBException - if clear operation otherwise fails
      • clear

        void clear​(byte[] beginKey,
                   byte[] endKey)
        Clears a range of keys in the database. The upper bound of the range is exclusive; that is, the key (if one exists) that is specified as the end of the range will NOT be cleared as part of this operation. Range clears are efficient with FoundationDB -- clearing large amounts of data will be fast. This will not affect the database until commit() is called.
        Parameters:
        beginKey - the first clear
        endKey - the key one past the last key to clear
        Throws:
        java.lang.IllegalArgumentException - if beginKey or endKey is null
        FDBException - if the clear operation otherwise fails
      • clear

        void clear​(Range range)
        Clears a range of keys in the database. The upper bound of the range is exclusive; that is, the key (if one exists) that is specified as the end of the range will NOT be cleared as part of this operation. Range clears are efficient with FoundationDB -- clearing large amounts of data will be fast. However, this will not immediately free up disk - data for the deleted range is cleaned up in the background. This will not affect the database until commit() is called.
        For purposes of computing the transaction size, only the begin and end keys of a clear range are counted. The size of the data stored in the range does not count against the transaction size limit.
        Parameters:
        range - the range of keys to clear
        Throws:
        FDBException - if the clear operation fails
      • clearRangeStartsWith

        @Deprecated
        void clearRangeStartsWith​(byte[] prefix)
        Deprecated.
        Replace with calls to clear(Range) with a parameter from a call to Range.startsWith(byte[]).
        Parameters:
        prefix - the starting bytes from the keys to be cleared.
        Throws:
        FDBException - if the clear-range operation fails
      • mutate

        void mutate​(MutationType optype,
                    byte[] key,
                    byte[] param)
        An atomic operation is a single database command that carries out several logical steps: reading the value of a key, performing a transformation on that value, and writing the result. Different atomic operations perform different transformations. Like other database operations, an atomic operation is used within a transaction.

        Atomic operations do not expose the current value of the key to the client but simply send the database the transformation to apply. In regard to conflict checking, an atomic operation is equivalent to a write without a read. It can only cause other transactions performing reads of the key to conflict.

        By combining these logical steps into a single, read-free operation, FoundationDB can guarantee that the transaction will not conflict due to the operation. This makes atomic operations ideal for operating on keys that are frequently modified. A common example is the use of a key-value pair as a counter.

        Note: If a transaction uses both an atomic operation and a serializable read on the same key, the benefits of using the atomic operation (for both conflict checking and performance) are lost. The behavior of each MutationType is documented at its definition.
        Parameters:
        optype - the operation to perform
        key - the target of the operation
        param - the value with which to modify the key
      • commit

        java.util.concurrent.CompletableFuture<java.lang.Void> commit()
        Commit this Transaction. See notes in class description. Consider using Database's run() calls for managing transactional access to FoundationDB.

        As with other client/server databases, in some failure scenarios a client may be unable to determine whether a transaction succeeded. In these cases, an FDBException will be thrown with error code commit_unknown_result (1021). The onError(java.lang.Throwable) function regards this exception as a retryable one, so retry loops that don't specifically detect commit_unknown_result could end up executing a transaction twice. For more information, see the FoundationDB Developer Guide documentation.

        If any operation is performed on a transaction after a commit has been issued but before it has returned, both the commit and the operation will throw an error code used_during_commit (2017). In this case, all subsequent operations on this transaction will throw this error.

        Returns:
        a CompletableFuture that, when set without error, guarantees the Transaction's modifications committed durably to the database. If the commit failed, it will throw an FDBException.
      • getCommittedVersion

        java.lang.Long getCommittedVersion()
        Gets the version number at which a successful commit modified the database. This must be called only after the successful (non-error) completion of a call to commit() on this Transaction, or the behavior is undefined. Read-only transactions do not modify the database when committed and will have a committed version of -1. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction.
        Returns:
        the database version at which the commit succeeded
      • getVersionstamp

        java.util.concurrent.CompletableFuture<byte[]> getVersionstamp()
        Returns a future which will contain the versionstamp which was used by any versionstamp operations in this transaction. The future will be ready only after the successful completion of a call to commit() on this Transaction. Read-only transactions do not modify the database when committed and will result in the future completing with an error. Keep in mind that a transaction which reads keys and then sets them to their current values may be optimized to a read-only transaction.
        Returns:
        a future containing the versionstamp which was used for any versionstamp operations in this transaction
      • getApproximateSize

        java.util.concurrent.CompletableFuture<java.lang.Long> getApproximateSize()
        Returns a future that will contain the approximated size of the commit, which is the summation of mutations, read conflict ranges, and write conflict ranges. This can be called multiple times before transaction commit.
        Returns:
        a future that will contain the approximated size of the commit.
      • onError

        java.util.concurrent.CompletableFuture<Transaction> onError​(java.lang.Throwable e)
        Resets a transaction and returns a delayed signal for error recovery. If the error encountered by the Transaction could not be recovered from, the returned CompletableFuture will be set to an error state. The current Transaction object will be invalidated by this call and will throw errors when used. The newly reset Transaction will be returned through the CompletableFuture if the error was retryable. If the error is not retryable, then no reset Transaction is returned, leaving this Transaction permanently invalidated.
        Parameters:
        e - the error caught while executing get()s and set()s on this Transaction
        Returns:
        a CompletableFuture to be set with a reset Transaction object to retry the transaction
      • cancel

        void cancel()
        Cancels the Transaction. All pending and any future uses of the Transaction will throw an RuntimeException.
      • watch

        java.util.concurrent.CompletableFuture<java.lang.Void> watch​(byte[] key)
                                                              throws FDBException
        Creates a watch that will become ready when it reports a change to the value of the specified key.

        A watch's behavior is relative to the transaction that created it. A watch will report a change in relation to the key's value as readable by that transaction. The initial value used for comparison is either that of the transaction's read version or the value as modified by the transaction itself prior to the creation of the watch. If the value changes and then changes back to its initial value, the watch might not report the change.

        Until the transaction that created it has been committed, a watch will not report changes made by other transactions. In contrast, a watch will immediately report changes made by the transaction itself. Watches cannot be created if the transaction has set TransactionOptions.setReadYourWritesDisable(), and an attempt to do so will raise a watches_disabled exception.

        If the transaction used to create a watch encounters an exception during commit, then the watch will be set with that exception. A transaction whose commit result is unknown will set all of its watches with the commit_unknown_result exception. If an uncommitted transaction is reset via onError(java.lang.Throwable) or destroyed, then any watches it created will be set with the transaction_cancelled exception.

        By default, each database connection can have no more than 10,000 watches that have not yet reported a change. When this number is exceeded, an attempt to create a watch will raise a too_many_watches exception. Because a watch outlives the transaction that creates it, any watch that is no longer needed should be cancelled.

        NOTE: calling code must call commit() for the watch to be registered with the database.
        Parameters:
        key - the key to watch for changes in value
        Returns:
        a CompletableFuture that will become ready when the value changes
        Throws:
        FDBException - if too many watches have been created on this database. The limit defaults to 10,000 and can be modified with a call to DatabaseOptions.setMaxWatches(long).
      • getDatabase

        Database getDatabase()
        Returns the Database that this Transaction is interacting with.
        Returns:
        the Database object
      • run

        <T> T run​(java.util.function.Function<? super Transaction,​T> retryable)
        Run a function once against this Transaction. This call blocks while user code is executing, returning the result of that code on completion.
        Specified by:
        run in interface TransactionContext
        Type Parameters:
        T - the return type of retryable
        Parameters:
        retryable - the block of logic to execute against this Transaction
        Returns:
        a result of the single call to retryable
      • runAsync

        <T> java.util.concurrent.CompletableFuture<T> runAsync​(java.util.function.Function<? super Transaction,​? extends java.util.concurrent.CompletableFuture<T>> retryable)
        Run a function once against this Transaction. This call returns immediately with a CompletableFuture handle to the result.
        Specified by:
        runAsync in interface TransactionContext
        Type Parameters:
        T - the return type of retryable
        Parameters:
        retryable - the block of logic to execute against this Transaction
        Returns:
        a CompletableFuture that will be set to the return value of retryable
      • close

        void close()
        Close the Transaction object and release any associated resources. This must be called at least once after the Transaction object is no longer in use. This can be called multiple times, but care should be taken that it is not in use in another thread at the time of the call.
        Specified by:
        close in interface java.lang.AutoCloseable