Transaction Concepts

    +
    A high-level overview of Distributed ACID Transactions with Couchbase.

    For a practical guide, see Using Couchbase Transactions.

    Overview

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]

    Transaction Mechanics

    cluster.transactions().run((ctx) -> {
        ctx.insert(collection, "doc1", doc1Content);
    
        var doc2 = ctx.get(collection, "doc2");
        ctx.replace(doc2, doc2Content);
    });

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]

    Rollback

    When an exception is thrown, either by the application from the lambda, or by the transactions logic itself (e.g. on a failed operation), then that attempt is rolled back.

    The application’s lambda may or may not be retried, depending on the error that occurred. The general rule for retrying is whether the transaction is likely to succeed on a retry. For example, if this transaction is trying to write a document that is currently involved in another transaction (a write-write conflict), this will lead to a retry as that is likely a transient state. But if the transaction is trying to get a document that does not exist, it will not retry.

    If the transaction is not retried then it will throw a TransactionFailedException, and its getCause method can be used for more details on the failure.

    The application can use this to signal why it triggered a rollback, as so:

    class BalanceInsufficient extends RuntimeException {
    }
    
    try {
        cluster.transactions().run((ctx) -> {
            var customer = ctx.get(collection, "customer-name");
    
            if (customer.contentAsObject().getInt("balance") < costOfItem) {
                throw new BalanceInsufficient();
            }
            // else continue transaction
        });
    } catch (TransactionCommitAmbiguousException e) {
        // This exception can only be thrown at the commit point, after the
        // BalanceInsufficient logic has been passed, so there is no need to
        // check getCause here.
        throw logCommitAmbiguousError(e);
    } catch (TransactionFailedException e) {
        if (e.getCause() instanceof BalanceInsufficient) {
            // Re-raise the error
            throw (RuntimeException) e.getCause();
        } else {
            throw logFailure(e);
        }
    }

    After a transaction is rolled back, it cannot be committed, no further operations are allowed on it, and the SDK will not try to automatically commit it at the end of the code block.

    Transaction Operations

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]

    Concurrency with Non-Transactional Writes

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]

    Custom Metadata Collections

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]

    var keyspace = TransactionKeyspace.create("bucketName", "scopeName", "collectionName");
    
    var cluster = Cluster.connect("localhost", ClusterOptions.clusterOptions("username", "password")
            .environment(env -> env.transactionsConfig(TransactionsConfig.metadataCollection(keyspace))));

    or at an individual transaction level with:

    cluster.transactions().run((ctx) -> {
        // Your transaction logic
    }, transactionOptions().metadataCollection(collection));

    Unresolved include directive in modules/concept-docs/pages/transactions.adoc - include::7.1.2@sdk:shared:partial$acid-transactions.adoc[]