Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

after_commit does not work as expected in nested transactions #536

Closed
averell23 opened this issue Feb 28, 2018 · 9 comments
Closed

after_commit does not work as expected in nested transactions #536

averell23 opened this issue Feb 28, 2018 · 9 comments

Comments

@averell23
Copy link

averell23 commented Feb 28, 2018

We are using aasm with activerecord and mysql/postgres for persistence. Today I found that the after_commit hook does not do what it says on the label, when you are in a nested transaction.

What happens

The hook is called after the aasm_transaction is complete, but before the outermost transaction is completed. This means that it will be called after a RELEASE SAVEPOINT but before the database transaction is COMMITed to the database.

What I expected

I had expected that the hook is only called after the database transaction COMMIT is done, that is, after the outermost database transaction is complete. This is also how the ActiveRecord after_commit hook works.

Was this means

Currently in an after_commit callback

  • The database state may only be visible from the current thread (e.g. not in asynchronous processes)
  • The current state may not actually be persisted, and can still be rolled back if an outer transaction fails

See the code

event.fire_callbacks(:after_commit, self, *args)

@caalberts
Copy link

@averell23 I'm also facing this issue in my application. I'm curious if you managed to work around this issue, and how.

I have an aasm after_commit hook which spawns a sidekiq background job, taking in the ActiveRecord object id. Something like: SomeJob.perform_async(self.id)

I have occasionally encountered situation where the job is picked up by Sidekiq worker, before the object is persisted to the database, and the job fails with error saying ActiveRecord::RecordNotFound: Couldn't find Object with 'id'=123.

@averell23
Copy link
Author

You can mitigate by not having calling the state change within another transaction, the hook works at expected if you call it outside a db transaction.

You could also work with the ActiveRecord.after_commit hook, but this would involve some manual work...

@FX-HAO
Copy link

FX-HAO commented May 21, 2019

A workaround

class AfterCommitWrap
  # If not in a transaction, the block will be executed instantly.
  def self.after_commit(&block)
    ActiveRecord::Base.transaction do
      ActiveRecord::Base.connection.add_transaction_record(new(&block))
    end
  end
end

event :complete, after_commit: :after_completion do
  transitions from: %i[checkout processing], to: :completed
end

def after_completion
  AfterCommitWrap.after_commit do
    # your code here
  end
end

I'd like someone could help to fix this issue :)

@stokarenko
Copy link
Contributor

#666 has been reverted.

#668 will fix the issue once again, but without explicit AR dependency.

@mquan
Copy link

mquan commented Jul 31, 2020

Any idea when a new version with the fix will be released?

@anilmaurya
Copy link
Member

@mquan Thank you for the reminder

Just released AASM 5.1.0 with the fix

@yugandharbandi2220
Copy link

yugandharbandi2220 commented Sep 6, 2021

@anilmaurya It's not working for me even after upgrading to the latest version. In my case, I'm doing multiple saves inside a single transaction. And, the after_commit hook is getting triggered before COMMIT

@stokarenko
Copy link
Contributor

@anilmaurya did you added after_commit_everywhere gem as explicit Gemfile dependency?

As per Readme,

Please note that :after_commit AASM callbacks behaves around custom implementation of transaction pattern rather than a real-life DB transaction. This fact still causes the race conditions and redundant callback calls within nested transaction. In order to fix that it's highly recommended to add gem 'after_commit_everywhere', '~> 1.0' to your Gemfile.

@yugandharbandi2220
Copy link

@stokarenko my bad, missed this. It's working after adding the dependency. Thanks for the help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants