Transaction Strategy
The transaction strategy wraps your action execution in a database transaction:
class TransferFunds
include Axn
use :transaction
expects :from_account, :to_account, :amount
def call
from_account.withdraw!(amount)
to_account.deposit!(amount)
expose :transfer_id, SecureRandom.uuid
end
endImportant: The transaction wraps:
beforehooks- The main
callmethod afterhooks
So the transaction is still open until all of the above complete. If any of them raise or call fail!, the transaction is rolled back.
on_success runs after the enclosing transaction commits. It is the place for work that must only happen once the DB work is durably persisted—calling an external HTTP service, sending email, enqueuing a job. This holds under nesting too: when an action runs inside another action's transaction (or any open ActiveRecord::Base.transaction), its on_success is deferred until the outermost transaction commits, and is skipped entirely if that transaction rolls back. With no open transaction it runs immediately.
Ordering follows from this: nested on_success callbacks fire in child-first order (inner before outer). One consequence to be aware of—because on_success waits for the commit, an outer action's after hooks (which run inside the transaction) execute before an inner action's on_success.
Putting slow or unreliable external calls inside call or after keeps the transaction open until they complete and can block the connection—use on_success instead.
Non-joinable transactions: deferral keys off
ActiveRecord.after_all_transactions_commit, which by design only tracks joinable transactions (the default). An action run directly inside an explicitly non-joinable transaction (ActiveRecord::Base.transaction(joinable: false), with no ordinary transaction between it and the action) is treated as if no transaction were open—itson_successruns immediately. Thetransactionstrategy and ordinaryActiveRecord::Base.transactionblocks are joinable, so nested actions are unaffected. (This is also whyon_successfires normally under Rails' transactional test fixtures, whose outer transaction is non-joinable—the callback runs immediately rather than being suppressed by the fixture rollback.)
Requirements: Requires ActiveRecord to be available in your application.