Skip to content

Latest commit

 

History

History
133 lines (106 loc) · 4.39 KB

File metadata and controls

133 lines (106 loc) · 4.39 KB
name transactions-php
summary ACID multi-document transactions with the Couchbase PHP SDK — $cluster->transactions()->run(), TransactionAttemptContext, ctx->get/insert/replace/remove, SQL++ in transactions, error handling, durability
description ACID multi-document transactions with the Couchbase PHP SDK — $cluster->transactions()->run(), TransactionAttemptContext, ctx->get/insert/replace/remove, SQL++ in transactions, error handling, durability
compatibility PHP SDK 4.x. Requires Couchbase Server 6.6.1+ or Capella.
metadata
last_verified min_server_version handoff
2026-05
7.0
condition skill
user asks about SQL++ queries
server-querying-php
condition skill
user asks about CAS, bulk ops, sub-document, or SDK patterns
sdk-patterns-php
condition skill
user asks about error handling, retries, or exception types
error-handling
condition skill
user asks about connection setup or SDK configuration
server-connection-php

Transactions — PHP

Transactions are ACID, multi-document, and automatically retried on transient failures. The lambda may run more than once — keep it idempotent and free of side effects.

Version requirement: Distributed transactions require Couchbase Server 7.0+. The 6.6 developer preview is not recommended for production.

Basic Transaction

use Couchbase\TransactionAttemptContext;
use Couchbase\TransactionOptions;
use Couchbase\Exception\TransactionFailedException;

try {
    $cluster->transactions()->run(function (TransactionAttemptContext $ctx) use ($collection) {
        // Insert
        $ctx->insert($collection, "doc-a", ["status" => "new"]);

        // Get
        $docA = $ctx->get($collection, "doc-a");

        // Replace
        $content = $docA->content();
        $content["status"] = "processed";
        $ctx->replace($docA, $content);

        // Remove
        $docB = $ctx->get($collection, "doc-b");
        $ctx->remove($docB);
    });
    echo "Transaction committed\n";
} catch (TransactionFailedException $e) {
    echo "Transaction failed: " . $e->getMessage() . "\n";
}

Transfer Pattern

try {
    $cluster->transactions()->run(function (TransactionAttemptContext $ctx) use ($collection) {
        $from = $ctx->get($collection, "account::alice");
        $to   = $ctx->get($collection, "account::bob");

        $fromDoc = $from->content();
        $toDoc   = $to->content();
        $amount  = 100;

        if ($fromDoc["balance"] < $amount) {
            throw new \Exception("Insufficient funds");
        }

        $fromDoc["balance"] -= $amount;
        $toDoc["balance"]   += $amount;

        $ctx->replace($from, $fromDoc);
        $ctx->replace($to,   $toDoc);
    });
} catch (TransactionFailedException $e) {
    echo "Transfer failed: " . $e->getMessage() . "\n";
}

SQL++ Inside a Transaction

use Couchbase\TransactionQueryOptions;

$cluster->transactions()->run(function (TransactionAttemptContext $ctx) {
    $ctx->query(
        "UPDATE `travel-sample`.`inventory`.`airline` SET status = 'active' WHERE country = \$1",
        TransactionQueryOptions::build()->positionalParameters(["US"])
    );
});

Durability

use Couchbase\DurabilityLevel;

$opts = TransactionOptions::build()
    ->durabilityLevel(DurabilityLevel::PERSIST_TO_MAJORITY);

$cluster->transactions()->run(function (TransactionAttemptContext $ctx) use ($collection) {
    $ctx->insert($collection, "order::1", ["status" => "pending"]);
}, $opts);

Error Handling

use Couchbase\Exception\TransactionFailedException;
use Couchbase\Exception\TransactionCommitAmbiguousException;

try {
    $cluster->transactions()->run(function (TransactionAttemptContext $ctx) use ($collection) {
        // transaction logic
    });
} catch (TransactionCommitAmbiguousException $e) {
    // Transaction may or may not have committed — check and handle
    echo "Ambiguous commit: " . $e->getMessage() . "\n";
} catch (TransactionFailedException $e) {
    // Transaction did not commit
    echo "Failed: " . $e->getMessage() . "\n";
}

Design Rules

  • Keep transactions under 1 second — long transactions increase conflict probability
  • SDK retries automatically on transient conflicts — don't add your own retry loop
  • No cross-bucket transactions
  • OnUpdate Eventing fires after commit, not within the transaction