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, orrolled_back. Derived fromresult. - $txn->committed
- $txn->rolled_back
-
True/false/undef booleans derived from
result:committedis true after a successful commit,rolled_backis 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;
1after a successful commit,0after 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_transactionis 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_txnfor that.current_transactionis 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
AUTHORS
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.