Skip to content

fix(rails): Only patch background worker when ActiveRecord::Base is defined - #3170

Open
ryanatwork wants to merge 1 commit into
getsentry:masterfrom
ryanatwork:fix/background-worker-partial-active-record
Open

ryanatwork wants to merge 1 commit into
getsentry:masterfrom
ryanatwork:fix/background-worker-partial-active-record

Conversation

@ryanatwork

Copy link
Copy Markdown

Description

Sentry::Railtie applies Sentry::BackgroundWorker::ActiveRecordConnectionPatch whenever ActiveRecord is defined:

patch_background_worker if defined?(ActiveRecord)

The patch then calls ActiveRecord::Base in an ensure after every job:

ensure
  if ActiveRecord::Base.connected?
    ActiveRecord::Base.connection_pool.release_connection
  end

Some apps load only part of ActiveRecord, for example require "active_record/errors" or require "active_record/validations" for the error and validation classes, without active_record/railtie or require "active_record". In those apps ActiveRecord is defined, but ActiveRecord::Base is neither defined nor registered for autoload. So every background-worker job raises NameError in the ensure, and BackgroundWorker#perform logs:

exception happened in background worker: uninitialized constant ActiveRecord::Base

Events are still delivered, because the block runs before the ensure. But the SDK logs an error for every envelope it sends.

This PR checks defined?(ActiveRecord::Base) instead. defined? returns "constant" for the autoload that require "active_record" registers, and it doesn't load active_record/base, so apps with full ActiveRecord still get the patch with no change in load order.

Related: #1618 (ActiveRecord loaded but misconfigured, which led to the connected? guard). That isn't the same problem: here ActiveRecord::Base isn't loaded at all.

Tests

Adds spec/isolated/background_worker_spec.rb. The patch is applied at most once per process, so each case boots a bare app in a child process, like streaming_reporter_spec.rb:

  • With active_record/railtie: the patch is applied, and a job runs without logging an error.
  • With only active_model + active_record/errors: the patch is skipped, and a job runs without logging an error. Without the railtie change this case fails with "patched,ran,error".

The existing connection-release specs in spec/sentry/rails/client_spec.rb still pass for full ActiveRecord apps.

🤖 Generated with Claude Code

…efined

The railtie applied ActiveRecordConnectionPatch whenever ActiveRecord
was defined, but the patch calls ActiveRecord::Base after every job.
Apps that load only part of ActiveRecord (e.g. active_record/errors
without the railtie) have ActiveRecord but not ActiveRecord::Base, so
every job raised NameError in the patch's ensure and the worker logged
"exception happened in background worker: uninitialized constant
ActiveRecord::Base".

Check for ActiveRecord::Base instead. defined? sees the autoload that
require "active_record" registers without loading Base, so full
ActiveRecord apps still get the patch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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

Successfully merging this pull request may close these issues.

1 participant