class ActiveRecord::Transaction

systems may lead to consistency issues.
won’t be rolled back as it was already committed. Relying solely on these to synchronize state between multiple
When using after_commit callbacks, it is important to note that if the callback raises an error, the transaction
== Caveats
immediately.
after the transaction is successfully committed, and if called outside a transaction, the callback will be invoked
In the above example, if publish_article is called inside a transaction, the callback will be invoked
end
end
NotificationService.article_published(article)
Article.current_transaction.after_commit do
article.update!(published: true)
def publish_article(article)
with transactions:
The callbacks offered by ActiveRecord::Transaction allow to rewriting this method in a way that is compatible
end
publish_article(article)
article = create_article(article)
Article.transaction do
a transaction, as it will interact with the remote system before the changes are persisted:
The above code works but has one important flaw, which is that it no longer works properly if called inside
end
NotificationService.article_published(article)
article.update!(published: true)
def publish_article(article)
changes in a remote system like clearing or updating a cache:
After updating the database state, you may sometimes need to perform some extra work, or reflect these
== Callbacks
Closed transactions are ‘blank?` too.
end
# We are inside a real and not finalized transaction.
if Article.current_transaction.open?
closed? predicates:
You can check whether a transaction is open or closed with the open? and
one.
when it represents absence of transaction, or it wraps a real but finalized
On the other hand, a transaction is closed when it is not open. That is,
A transaction is open if it wraps a real transaction that is not finalized.
that has been either committed or rolled back.
We say that a transaction is finalized when it wraps a real transaction
== State
absence of a transaction.
It can either map to an actual transaction/savepoint, or represent the
Class specifies the interface to interact with the current transaction state.

def after_commit(&block)

If the callback raises an error, the transaction remains committed.

is rolled back. This operation is repeated until the outermost transaction is reached.
the parent when the current transaction commits, or dropped when the current transaction
If the transaction has a parent transaction, the callback is transferred to

to register the callback raises ActiveRecord::ActiveRecordError.
immediately, unless the transaction is finalized, in which case attempting
If there is no currently open transactions, the block is called

Registers a block to be called after the transaction is fully committed.
def after_commit(&block)
  if @internal_transaction.nil?
    yield
  else
    @internal_transaction.after_commit(&block)
  end
end

def after_rollback(&block)

will raise ActiveRecord::ActiveRecordError.
If the transaction is already finalized, attempting to register a callback

the block is never called.
If the entire chain of nested transactions are all successfully committed,

transaction, the callback is automatically added to the parent transaction.
If the transaction is successfully committed but has a parent

raises ActiveRecord::ActiveRecordError.
if the transaction is finalized, attempting to register the callback
If there is no currently open transactions, the block is not called. But

Registers a block to be called after the transaction is rolled back.
def after_rollback(&block)
  @internal_transaction&.after_rollback(&block)
end

def closed?

Returns true if the transaction doesn't exist or is finalized.
def closed?
  @internal_transaction.nil? || @internal_transaction.state.finalized?
end

def initialize(internal_transaction) # :nodoc:

:nodoc:
def initialize(internal_transaction) # :nodoc:
  @internal_transaction = internal_transaction
  @uuid = nil
end

def open?

Returns true if the transaction exists and isn't finalized yet.
def open?
  !closed?
end

def uuid

Returns a UUID for this transaction or +nil+ if no transaction is open.
def uuid
  if @internal_transaction
    @uuid ||= Digest::UUID.uuid_v4
  end
end