In this previous ) offers a plug-and-play solution to tackle this case.
The principle is simple: include the AfterCommitEverywhere module, and you'll have access to after_commit, after_rollback, and before_commit "callbacks" wherever you need them:
include AfterCommitEverywhere
class BusinessLogicService
def call(user_id)
user = User.find(user_id)
Post.transaction do
user.posts.update_all(active: false)
user.update!(active: false)
after_commit do
puts 'transaction over!'
# do something
end
puts 'transaction ongoing'
end
end
end
Okay, but how does this work under the hood?
Let's see what a call to after_commit does, in the on the connection
- If
prependoption is passed
- Fetches the array of
records(models with callbacks defined on them) on the connection'scurrent_transaction
- Fetches the array of
- else (general case)
- Wraps the callback in an ActiveRecord model-like class called
Wrap, to be able to register transactional callbacks on it. This is the trick that allows usage of ActiveRecord's native callbacks. - Calls
ActiveRecord::ConnectionAdapters::DatabaseStatements#add_transaction_recordon the connection with the wrapped callback
- Wraps the callback in an ActiveRecord model-like class called
- Yields the callback or raises, depending on config
As you can see, what I like about this gem is that it relies only on ActiveRecord's internals and simply exposes them in a simple, plug-and-play module.
As a conclusion, transactional callbacks offer a powerful tool for maintaining data integrity and ensuring atomicity in complex DB operations.
With the help of after_commit_everywhere, we can take this concept further and use them outside of ActiveRecord models, but, it's important to remember that over-reliance on callbacks can lead to tightly coupled code and potential performance issues.
SOCIAL SHARE CARD GENERATOR