NAME

DBIx::QuickORM::Manual::Transactions - A guide to transactions in DBIx::QuickORM.

DESCRIPTION

This guide covers transactions in DBIx::QuickORM: starting a transaction, nesting transactions as savepoints, queuing success / fail / completion callbacks, controlling a transaction by hand, and automatically retrying work when a connection is lost.

Transactions are controlled through the connection (DBIx::QuickORM::Connection). Each transaction or savepoint is represented by a DBIx::QuickORM::Connection::Transaction object.

This is part of the DBIx::QuickORM documentation; see DBIx::QuickORM::Manual for the documentation hub.

BASIC TRANSACTIONS

The simplest way to run a transaction is to pass an action callback to txn() on the connection:

$con->txn(sub {
    my $txn = shift;
    $foo->update(...);
    $bar->insert(...);
});

If the callback returns normally the transaction is committed. If it throws an exception, the transaction is rolled back and the exception propagates.

transaction() is a full alias for txn(); use whichever reads better.

txn() always returns a DBIx::QuickORM::Connection::Transaction object. When you pass an action callback the returned object is already complete, so you can inspect its outcome (see "THE TRANSACTION OBJECT" below).

MANUAL CONTROL FROM INSIDE THE CALLBACK

The callback receives the transaction object as its only argument. You can end the transaction early by calling commit or rollback on it; either one breaks out of the action callback:

$con->txn(sub {
    my $txn = shift;
    ...
    $txn->rollback if $should_abort;     # exits the callback, rolls back
    ...
    $txn->commit("looks good");          # exits the callback, commits
});

abort is an alias for rollback. Both commit and rollback accept an optional reason string.

NESTED TRANSACTIONS AND SAVEPOINTS

Calls to txn() may be nested. The outermost call starts a real database transaction; each nested call creates a savepoint instead. Committing an inner transaction releases its savepoint; rolling one back rolls back to its savepoint without disturbing the outer transaction.

$con->txn(sub {            # real transaction
    $foo->insert(...);

    $con->txn(sub {        # savepoint
        $bar->insert(...);
        # rolling this back undoes only $bar's insert
    });

    $baz->insert(...);
});                        # commit happens here

Use is_savepoint on a transaction object to tell which kind it is.

CALLBACKS

You can queue callbacks to run when a transaction finishes. They are fired after the underlying commit or rollback has been issued.

on_success

Runs only if the transaction commits.

on_fail

Runs only if the transaction is rolled back.

on_completion

Runs when the transaction finishes, regardless of outcome.

Pass them as parameters to txn():

$con->txn(
    on_success    => sub { my $txn = shift; ... },
    on_fail       => sub { my $txn = shift; ... },
    on_completion => sub { my $txn = shift; ... },
    action        => sub { my $txn = shift; ... },
);

You can also queue callbacks against an existing transaction object directly with add_success_callback, add_fail_callback, and add_completion_callback.

PARENT AND ROOT CALLBACKS

When you are inside a nested transaction you can attach callbacks to an outer transaction instead of the current one. This is useful when a savepoint wants to defer work until the enclosing transaction actually commits.

The on_parent_* variants attach to the immediate parent transaction; the on_root_* variants attach to the outermost (root) transaction no matter how deeply nested you are:

$con->txn(
    on_parent_success    => sub { ... },
    on_parent_fail       => sub { ... },
    on_parent_completion => sub { ... },

    on_root_success    => sub { ... },
    on_root_fail       => sub { ... },
    on_root_completion => sub { ... },

    action => sub { ... },
);

When there is no parent or root transaction above the current one, the corresponding parent / root callbacks are simply no-ops.

LONG-LIVED TRANSACTIONS

If you need a transaction that is not bound to a single callback, call txn() without an action. It returns a live DBIx::QuickORM::Connection::Transaction object that you control by hand:

my $txn = $con->txn();
...
$txn->commit;    # or $txn->rollback;

If the transaction object falls completely out of scope and is destroyed while still open, it is rolled back automatically as a safety net. Never rely on this for normal flow; commit or roll back explicitly.

THE TRANSACTION OBJECT

A DBIx::QuickORM::Connection::Transaction exposes its state:

$txn->state

Returns one of active, committed, or rolled_back. Derived from result.

$txn->committed
$txn->rolled_back

True/false/undef booleans derived from result: committed is true after a successful commit, rolled_back is its inverse, and both return undef while the transaction is still open.

$txn->exception

The exception that forced a rollback, if any. Set when the transaction body threw or the object fell out of scope; undef for a normal commit or an explicit rollback.

$txn->is_savepoint

True when this transaction is a savepoint (i.e. it was nested inside another transaction).

$txn->result

Undef while the transaction is still open; 1 after a successful commit, 0 after a rollback.

See DBIx::QuickORM::Connection::Transaction for the full interface.

INSPECTING THE CURRENT TRANSACTION

The connection can tell you whether a transaction is active:

$con->in_txn

Returns true if any transaction is active. If the transaction is managed by DBIx::QuickORM the transaction object is returned; if a transaction is open but not managed by the ORM, a plain true value is returned instead. in_transaction is an alias.

$con->current_txn

Returns the current DBIx::QuickORM::Connection::Transaction, or undef if none is active. Note that this returns undef for transactions not managed by the ORM, so do not use it as a generic "am I in a transaction" check; use in_txn for that. current_transaction is an alias.

AUTOMATIC RETRY

Some failures (notably a dropped connection) are worth retrying. Two helpers on the connection handle this.

auto_retry runs a callback, retrying it until it succeeds or a retry count is exhausted. If the connection appears to be down between attempts it reconnects before retrying. The default count is 1 (so up to two attempts total). It returns whatever the callback returns (in scalar context).

my $result = $con->auto_retry(sub { ... });
my $result = $con->auto_retry($count, sub { ... });

auto_retry_txn is the transaction-aware convenience form. It runs the action inside a transaction and retries the whole transaction on failure. It is roughly equivalent to:

$con->auto_retry(sub { $con->txn(sub { ... }) });

Usage:

$con->auto_retry_txn(sub { my $txn = shift; ... });
$con->auto_retry_txn(\%params, sub { my $txn = shift; ... });
$con->auto_retry_txn(%params, action => sub { my $txn = shift; ... });

Use count => $NUM to set the maximum number of retries (default 1); all other parameters are passed through to txn().

Important: auto_retry cannot be used inside an open transaction, and will croak if you try. Retrying only makes sense around a whole transaction, not in the middle of one, because a half-applied transaction cannot be safely replayed. Reach for auto_retry_txn when you want the retry to wrap the transaction itself.

TRANSACTIONS AND ROW STATE

Row objects are transaction-aware: a row tracks its data through the transaction / savepoint stack, so changes made inside a transaction are unwound correctly when that transaction (or savepoint) rolls back. The mechanics of this are described elsewhere rather than repeated here; see DBIx::QuickORM::Manual::Concepts for the concepts and DBIx::QuickORM::Connection for the connection-level details.

TRANSACTIONS AND ASYNC QUERIES

Transactions and out-of-band queries interact. You cannot start a transaction while an async query is active, and by default an active aside or forked query also blocks a new transaction (the force, ignore_aside, and ignore_forks parameters to txn() relax this). For the full picture of async, aside, and forked queries and how they relate to transactions, see DBIx::QuickORM::Manual::Async.

SEE ALSO

DBIx::QuickORM::Manual

The documentation hub.

DBIx::QuickORM::Connection

The connection object, where transactions are controlled.

DBIx::QuickORM::Connection::Transaction

The transaction / savepoint object.

DBIx::QuickORM::Manual::Async

Asynchronous, aside, and forked queries and how they interact with transactions.

DBIx::QuickORM::Manual::Concepts

Key concepts, including transaction-aware row state.

SOURCE

The source code repository for DBIx-QuickORM can be found at https://github.com/exodist/DBIx-QuickORM/.

MAINTAINERS

Chad Granum <exodist@cpan.org>

AUTHORS

Chad Granum <exodist@cpan.org>

COPYRIGHT

Copyright Chad Granum <exodist7@gmail.com>.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

See https://dev.perl.org/licenses/