The project is in a healthy, maintained state
Detects external side effects such as HTTP requests, email delivery, and background job enqueuing performed inside ActiveRecord transactions, helping prevent inconsistent state when a database transaction rolls back.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Runtime

 Project Readme

TransactionGuard

TransactionGuard is a Rails / ActiveRecord Ruby gem that detects external side effects inside database transactions — including HTTP requests, email delivery, and background job enqueueing.

Database transactions can roll back database changes, but they cannot automatically roll back those external operations. TransactionGuard helps catch these mismatches during development and testing.

For example:

User.transaction do
  user = User.create!(name: "Yashika")

  SomeExternalApi.create_user(user)
end

If the transaction later rolls back, the external API request cannot automatically be rolled back with it.

Installation

Add the gem to your application's Gemfile:

gem "transaction_guard"

Then run:

bundle install

For local development, you can use the gem directly from a local path:

gem "transaction_guard", path: "../transaction_guard"

Configuration

TransactionGuard supports three modes:

  • :warn — report external side effects
  • :raise — raise an error when a side effect is detected
  • :off — disable detection

The default mode is :warn.

In Rails applications, the included Railtie sets:

  • :warn in development and test
  • :off in production

Override this in an initializer:

# config/initializers/transaction_guard.rb
TransactionGuard.configure do |config|
  config.mode = :warn
end

Warn mode

This is the default:

TransactionGuard.configure do |config|
  config.mode = :warn
end

When an external side effect is detected inside a transaction, TransactionGuard reports a warning including the operation and caller location.

Raise mode

Use :raise when you want to prevent the transaction from continuing after an external side effect is detected:

TransactionGuard.configure do |config|
  config.mode = :raise
end

An invalid configuration value raises an ArgumentError:

TransactionGuard.configure do |config|
  config.mode = :invalid
end

Off mode

Detection can be disabled:

TransactionGuard.configure do |config|
  config.mode = :off
end

HTTP detection

TransactionGuard detects HTTP requests made through Net::HTTP while an ActiveRecord transaction is open.

Example:

User.transaction do
  Net::HTTP.get(URI("https://example.com"))
end

TransactionGuard reports the external HTTP operation.

The detector covers common Net::HTTP methods including:

get
post
put
patch
delete
head
options

Clients that build on Net::HTTP (for example some Faraday adapters) may also be detected. Direct Faraday, HTTParty, httpx, and similar clients are not hooked in 0.1.0.

Email detection

TransactionGuard detects email delivery performed inside an ActiveRecord transaction.

For example:

User.transaction do
  user = User.create!

  TestMailer.welcome(user).deliver_now
end

It also detects:

User.transaction do
  TestMailer.welcome(user).deliver_later
end

deliver_later is reported as an email side effect rather than generating an additional warning for the internal ActiveJob enqueue.

Background job detection

TransactionGuard detects ActiveJob operations performed inside transactions.

Enqueueing a job

User.transaction do
  user = User.create!

  WelcomeJob.perform_later(user.id)
end

This is reported as a job enqueue operation.

Executing a job immediately

User.transaction do
  WelcomeJob.perform_now
end

This is reported as job execution.

Sidekiq, Resque, and other non-ActiveJob APIs are not detected in 0.1.0.

Why does this matter?

Consider:

User.transaction do
  user = User.create!

  WelcomeJob.perform_later(user.id)

  raise ActiveRecord::Rollback
end

The database record is rolled back, but the background job may already have been enqueued.

The job could therefore execute with an ID that no longer exists.

Similar problems can occur with HTTP requests and email delivery.

Recommended alternatives

When an external side effect depends on a successful database transaction, move the operation until after the transaction commits.

For example:

user = User.create!

User.transaction do
  user.update!(status: "active")
end

WelcomeJob.perform_later(user.id)

You can also use after_commit (or enqueue ActiveJob only after a successful commit). That fixes the ordering problem: the side effect no longer runs if the transaction rolls back.

after_commit alone is not fully crash-safe. If the process dies after the commit succeeds but before the callback runs, the email or job may never go out, with no automatic retry. When delivery must be guaranteed, prefer a transactional outbox (record the intent in the same DB transaction) plus a worker or reconciliation job that sends from the outbox, or another reliable event-publishing approach.

Patterns to consider:

  • Run the side effect after the transaction (as in the example above)
  • after_commit — good for ordering; not durable across process failure by itself
  • ActiveJob triggered after a successful commit
  • Transactional outbox + worker / reconciliation
  • Reliable event publishing

TransactionGuard does not automatically move, delay, retry, or otherwise modify external operations. It reports the potentially unsafe operation so the application can decide how to handle it.

Development

Clone the repository and install dependencies:

git clone https://github.com/yashika279/transaction_guard.git
cd transaction_guard
bundle install

Run the test suite:

bundle exec rspec

Run RuboCop:

bundle exec rubocop

Build the gem locally:

bundle exec gem build transaction_guard.gemspec

Contributing

Bug reports, feature ideas, feedback, and pull requests are welcome.

  • Open an issue for bugs, features, or feedback.
  • master is protected — fork the repo, push your branch, and open a PR against master.

See CONTRIBUTING.md for setup, the fork workflow, and the PR checklist. Please make sure tests and RuboCop pass before submitting a pull request.

Security

See SECURITY.md for how to report vulnerabilities.

License

TransactionGuard is available as open source under the MIT License.