# palkan/isolator: catching HTTP calls and background jobs inside DB transactions

> Isolator is a Ruby gem that raises an error when code inside a database transaction performs a non-atomic side effect, such as an HTTP request or an enqueued job. It is meant for test and staging environments, not production.

**palkan/isolator** — Detect non-atomic interactions within DB transactions

- Repository: https://github.com/palkan/isolator
- Stars: 1,123 · Forks: 32
- Language: Ruby
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/palkan-isolator

## The bug Isolator is built to catch

A transaction is a promise that a block of work either lands or does not. An HTTP call to a payment provider inside that block breaks the promise: the charge can succeed while the commit fails, or the commit can succeed while the charge times out. The same problem appears with background jobs. If a job is enqueued inside a transaction and the transaction rolls back, the worker still runs against a row that never existed. The README shows both cases, including the implicit transaction created by an after_create callback on a Comment model that calls deliver_later.

Isolator exists for people who already know this is wrong but cannot see it happening. Transactions are often implicit: ActiveRecord wraps saves and callbacks in one, and the offending call sits several frames away from the code under review. The README states the gem is supposed to be used in tests and on staging, which tells you the intended audience is application developers and CI pipelines rather than operators.

## How the sniffer and adapters work

Isolator is described in its README as a plug-and-play tool that begins to work as soon as it is required. It hooks into transaction boundaries and into a set of adapters that wrap the libraries most likely to fire during a transaction. The adapters listed are :http (built on top of Sniffer), :active_job, :sidekiq, :resque, :resque_scheduler, :sucker_punch, :mailer, :webmock and :action_cable. When one of those adapters sees activity while a transaction is open, Isolator records an offense and either logs it or raises.

The mechanism is detection, not prevention. Isolator does not stop the HTTP request or cancel the job; it raises after the fact so the test fails loudly. That is the right shape for a test-suite guard, and it is also why the gem is not something to enable in production. The README also exposes lifecycle callbacks, before_isolate and after_isolate, plus on_transaction_begin and on_transaction_end, which receive the connection id and the current transaction depth. Those callbacks are the extension point if you want to build your own reporting on top of the built-in adapters.

One design detail worth noting: Isolator does not distinguish framework-level adapters. The README says the :active_job spy does not take into account which Active Job adapter you use, so a backend that is already safe, such as Que, still trips the spy unless you disable it.

## Installing Isolator and seeing your first offense

The README puts the gem in the development and test group, or outside a group with require: false when you also want it in staging. The second form is the one the README recommends for staging use, because you require it yourself once the application is loaded.

```ruby
# We suppose that Isolator is used in development and test
# environments.
group :development, :test do
  gem "isolator"
end

# Or you can add it to Gemfile with `require: false`
# and require it manually in your code.
gem "isolator", require: false
```

Load order matters. The README says Isolator detects the environment automatically and includes only the necessary adapters, so it must be required at the end; in Rails, all adapters are loaded after application initialization. If you require isolator after database_cleaner, transactional test support works. There is also a documented caveat for APM instrumentation: if you instrument Net::HTTP, the README points to issue 44 and says you may need to force the sniffer into prepend mode.

The simplest first use is to write a test that calls out inside a transaction and confirm the error appears. In the test environment, raise_exceptions is true by default, so the example from the README raises Isolator::HTTPError without any configuration:

```ruby
User.transaction do
  user = User.new(user_params)
  user.save!
  PaymentsService.charge!(user)
end
#=> raises Isolator::HTTPError
```

The configuration block is where you change that default. Setting raise_exceptions to false turns offenses into log entries instead of failures, and config.logger, config.send_notifications, config.backtrace_filter, config.ignorer, config.disallow_per_thread_concurrent_transactions and config.max_subtransactions_depth are the documented knobs.

```ruby
Isolator.configure do |config|
  config.logger = nil
  config.raise_exceptions = false # true in test env
  config.send_notifications = false
  config.backtrace_filter = ->(backtrace) { backtrace.take(5) }
  config.ignorer = Isolator::Ignorer
  config.disallow_per_thread_concurrent_transactions = false
  config.max_subtransactions_depth = 5
end
```

If you use uniform_notifier for notifications, note that the README says it must be installed separately. Adapters can also be toggled at runtime:

```ruby
Isolator.adapters.http.disable!
Isolator.adapters.http.enable!
```

## False positives, ignored offenses and the wrong tool cases

The README is candid that Isolator can produce false positives. Because each adapter is a wrapper over the original code, a second library patching the same behavior can trigger an offense that is not really a violation. The README's example is Sidekiq used with sidekiq-postpone. The documented escape hatch is the ignorer, which by default uses a row-number-based list from a .isolator_todo.yml file.

Two more constraints matter before you adopt this. First, ORM support is narrow: ActiveRecord 6.0 and above, or ROM::SQL only when the Active Support instrumentation extension is loaded. If your application uses Sequel, or an older ActiveRecord, Isolator does not cover you. Second, the README labels multiple-database support experimental as of v0.7.0 and asks users to report issues, so multi-database Rails applications should expect rough edges.

There is also a conceptual limit. Isolator detects calls that go through the libraries it knows about. A hand-rolled HTTP client that bypasses Sniffer, or a job pushed onto a queue through a path no adapter wraps, will not be caught. The :webmock adapter exists precisely because mocked HTTP requests are unseen by Sniffer in tests, which is a useful reminder that adapter coverage, not the transaction itself, defines what Isolator can see.

## Isolator versus moving the work out of the transaction

The obvious alternative is not another gem but a change in structure: move the side effect out of the transaction entirely and run it after commit. The README points at the after_commit callback from the after_commit_everywhere gem for actions that should run only after a successful commit, or the native ActiveRecord callback when a model-level hook is enough. That approach fixes the bug rather than reporting it, and it costs nothing at runtime.

The difference in approach is real. after_commit_everywhere is a correctness mechanism: the job or HTTP call is deferred until the transaction has committed, so the failure mode disappears. Isolator is a detection mechanism: the offending code still runs inside the transaction, and the gem surfaces it as a test failure so a human can fix it. They are not substitutes. If you already use after_commit_everywhere consistently, Isolator still has value as a regression guard against the next developer who adds deliver_later to an after_create callback. If you use neither, Isolator will tell you where the problems are, but it will not fix them.

## Maintenance, licence and upgrade cost

The repository is not archived and the last push was on 2026-09-03, so the project is being touched recently. The most recent release listed is v1.1.0 from 2024-08-12, preceded by v1.0.0 in January 2024 and v0.11.0 in September 2023, so releases are infrequent and the 1.x line has been stable for a while. The gemspec, Gemfile, Rakefile and gemfiles directory in the repository root indicate a standard Ruby gem layout with multiple Gemfile variants for testing against different dependency versions.

The licence is MIT, which permits commercial use and modification; the LICENSE.txt file is at the repository root. This is not legal advice, and you should read the licence text yourself if you plan to redistribute the gem or bundle it into a product. The practical upgrade cost is low for a test-only dependency: the configuration surface is small, and the main churn points are the adapters you have enabled and the ignorer file you maintain. Expect to revisit .isolator_todo.yml entries as the underlying libraries change, since those entries are the accepted false positives.

## Conclusion

Adopt Isolator if you run a Rails or ROM::SQL application and want a test-suite guard against HTTP calls, mailers and job enqueues that sit inside a transaction. Skip it if your only ORM is Sequel or plain ActiveRecord below 6.0, or if you cannot tolerate its false positives around libraries that patch the same code paths. Before wiring it in, check the load order of isolator against database_cleaner and any APM instrumentation, and confirm whether you need to disable the :active_job adapter because Que is already safe.

## FAQ

### What does palkan/isolator do?

It detects non-atomic interactions inside database transactions, such as HTTP calls or background job enqueues, and raises errors like Isolator::HTTPError or Isolator::BackgroundJobError. The README says it is meant for tests and staging.

### How do I install palkan/isolator?

Add the gem to the development and test group in your Gemfile, or add it with require: false if you want to use it in staging. The README notes that load order matters and isolator should be required at the end.

### How do I use palkan/isolator?

Require the gem and it starts working; in the test environment it raises exceptions on offenses by default. You can then configure it with Isolator.configure and enable or disable adapters such as http and active_job.

## Sources

- [Issues](https://github.com/palkan/isolator/issues)
- [License: MIT](https://github.com/palkan/isolator/blob/master/LICENSE)
- [palkan/isolator on GitHub](https://github.com/palkan/isolator)
- [README](https://github.com/palkan/isolator/blob/master/README.md)
- [Releases](https://github.com/palkan/isolator/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/palkan-isolator
