Giter VIP home page Giter VIP logo

isolator's Issues

Mailer adapter does not behave as expected

There seem to be two problems, but I'm unsure if they're related. Please let me know if you'd like to split the problems into two separate issues. Here's the setup:

class User
  after_create :send_first_email
  after_commit :send_second_email, on: :create

  private

  def send_first_email
    UserMailer.delay.first_email(id)
  end

  def send_second_email
    UserMailer.second_email(id).deliver_later
  end
end

When I run a simple create spec, I get Isolator::BackgroundJobError but it's because of send_second_email.

  1. I believe the first problem is the mailer adapter doesn't recognize UserMailer.delay.first_email(id) as a job enqueue. This is confirmed when I changed it to UserMailer.first_email(id).deliver_later and the error is correctly thrown at send_first_email

  2. I'm not sure if I understand the second problem, my expectation is error shouldn't be thrown because send_second_email it's done after the transaction in after_commit. Is this correct?

I'm using ruby 2.6.5 and rails 5.1.7

Sidekiq isolator doesn't work if "sidekiq/testing" is required only AFTER "isolator"

Is your feature request related to a problem? Please describe.

In a project I'm migrating from DelayedJob under ActiveRecord to raw Sidekiq (ie not through ActiveJob). I've discovered that I've missed a few areas where I forgot to consider done after_commit.

I installed this gem hoping it would pick up on them, It took me a while to realize that although it has a Sidekiq module, it only supports the calls for ActiveJob api (deliver_later).

It also (questionably!) seems to support a call that is I think DelayedJob specific (e.g. xxx.delay https://github.com/palkan/isolator/blob/master/spec/isolator/adapters/background_jobs/sidekiq_spec.rb#L24)

Describe the solution you'd like

I would like the gem to raise an error with SidekiqJobClass.perform_async when in a transaction

Additional context

While doing this it might be good to modernize terminology in the rspec test - Worker -> Job (sidekiq/sidekiq#4971)

Untracked transaction for multiple databases in System Tests

Our environment is as such:

  • Rails 7.1
  • RSpec
  • Puma
  • Multiple databases

We're noticing Trying to finalize an untracked transaction warnings sometimes in our system specs and were able to figure out the problem. Let's say you have 2 databases (A and B). When you start a system spec it could start with only 1 connection to A. Puma then spins up in a separate thread and starts serving requests. Keep in mind that Rails system tests makes it so that the connection pool is shared between the test suite and the Puma server. In serving the request, a connection to database B is made. Since ThreadStateProxy is thread-aware, it stores a state per thread. The connection increment is happening in the Puma thread. When the system test finishes and transactions are rolled back, the transaction in database B counter goes from 0 to -1 because according to the test suite thread, the transaction/connection was never made.

I'm still thinking about how to solve this, but I wanted to file this issue in the meantime.

Subtransactions tracking/preventions

Is your feature request related to a problem? Please describe.

https://about.gitlab.com/blog/2021/09/29/why-we-spent-the-last-month-eliminating-postgresql-subtransactions/

Describe the solution you'd like

It would be great if Isolator could track subtransactions and report them.
The feature MUST be optional (for now):

Isolator.configure do |config|
  # Must be set to a non-zero value to be activated (so, default is 0)
  config.max_substransactions_depth = 32
end

Additional context

Gitlab implementation: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67918/diffs

Rails discussion: rails/rails#44518

Isolator raises exceptions when using DatabaseCleaner with multiple DBs

What did you do?

We recently setup DatabaseCleaner to work with multiple DBs and Isolator is raising errors on tests in which we persist models
with after_commit callbacks that enqueue background jobs. I also checked with HTTP requests made in after after_commit callbacks and it also raises exceptions.

Happy to submit a PR for the fix. Thank you!

What did you expect to happen?

It should not raise errors for jobs enqueued or HTTP request performed on after_commit callbacks.

Additional context

To reproduce it:

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'

  gem 'activejob', '~> 7.0.5.1'
  gem 'activerecord', '~> 7.0.5.1'
  gem 'sqlite3'
  gem 'rspec'

  gem 'database_cleaner'
  gem 'isolator', '~> 0.9.0', require: false
end

require 'active_job'
require 'active_record'
require 'rspec/autorun'

require 'uri'
require 'net/http'

PRIMARY_DB   = 'primary.sqlite3'
SECONDARY_DB = 'secondary.sqlite3'

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: PRIMARY_DB)
ActiveRecord::Schema.define do
  create_table :animals do |t|
    t.string :name, null: false
  end
end

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: SECONDARY_DB)
ActiveRecord::Schema.define do
  create_table :pets do |t|
    t.string :nickname, null: false
  end
end

class DummyJob < ActiveJob::Base; end

class Animal < ActiveRecord::Base
  self.establish_connection(adapter: 'sqlite3', database: PRIMARY_DB)

  after_commit { DummyJob.perform_later }
end

class Pet < ActiveRecord::Base
  self.establish_connection(adapter: 'sqlite3', database: SECONDARY_DB)

  after_commit { Net::HTTP.get_response(URI('https://www.google.com/')) }
end

RSpec.configure do |config|
  DatabaseCleaner[:active_record, db: Pet].strategy    = :transaction
  DatabaseCleaner[:active_record, db: Animal].strategy = :transaction

  config.before(:each) { DatabaseCleaner.start }

  config.after(:each) { DatabaseCleaner.clean }

  require 'isolator'

  Isolator.configure { |isolator_config| isolator_config.raise_exceptions = true }
end

RSpec.describe 'Errors' do
  it { Pet.create(nickname: 'Spike') }
  it { Animal.create(name: 'Dog') }
end

It seems to always increment the transactions threshold for the same connection. In lib/isolator/database_cleaner_support.rb we could assign a lambda that leverages the connection_class used by DatabaseCleaner before increasing/decreasing the transactions threshold on the start and clean methods:

Isolator.default_connection_id = -> { connection_class.connection.object_id }

Thoughts?

Environment

Ruby Version: 3.2.1

Framework Version (Rails, whatever): ActiveRecord 7.0.5.1 and ActiveJob 7.0.5.1

Isolator Version: 0.9.0

Stack level too deep

The HTTP adapter requires sniffer which patches net-http with alias_method. This causes problems when other gems (rack-mini-profiler, APMs like datadog) use prepend to patch the same methods, leading to stack level too deep.

One workaround would be if isolator did not require sniffer unless the http isolator was activated.

Using isolator with multiple database connections

In our app, we're using multiple db connections, and some of them establish connection lazily. In one instance, we notice that simply connecting to a new db increments Thread.current[:isolator_transactions]. I'm guessing a new connection matches the START_PATTERN

START_PATTERN = %r{(\ABEGIN|\ASAVEPOINT)}xi
. Do you have a recommended way for dealing with multiple connections?

Inline isolator exceptions

Is your feature request related to a problem? Please describe.

When we change code, .isolator_ignore.yml needs to be updated with new line numbers.

There are valid reasons to have ignored things, for example if a failed call to an external service should roll back the transaction

Describe the solution you'd like

It would be great if we could do what rubocop does, e.g .my_exception # rubocop:disable Some/Rule.

I think directly above the line makes the most sense, e.g. # isolator:disable MyException

Describe alternatives you've considered

I looked into whether the current API for the dependency injected ignore class could support this. I thought maybe I could check the frame where the exception happened and check for the comment above the line number of the frame where the exception was raised? I could potentially do this with no code in the gem, but it would be great if this was a first-class feature.

Additional context

n/a

Ignore http requests in models that save files to AWS using carrier_waive

In project we have models with such lines:

mount_uploader :file, DocUploader
mount_base64_uploader :signature, SignatureUploader

Isolator raises exception on all such models when find Model.create(file: file) or Model.update(file: file)

I tried to add these models and uploaders to .isolator_todo.yml, but still getting exceptions.

May be I can ignore such situation using Isolator.adapters.http.ignore_if, but I don't understand the trigger on which this condition may rely.

Can anyone help with this question?

Incompatible character encodings when logging jobs with non-utf8 arguments

What did you do?

Tried running isolator against the GitHub test suite and saw a number of incompatible character encodings: ASCII-8BIT and UTF-8 errors.

Here's a test to reproduce the problem:

  context "when logging non-utf8 details" do
    specify do
      logger = instance_double("Logger")
      Isolator.configure do |config|
        config.logger = logger
      end

      job_args = ["abc123", "\xff".dup.force_encoding(Encoding::ASCII_8BIT)]
      details = "MyJob (#{job_args.join(", ")})"
      exception = Isolator::BackgroundJobError.new(details: details)

      described_class.new(exception).call
    end
  end

Because of the ASCII_8BIT job argument, the whole exception message ends up as ASCII_8BIT. We're able to combine that just fine with other ascii-only strings, but we're not able to combine that with the UTF_8 used to print the backtrace.

What did you expect to happen?

I'm not certain. Encodings can be difficult...

A fallback to leave out the if there's any sort of encoding problem, perhaps? Or replace it with - or something else ascii that's likely to be more broadly compatible?

Environment

Ruby Version:

3.2

Framework Version (Rails, whatever):

Rails main (7.1)

Isolator Version:

0.10.0

Being able to allow/ignore inside a specific transaction

Sometimes you actually do want to allow actions to happen inside a transaction, for example a plain lock.
The failure case could be that you rather repeat the action than perhaps missing to run the action, so you wrap it inside a lock.

reminder.with_lock do
  Something.deliver_reminder(reminder)
  reminder.update!(sent: true)
end

This will cause a problem with Isolator, with no apparent way to protect from it.

It would be great with any one of these:

reminder.with_lock do
  Isolator.allow do
    Something.deliver_reminder(reminder)
  end
  reminder.update!(sent: true)
end

or if that's not feasible:

Isolator.adapter.something.ignore_if { |args, object, method|
  object.instance_of?(Something) && method == :deliver_reminder
}
# …
reminder.with_lock do
  Something.instance.deliver_reminder(reminder)
  reminder.update!(sent: true)
end

I've read the source code trying to find a good way to do this. I could try to hook into the current "TODO" system, but that is not optimal because:

  1. It implies that it's a problem that should be solved some day ("TODO")
  2. It's based on filenames and/or line numbers, and not context.

The current ignore_if only passes along the arguments, which means it's not possible to see which ActiveJob is being looked at. (SomeJob.perform_later would have args == [])

Writing my own Ignorer class seems excessive, and it lacks examples on how to do it. I find the interface I'm supposed to implement to be a bit hard to see.

I could call disable! and enable! on the adapter, but that is not thread-safe.

My proposed solutions have one glaring problem, which is: What if you only load Isolator in development/test? Then Isolator.allow will be undefined.
I would solve this by actually wrapping it in my own helpers, so that would not be a problem for me, but it would have to be documented.

private

def allow_inside_transaction
  if defined?(Isolator)
    Isolator.allow { yield }
  else
    yield
  end
end

Corrupt transaction counts when connection closes unexpectedly

What did you do?

I doubt this is a bug in practice, since connections don't tend to go away all of a sudden in test (maybe in staging if the gem is used there?). But I figured I'd report it anyway, at least as further discouragement from using this in production environments.

I wrote a test to demonstrate the problem. If the database connection closes before the transaction completes, we can fail to either commit or rollback, leaving the transaction count permanently incremented.

    context "when the connection fails" do
      specify do
        expect(Isolator).to_not be_within_transaction

        begin
          ar_class.transaction do
            ar_class.all.to_a
            expect(Isolator).to be_within_transaction

            # Imitate a problem with the underlying connection, preventing us from committing or rolling back
            allow(ar_class.connection).to receive(:commit_db_transaction).and_raise("Failed to commit")
            allow(ar_class.connection).to receive(:rollback_db_transaction).and_raise("Failed to rollback")
          end
        rescue
        end

        # The counts are off now, so this fails
        expect(Isolator).to_not be_within_transaction
      end
    end

We've got some transaction tracking stuff at GitHub that patches the Rails TransactionManager, and we've got an ensure there that allows us to decrement counters even if we fail to commit or rollback. There's perhaps an argument to be made for adding some transaction-specific instrumentation to Rails itself that we could use instead.

What did you expect to happen?

Ideally this test would pass, but I understand why it doesn't. There's nothing coming through sql.active_record that would indicate a failed rollback.

What actually happened?

It fails

Additional context

Environment

Ruby Version:

Framework Version (Rails, whatever):

Isolator Version:

Error raised when query has invalid encoding

We recently tried to add isolator to our app and it made some tests.

We have tests that confirm that sending invalid UTF-8 chars works (they are sanitized), but now that query is triggering a crash in isolator code.

This is the app code that triggers the failure:

User.find_or_initialize_by(email: "Invalid \xAD char")

We get the error ArgumentError: invalid byte sequence in UTF-8, this is the stack trace after that call is made:

"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/isolator-0.8.0/lib/isolator/orm_adapters/active_support_subscriber.rb:13:in `match?'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/isolator-0.8.0/lib/isolator/orm_adapters/active_support_subscriber.rb:13:in `block in subscribe!'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/fanout.rb:189:in `finish'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/fanout.rb:62:in `block in finish'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/fanout.rb:62:in `each'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/fanout.rb:62:in `finish'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/instrumenter.rb:45:in `finish_with_state'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activesupport-6.0.4.4/lib/active_support/notifications/instrumenter.rb:30:in `instrument'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract_adapter.rb:718:in `log'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:199:in `execute'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/mysql/database_statements.rb:41:in `execute'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/marginalia-1.11.1/lib/marginalia.rb:71:in `execute_with_marginalia'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:210:in `execute_and_free'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/mysql/database_statements.rb:46:in `exec_query'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract/database_statements.rb:489:in `select'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract/database_statements.rb:70:in `select_all'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/abstract/query_cache.rb:107:in `select_all'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/connection_adapters/mysql/database_statements.rb:12:in `select_all'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/querying.rb:46:in `find_by_sql'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:824:in `block in exec_queries'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:842:in `skip_query_cache_if_necessary'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:811:in `exec_queries'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:626:in `load'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:250:in `records'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation/finder_methods.rb:499:in `find_take'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation/finder_methods.rb:98:in `take'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation/finder_methods.rb:81:in `find_by'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/relation.rb:227:in `find_or_initialize_by'",
"/dev/ruby/2.7.4/lib/ruby/gems/2.7.0/gems/activerecord-6.0.4.4/lib/active_record/querying.rb:21:in `find_or_initialize_by'"

The error comes from calling match? with a value that might not be in the right encoding.

module Isolator
  # ActiveSupport notifications listener
  # Used for ActiveRecord and ROM::SQL (when instrumentation is available)
  module ActiveSupportSubscriber
    START_PATTERN = %r{(\ABEGIN|\ASAVEPOINT)}xi
    FINISH_PATTERN = %r{(\ACOMMIT|\AROLLBACK|\ARELEASE|\AEND TRANSACTION)}xi

    def self.subscribe!(event)
      ::ActiveSupport::Notifications.subscribe(event) do |_name, _start, _finish, _id, query|
        connection_id = query[:connection_id] || query[:connection]&.object_id || 0
        Isolator.incr_transactions!(connection_id) if START_PATTERN.match?(query[:sql])
        Isolator.decr_transactions!(connection_id) if FINISH_PATTERN.match?(query[:sql])
      end
    end
  end
end

As ActiveRecord doesn't raise an error, I would expect Isolator to also handle strings that have the wrong encoding (maybe force UTF-8), what do you think?

I'm available to submit a PR for the fix. Thank you for the nice gem 💚

Idea: Add an RSpec matcher

In our tests, we're actively mocking calls to our objects that wrap some external APIs instead of mocking raw http requests (e.g., with webmock). Because of this, isolator can't detect transaction violations out of the box in our app because we don't make any HTTP calls in our specs.

When we noticed that we thought “wouldn't it be awesome to have some method to restrict calling some service objects when we're in a transaction?”

So, @nepalez suggested following pseudocode:

allow(ExternalApi::GetUser).to receive(:call).outside_of_transaction.and_return({ "Name" => "Vasya" })

I started to investigate possibility to change rspec-mocks behavior and following proof-of-concept for existing matcher customization was born:

# spec/support/matchers/outside_of_transaction.rb

module RSpec
  module Mocks
    module Matchers
      class Receive
        def outside_of_transaction
          self
        end
      end

      module NotInTransaction
        def matches?(subject, *args, &block)
          if Isolator.within_transaction?
            # We doesn't raise Isolator::UnsafeOperationError because it may be
            # rescued somewhere in subject, while MockExpectationError may not.
            @error_generator.send(:__raise, <<~MSG.squish)
              #{@error_generator.intro} received #{subject.inspect}
              while in database transaction, but it is unsafe to call it so.
            MSG
          end

          super
        end
      end
    end

    class MessageExpectation
      def outside_of_transaction
        raise_already_invoked_error_if_necessary(__method__)
        extend Matchers::NotInTransaction
        self
      end
    end
  end
end

See rspec/rspec-mocks#1230 on how it is possible to change existing matcher's behavior in rspec-mocks (unfortunately I couldn't find any docs for this).

What do you think? Is it correct approach? Or maybe you have some better ideas?

Failure when saving attachment with action_text/active storage and AWS

What did you do?

Add an attachment to an action_text field, saved.

What did you expect to happen?

No error.

What actually happened?

Isolator::HTTPError - You are trying to make an outgoing network request inside db transaction.

Transaction is Rails's wrapping of an save call.
Callback is https://github.com/rails/rails/blob/d9fda555942bfb6bcb1f75477b1425e2ab3162f5/actiontext/app/models/action_text/rich_text.rb#L17-L19
HTTP request is coming from aws-sdk-core.

Return value is: ["/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/sniffer-0.4.0/lib/sniffer/adapters/net_http_adapter.rb:28:in `request_with_sniffer'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/connection_pool.rb:341:in `request'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/handler.rb:76:in `block in transmit'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/handler.rb:128:in `block in session'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/connection_pool.rb:103:in `session_for'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/handler.rb:123:in `session'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/handler.rb:75:in `transmit'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/net_http/handler.rb:49:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/content_length.rb:17:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/request_callback.rb:85:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/streaming_retry.rb:60:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/s3_signer.rb:124:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/s3_signer.rb:61:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/s3_host_id.rb:17:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/xml/error_handler.rb:10:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/transfer_encoding.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/helpful_socket_errors.rb:12:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/s3_signer.rb:102:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/redirects.rb:20:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/retry_errors.rb:348:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/dualstack.rb:38:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/accelerate.rb:58:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/http_checksum.rb:18:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/endpoint_pattern.rb:31:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb:34:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/expect_100_continue.rb:21:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/bucket_dns.rb:35:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/arn.rb:49:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/rest/handler.rb:10:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/user_agent.rb:13:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/endpoint_discovery.rb:80:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/endpoint.rb:47:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/param_validator.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/raise_response_errors.rb:16:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/sse_cpk.rb:24:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/dualstack.rb:30:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/plugins/accelerate.rb:47:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/jsonvalue_converter.rb:22:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/idempotency_token.rb:19:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/param_converter.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/request_callback.rb:71:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/aws-sdk-core/plugins/response_paging.rb:12:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/plugins/response_target.rb:24:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-core-3.109.1/lib/seahorse/client/request.rb:72:in `send_request'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/client.rb:5339:in `get_object'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/aws-sdk-s3-1.83.1/lib/aws-sdk-s3/object.rb:890:in `get'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/service/s3_service.rb:54:in `block in download_chunk'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:205:in `instrument'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/service.rb:155:in `instrument'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/service/s3_service.rb:53:in `download_chunk'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob/identifiable.rb:27:in `download_identifiable_chunk'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob/identifiable.rb:22:in `identify_content_type'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/app/models/active_storage/blob/identifiable.rb:11:in `identify_without_saving'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_one.rb:12:in `initialize'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:35:in `new'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:35:in `build_subchange_from'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:31:in `block in subchanges'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:31:in `collect'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:31:in `subchanges'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:17:in `blobs'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/changes/create_many.rb:9:in `initialize'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/model.rb:144:in `new'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activestorage-6.1.3.2/lib/active_storage/attached/model.rb:144:in `embeds='", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actiontext-6.1.3.2/app/models/action_text/rich_text.rb:18:in `block in <class:RichText>'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:427:in `instance_exec'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:427:in `block in make_lambda'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:198:in `block (2 levels) in halting'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:604:in `block (2 levels) in default_terminator'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:603:in `catch'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:603:in `block in default_terminator'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:199:in `block in halting'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:512:in `block in invoke_before'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:512:in `each'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:512:in `invoke_before'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:115:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/autosave_association.rb:385:in `around_save_collection_association'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:137:in `run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:824:in `_run_save_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/callbacks.rb:457:in `create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/timestamp.rb:126:in `create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/persistence.rb:474:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/validations.rb:47:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:298:in `block in save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:354:in `block in with_transaction_returning_status'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:318:in `transaction'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:350:in `with_transaction_returning_status'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:298:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/suppressor.rb:44:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/autosave_association.rb:470:in `save_has_one_association'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/autosave_association.rb:213:in `block in add_autosave_association_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:427:in `block in make_lambda'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:235:in `block in halting_and_conditional'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:516:in `block in invoke_after'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:516:in `each'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:516:in `invoke_after'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:107:in `run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:824:in `_run_create_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/callbacks.rb:461:in `_create_record'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/timestamp.rb:108:in `_create_record'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/persistence.rb:900:in `create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/callbacks.rb:457:in `block in create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:117:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/autosave_association.rb:385:in `around_save_collection_association'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:137:in `run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:824:in `_run_save_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/callbacks.rb:457:in `create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/timestamp.rb:126:in `create_or_update'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/persistence.rb:474:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/validations.rb:47:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:298:in `block in save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:354:in `block in with_transaction_returning_status'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `block in transaction'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:310:in `block in within_new_transaction'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:26:in `block (2 levels) in synchronize'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `handle_interrupt'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:25:in `block in synchronize'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `handle_interrupt'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/concurrency/load_interlock_aware_monitor.rb:21:in `synchronize'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/connection_adapters/abstract/transaction.rb:308:in `within_new_transaction'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/connection_adapters/abstract/database_statements.rb:320:in `transaction'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:350:in `with_transaction_returning_status'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/transactions.rb:298:in `save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/suppressor.rb:44:in `save'", "/www/Simplero/app/controllers/site/comments_controller.rb:21:in `create'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/abstract_controller/base.rb:228:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/rendering.rb:30:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/abstract_controller/callbacks.rb:42:in `block in process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:117:in `block in run_callbacks'", "/www/Simplero/app/controllers/application_controller.rb:60:in `catch_unknown_format'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/controllers/concerns/request_info_concerns.rb:51:in `block in set_request_info'", "/www/Simplero/lib/request_info.rb:73:in `with'", "/www/Simplero/app/controllers/concerns/request_info_concerns.rb:35:in `set_request_info'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/controllers/concerns/logging_concerns.rb:23:in `set_time_zone'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/controllers/application_controller.rb:209:in `find_current_account'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/controllers/concerns/logging_concerns.rb:44:in `postgres_logger'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/controllers/application_controller.rb:100:in `profile'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/www/Simplero/app/concerns/local_draft_clearer.rb:15:in `block in clear_local_storage_on_successful_save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/wisper-1.3.0/lib/wisper/temporary_listeners.rb:15:in `with'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/wisper-1.3.0/lib/wisper/temporary_listeners.rb:6:in `with'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/wisper-1.3.0/lib/wisper.rb:17:in `with_listeners'", "/www/Simplero/app/concerns/local_draft_clearer.rb:14:in `clear_local_storage_on_successful_save'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actiontext-6.1.3.2/lib/action_text/rendering.rb:20:in `with_renderer'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actiontext-6.1.3.2/lib/action_text/engine.rb:55:in `block (4 levels) in <class:Engine>'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `instance_exec'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:126:in `block in run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:137:in `run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/abstract_controller/callbacks.rb:41:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/rescue.rb:22:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `block in instrument'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/notifications/instrumenter.rb:24:in `instrument'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/notifications.rb:203:in `instrument'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/instrumentation.rb:33:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal/params_wrapper.rb:249:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/railties/controller_runtime.rb:27:in `process_action'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/abstract_controller/base.rb:165:in `process'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionview-6.1.3.2/lib/action_view/rendering.rb:39:in `process'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal.rb:190:in `dispatch'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_controller/metal.rb:254:in `dispatch'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/routing/route_set.rb:50:in `dispatch'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/routing/route_set.rb:33:in `serve'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/routing/mapper.rb:19:in `block in <class:Constraints>'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/routing/mapper.rb:49:in `serve'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/journey/router.rb:50:in `block in serve'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/journey/router.rb:32:in `each'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/journey/router.rb:32:in `serve'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/routing/route_set.rb:842:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/http_accept_language-2.0.0/lib/http_accept_language/middleware.rb:13:in `call'", "/www/Simplero/lib/middleware/development_middleware.rb:15:in `call'", "/www/Simplero/lib/middleware/routes_reloader.rb:14:in `call'", "/www/Simplero/lib/middleware/http_method_not_allowed.rb:9:in `call'", "/www/Simplero/lib/middleware/redirect_to_simplero_middleware.rb:16:in `call'", "/www/Simplero/lib/middleware/no_www_middleware.rb:17:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/etag.rb:27:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/head.rb:12:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/http/permissions_policy.rb:22:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/http/content_security_policy.rb:18:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/cookies.rb:689:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.1.3.2/lib/active_record/migration.rb:601:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/callbacks.rb:98:in `run_callbacks'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/callbacks.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/executor.rb:14:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/actionable_exceptions.rb:18:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/better_errors-2.4.0/lib/better_errors/middleware.rb:84:in `protected_app_call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/better_errors-2.4.0/lib/better_errors/middleware.rb:79:in `better_errors_call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/better_errors-2.4.0/lib/better_errors/middleware.rb:57:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/debug_exceptions.rb:29:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:37:in `call_app'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:26:in `block in call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:99:in `block in tagged'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:37:in `tagged'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/tagged_logging.rb:99:in `tagged'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.1.3.2/lib/rails/rack/logger.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/sprockets-rails-3.2.2/lib/sprockets/rails/quiet_assets.rb:13:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/request_store-1.5.0/lib/request_store/middleware.rb:19:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/request_id.rb:26:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/method_override.rb:24:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rails_same_site_cookie-0.1.8/lib/rails_same_site_cookie/middleware.rb:13:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/runtime.rb:22:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.1.3.2/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/executor.rb:14:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/static.rb:24:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-2.2.3/lib/rack/sendfile.rb:110:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/actionpack-6.1.3.2/lib/action_dispatch/middleware/host_authorization.rb:92:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-cors-1.1.0/lib/rack/cors.rb:100:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-attack-4.2.0/lib/rack/attack.rb:104:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-utf8_sanitizer-1.3.2/lib/rack/utf8_sanitizer.rb:19:in `call'", "/www/Simplero/lib/middleware/sendgrid_charset_middleware.rb:38:in `call'", "/www/Simplero/lib/middleware/raw_post_middleware.rb:12:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/webpacker-5.2.1/lib/webpacker/dev_server_proxy.rb:25:in `perform_request'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/rack-proxy-0.6.5/lib/rack/proxy.rb:57:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.1.3.2/lib/rails/engine.rb:539:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/configuration.rb:249:in `call'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/request.rb:77:in `block in handle_request'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/thread_pool.rb:338:in `with_force_shutdown'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/request.rb:76:in `handle_request'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/server.rb:438:in `process_client'", "/home/jason/.rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/puma-5.3.2/lib/puma/thread_pool.rb:145:in `block in spawn_thread'"]

Additional context

Is there a way to allow these kinds of requests other than coming up with some ignore logic per #24 (comment) ?

Environment

Ruby 2.6.6
Rails 6.1.3.2
aws-sdk-s3 1.83.1
isolator 0.7.0

Rails 7.1 compatibility

What did you do?

Tried to upgrade an app from Rails 7.0.8 to Rails 7.1 with the following initializer:

# frozen_string_literal: true

if defined?(Isolator)
  # Check can be disabled since we have transactional_push enabled in Sidekiq.
  Isolator.adapters.active_job.disable!
end

What actually happened?

The following exception is raised on boot:

/Users/sunny/.rbenv/versions/3.1.3/lib/ruby/gems/3.1.0/gems/isolator-1.0.1/lib/isolator/simple_hashie.rb:12:in `block in method_missing': undefined method `active_job' for {"http"=>#<Module:0x000000010cdeb1d0>, "sidekiq"=>#<Module:0x000000010cde1270>, "mailer"=>#<Module:0x000000010cdd9c28>}:Isolator::SimpleHashie (NoMethodError)
	from /Users/sunny/.rbenv/versions/3.1.3/lib/ruby/gems/3.1.0/gems/isolator-1.0.1/lib/isolator/simple_hashie.rb:12:in `fetch'
	from /Users/sunny/.rbenv/versions/3.1.3/lib/ruby/gems/3.1.0/gems/isolator-1.0.1/lib/isolator/simple_hashie.rb:12:in `method_missing'
	from /Users/sunny/code/cults/config/initializers/isolator.rb:5:in `<main>'

Additional content

Commenting out the disabling boots the app with no issue.

Environment

Ruby Version: 3.1.3

Framework Version: Rails 7.1.2 (previously 7.0.8)

Isolator Version: 1.0.1

Active Storage - Analyze Job - Always fails because of after_create_commit callback

When used together with Rails 6 and ActiveStorage, this fails always, as ActiveStorage automatically always tries to schedule the AnalyzeJob after the record is created:

Isolator::BackgroundJobError:
        You are trying to enqueue background job inside db transaction. In case of transaction failure, this may lead to data inconsistency and unexpected bugs
        Details: ActiveStorage::AnalyzeJob (#<ActiveStorage::Blob:0x00005588a27565e0>)
      # /builds/smartq/qflow/vendor/bundle/ruby/2.6.0/gems/activejob-6.0.0/lib/active_job/enqueuing.rb:22:in `perform_later'
      # /builds/smartq/qflow/vendor/bundle/ruby/2.6.0/gems/activestorage-6.0.0/app/models/active_storage/blob/analyzable.rb:37:in `analyze_later'
      # /builds/smartq/qflow/vendor/bundle/ruby/2.6.0/gems/activestorage-6.0.0/app/models/active_storage/attachment.rb:37:in `analyze_blob_later'
      # /builds/smartq/qflow/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.0/lib/active_support/callbacks.rb:429:in `block in make_lambda'
      # /builds/smartq/qflow/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.0/lib/active_support/callbacks.rb:264:in `block in conditional'

Which is called from Blob: after_create_commit :analyze_blob_later, :identify_blob

Add .isolator_ignore support

The .isolator_todo.yml configuration file which is used to temporary ignore some parts of source code is a great feature, but its name might be misleading in case of code which intentionally contains some IO calls inside a transaction. IMO it'd be great if there was support for .isolator_ignore.yml or .isolatorignore.yml file with the same syntax as .isolator_todo.yml but different purpose. Current approach works perfectly with small development teams but if there are a lot of people working on making codebase sync with Isolator it might be a problem.

That's currently possible to implement a custom Ignorer class which respects both todo and ignore files but I think it'd be good to make it a built-in feature.

So, I'd like to suggest to add the configuration file which points to code that will not be fixed any time soon and keep .isolator_todo.yml as a TODO-list. Thanks for a great gem BTW.

NoMethodError when building an exception for Faraday request

NoMethodError: undefined method` []' for #<Sniffer::DataItem::Request:0x00007fe477c8c040>

/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/isolator-0.6.0/lib/isolator/adapters/http/sniffer.rb:13:in `block in <top (required)>'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/isolator-0.6.0/lib/isolator/adapters/base.rb:50:in `build_exception'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/isolator-0.6.0/lib/isolator/adapters/base.rb:27:in `notify'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/isolator-0.6.0/lib/isolator/adapter_builder.rb:25:in `block (2 levels) in add_patch_method'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sniffer-0.3.2/lib/sniffer/adapters/net_http_adapter.rb:28:in `request_with_sniffer'
/.rbenv/versions/2.5.1/lib/ruby/2.5.0/net/http.rb:1457:in `block in request'
/.rbenv/versions/2.5.1/lib/ruby/2.5.0/net/http.rb:910:in `start'
/.rbenv/versions/2.5.1/lib/ruby/2.5.0/net/http.rb:1455:in `request'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sniffer-0.3.2/lib/sniffer/adapters/net_http_adapter.rb:32:in `block in request_with_sniffer'
/.rbenv/versions/2.5.1/lib/ruby/2.5.0/benchmark.rb:308:in `realtime'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/sniffer-0.3.2/lib/sniffer/adapters/net_http_adapter.rb:31:in `request_with_sniffer'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/adapter/net_http.rb:87:in `perform_request'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/adapter/net_http.rb:43:in `block in call'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/adapter/net_http.rb:92:in `with_net_http_connection'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/adapter/net_http.rb:38:in `call'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/rack_builder.rb:143:in `build_response'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/connection.rb:387:in `run_request'
/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/faraday-0.15.2/lib/faraday/connection.rb:175:in `put'
/app/services/fake_ssn_uploader_service.rb:15:in `call'```

Failing code:

class FakeSsnUploaderService
  attr_reader :url, :value

  def initialize(url, value)
    @url = url
    @value = value
  end

  def call
    connection.put(url, value) do |f|
      f.headers["Content-Type"] = "text/plain"
    end
  end

  private

  def connection
    Faraday.new do |faraday|
      # faraday.use Faraday::Response::RaiseError
      faraday.adapter Faraday.default_adapter
    end
  end
end

ActiveRecord::Base.transaction do
  FakeSsnUploaderService.new(url, data).call
end

Errors when combined with `ActiveStorage::FixtureSet.blob`

What did you do?

We have a Rails 7 app which makes use of fixtures. Specifically, we're using FixtureSet to saturate ActiveStorage files.

When I add isolator to the app, my fixtures start throwing errors.

What did you expect to happen?

Fixtures to work as normally.

What actually happened?

Fixtures fail with the following backtrace:

 /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/testing/file_fixtures.rb:27:in `join'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/testing/file_fixtures.rb:27:in `file_fixture'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activestorage-7.0.7.2/lib/active_storage/fixture_set.rb:65:in `prepare'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activestorage-7.0.7.2/lib/active_storage/fixture_set.rb:61:in `blob'
    /Users/evan/Projects/get-weatherized/quote-app/test/fixtures/active_storage/blobs.yml:2:in `get_binding'
    /Users/evan/.rvm/rubies/ruby-3.2.2/lib/ruby/3.2.0/erb.rb:429:in `eval'
    /Users/evan/.rvm/rubies/ruby-3.2.2/lib/ruby/3.2.0/erb.rb:429:in `result'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/configuration_file.rb:48:in `render'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/configuration_file.rb:22:in `parse'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/configuration_file.rb:18:in `parse'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixture_set/file.rb:53:in `raw_rows'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixture_set/file.rb:42:in `config_row'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixture_set/file.rb:28:in `model_class'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:725:in `block (2 levels) in read_fixture_files'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixture_set/file.rb:16:in `open'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:724:in `block in read_fixture_files'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:723:in `each'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:723:in `each_with_object'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:723:in `read_fixture_files'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:657:in `initialize'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:598:in `new'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:598:in `block in read_and_insert'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:596:in `map'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:596:in `read_and_insert'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/fixtures.rb:567:in `create_fixtures'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/test_fixtures.rb:275:in `load_fixtures'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/test_fixtures.rb:125:in `setup_fixtures'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/isolator-0.10.0/lib/isolator/railtie.rb:38:in `setup_fixtures'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activerecord-7.0.7.2/lib/active_record/test_fixtures.rb:10:in `before_setup'
    /Users/evan/.rvm/gems/ruby-3.2.2@quote_app_dev/gems/activesupport-7.0.7.2/lib/active_support/testing/setup_and_teardown.rb:40:in `before_setup'

Additional context

Environment

Ruby Version:
3.2.2

Framework Version (Rails, whatever):
Rails 7.0.7

Isolator Version:
0.10.0

[Help needed] `after_create_commit` raises `Isolator::BackgroundJobError`?

I've been trying to test isolator on my application, however, I keep receiving

     Isolator::BackgroundJobError:
       You are trying to enqueue background job inside db transaction. In case of transaction failure, this may lead to data inconsistency and unexpected bugs
       Details: MyWorker ([387])

This is raised from my model (ApplicationRecord) in a line like

after_commit :call_my_worker, on: :create

I'm opening this issue just to ask if there isn't any basic configuration I'm missing and should be aware of.

Any reason to not run in production?

Isolator is supposed to be used in tests and on staging.

Clearly I don't want to raise exceptions in production, but if I used it in production, configured to send to my error reporting platform, would there be any issues?

  • Does isolator change behaviour somehow?
  • Or affect performance?
  • Or is there a risk that my error reporting itself will get flagged by isolator, leading to infinite recursion?

Ruby 2.7 deprecation warning

If you run this test using ruby 2.7.7, it emits warning Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call. This was introduced in version 0.9.0 (presumably #51). The crash is in the call to enqueue here; that method is redefined here.

# frozen_string_literal: true

module CaptureRuby27DeprecationWarnings
  RUBY_2_7_DEPRECATIONS = [
    "Using the last argument as keyword parameters is deprecated",
    "Passing the keyword argument as the last hash parameter is deprecated",
    "Splitting the last argument into positional and keyword parameters is deprecated"
  ].freeze

  def self.extended(_module)
    Warning[:deprecated] = true
  end

  def warn(message)
    if ruby_2_7_deprecation_warning?(message)
      raise(SyntaxError, message)
    else
      super
    end
  end

  private

  def ruby_2_7_deprecation_warning?(message)
    RUBY_2_7_DEPRECATIONS.any? { |warning| message.include?(warning) }
  end
end

Warning.extend(CaptureRuby27DeprecationWarnings)


require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activejob", "~> 7.0.0"
  gem "activesupport", "~> 7.0.0"

  # gem "isolator", "0.11.0", require: false # fail
  gem "isolator", "0.9.0", require: false # fail
  # gem "isolator", "0.8.0", require: false # pass
end

require "minitest/autorun"
require "active_job"
require "active_support"
require "isolator"

class BuggyJob < ActiveJob::Base
  def perform(message)
    puts message
  end
end

class BuggyJobTest < ActiveJob::TestCase
  def test_stuff
    assert_equal "2.7.7", RUBY_VERSION

    BuggyJob.set(priority: 1).perform_later("hi")
  end
end

KeyError: key not found: isolator_threshold

Hello :)

We've added Isolator to our app and are getting the following error on a regular basis:

KeyError: key not found: isolator_threshold
  from isolator/ext/thread_fetch.rb:7:in `raise'
  from isolator/ext/thread_fetch.rb:7:in `fetch'
  from isolator.rb:67:in `transactions_threshold'
  from isolator.rb:91:in `within_transaction?'
  from isolator/adapters/base.rb:31:in `notify?'
  from isolator/adapters/base.rb:26:in `notify'
  from isolator/adapter_builder.rb:24:in `block (2 levels) in add_patch_method'
  from sidekiq/client.rb:74:in `push'
  from sidekiq/client.rb:131:in `push'
  from sidekiq/scheduled.rb:26:in `block (2 levels) in enqueue_jobs'
  from sidekiq/scheduled.rb:15:in `each'
  from sidekiq/scheduled.rb:15:in `block in enqueue_jobs'
  from sidekiq.rb:96:in `block in redis'
  from connection_pool.rb:64:in `block (2 levels) in with'
  from connection_pool.rb:63:in `handle_interrupt'
  from connection_pool.rb:63:in `block in with'
  from connection_pool.rb:60:in `handle_interrupt'
  from connection_pool.rb:60:in `with'
  from sidekiq.rb:93:in `redis'
  from sidekiq/scheduled.rb:14:in `enqueue_jobs'
  from sidekiq/scheduled.rb:77:in `enqueue'
  from sidekiq/scheduled.rb:68:in `block in start'
  from sidekiq/util.rb:16:in `watchdog'
  from sidekiq/util.rb:25:in `block in safe_thread'

We use:

  • Ruby 2.5.0
  • Sidekiq 5.1.1
  • Isolator 0.2.1

Please advise.

Corrupt transaction counts with Rails 7.1 restarting savepoint transactions

This is similar to #64, but I'm filing it separately since this one really is a bug.

What did you do?

I tried to run isolator at GitHub and ran into some Trying to finalize an untracked transaction warnings. Here's a failing test (against Rails main) to demonstrate the issue:

    context "when rolling back a restarting savepoint transaction" do
      specify do
        expect(Isolator).to_not be_within_transaction

        begin
          ActiveRecord::Base.logger = Logger.new(STDOUT)
          # RealTransaction (begin..rollback)
          ar_class.transaction do
            ar_class.first
            # Savepoint Transaction (savepoint..rollback)
            ar_class.transaction(requires_new: true) do
              # ResetParentTransaction (rollback to outer savepoint)
              ar_class.transaction(requires_new: true) do
                ar_class.first
                expect(Isolator).to be_within_transaction
                raise "Rollback"
              end
            end
          ensure
            expect(Isolator).to be_within_transaction # Oops, transaction count is already 0 so this test fails
          end
        rescue
        end

        expect(Isolator).to_not be_within_transaction
      end
    end

This happens because of rails/rails#44526, which can cause the same savepoint to get rolled back more than once. In older versions of Rails the transaction queries were:

BEGIN
SAVEPOINT active_record_1
SAVEPOINT active_record_2
ROLLBACK TO SAVEPOINT active_record_2
ROLLBACK TO SAVEPOINT active_record_1
ROLLBACK

But in Rails 7.1 it becomes:

BEGIN
SAVEPOINT active_record_1
ROLLBACK TO SAVEPOINT active_record_1
ROLLBACK TO SAVEPOINT active_record_1
ROLLBACK

That's 2 starts and 3 finishes, so we end up decrementing the count too far.

Environment

Ruby Version:

3.2

Framework Version (Rails, whatever):

Rails 7.1 (unreleased)

Isolator Version:

0.10.0

Crashes when used with `disable_ddl_transaction!` & `commit_db_transaction`

What did you do?

# Gemfile

group :development, :test do
  gem "isolator"
end
# db/migrate/20210824201715_test_migration.rb

class TestMigration < ActiveRecord::Migration[7.0]
  disable_ddl_transaction!

  def change
    commit_db_transaction
  end
end

When you run rake db:migrate, it crashes:

StandardError: An error has occurred, all later migrations canceled:

undefined method `[]' for nil:NilClass
/gems/isolator-0.7.0/lib/isolator.rb:138:in `decr_transactions!'
/gems/isolator-0.7.0/lib/isolator/orm_adapters/active_support_subscriber.rb:14:in `block in subscribe!'
/activesupport/lib/active_support/notifications/fanout.rb:202:in `finish'
/activesupport/lib/active_support/notifications/fanout.rb:66:in `block in finish'
/activesupport/lib/active_support/notifications/fanout.rb:66:in `each'
/activesupport/lib/active_support/notifications/fanout.rb:66:in `finish'
/activesupport/lib/active_support/notifications/instrumenter.rb:73:in `finish_with_state'
/activesupport/lib/active_support/notifications/instrumenter.rb:50:in `instrument'
/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:715:in `log'
/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:43:in `execute'
/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb:117:in `commit_db_transaction'
/activerecord/lib/active_record/migration.rb:931:in `block in method_missing'
/activerecord/lib/active_record/migration.rb:899:in `block in say_with_time'
/activerecord/lib/active_record/migration.rb:899:in `say_with_time'
/activerecord/lib/active_record/migration.rb:920:in `method_missing'
/db/migrate/20210824201715_test_migration.rb:5:in `change'

What did you expect to happen?

The migration to complete safely.

What actually happened?

It crashed.

I tried running ISOLATOR_DEBUG=true rake db:migrate but didn't see any additional logs.

Environment

Ruby Version: ruby 2.7.3p183 (2021-04-05 revision 6847ee089d) [x86_64-darwin19]

Framework Version (Rails, whatever): Rails 7.0.0.alpha (but have also seen this on 6.1)

Isolator Version: isolator (0.7.0)

Should we ignore by default Turbo::Streams::ActionBroadcastJob?

What did you do?

I am seeing error linked to turbo-rails

    Isolator::BackgroundJobError:
       You are trying to enqueue background job inside db transaction. In case of transaction failure, this may lead to data inconsistency and unexpected bugs
       Details: Turbo::Streams::ActionBroadcastJob (Z2lkOi8vZHJhZGlzL0F3eEpvYi8yNjc, {:action=>:replace, :target=>"awx_job_awx_job_267"

What did you expect to happen?

I am wondering if we should mark them as safe. Like in the README.

Isolator does not distinguish framework-level adapters. For example, :active_job spy doesn't take into account which AJ adapter you use; if you are using a safe one (e.g. Que) just disable the :active_job adapter to avoid false negatives (i.e. Isolator.adapters.active_job.disable!).

Sidekiq isolator doesn't work if "sidekiq/testing" is required only AFTER "isolator"

What did you do?

Added isolator and required it at the end of my initializers

What did you expect to happen?

It would catch sidekiq calls duringt transactions in my existing specs

What actually happened?

Very few were caught (some were - perhaps to do with test ordering...)

My rspec support files were requiring "sidekiq/testing" after rails had been initialized.

When I required sidekiq/testing at the beginning of my initializer it then worked.

# config/initializers/z_isolator.rb
require "sidekiq/testing" if Rails.env.test?

unless Rails.env.production? # so we get it in staging too
  require "isolator"
  Isolator.configure do |config|
    config.send_notifications = true
  end
end

Additional context

Not 100% sure what you should do about this - could just put this in the README. It's a bit ugly to have a if Rails.env.testing? in an initializer but not the end of the world.

There may be additional context to how I'm using sidekiq testing mode (switching between fake and inline in different specs for example) but I think it's mainly when sidekiq/testing is required (and thus when it does a prepend on Sidekiq::Client)

Environment

Ruby Version:
2.7.6
Framework Version (Rails, whatever):
rails 6.0.2
Isolator Version:
0.8.0

tag v0.10.0

Releasing new version without proper git tag is bad habit.

How to handle rescue of StandardError?

In the process of implementing this in our codebase, and I realized that it is very hard to track down some of these, due to broad rescue of StandardError in various places in our codebase.

Is there a suggested way to handle this?

Would it make sense to refactor Isolator's Error classes to inherit from Exception rather than StandardError?

Since this is a tool meant to identify poor-practice in pre-prod environments, I think the latter question may make sense to do, but may be missing some larger implications at the cursory level of thought I've given it.

Isolator behaves differently between rails 6.1.3.2 and 6.1.4

We recently upgraded from rails 6.1.3.2 to 6.1.4 and found that when there's a ActiveRecord::Deadlocked exception, isolator does not clean up the stale connection properly. This may be due to rails changing how it handles deadlock. It seems rails 6.1.4 disconnects the db connection and establishes a new one.

In rails 6.1.3.2: when ActiveRecord::Deadlocked is raised, ActiveRecord::Base.connected? does not change and the same connection id is seen in Isolator.state[:transactions].

In rails 6.1.4 when ActiveRecord::Deadlocked is raised, ActiveRecord::Base.connected? changes from true to false. Inspecting Isolator.state[:transactions] subsequently will show both the dead and new connection ids. If ActiveRecord::Deadlocked is raised within a transaction, then the dead connection's transactions count remains higher and will fail within_transaction? check.

Do you think isolator should listen to disconnect event and remove the dead connection from Isolator.state[:transactions]?

Mailer is not present in Isolator.adapters after initialize

What did you do?

Installed gem
add .isolator_ignore.yml where add
mailer: - app/concepts/api/v1/lib/operation/bookings/create_transaction.rb

What did you expect to happen?

my tests are passed

What actually happened?

got
Isolator::MailerError ... ...

Additional context

Failures:

1) Api::V1::Workspaces::Bookings::Operation::Update Success updates booking
   Failure/Error: notify(*args, **kwargs, sync: true)
   
   Isolator::MailerError:
     You are trying to send email inside db transaction.
     Details: From: ["[email protected]"]
     To: ["[email protected]"]
     Subject: New Payment from Rosalee Feest is due Feb 22, 2023

Environment

Ruby Version: 3.1.2

Framework Version (Rails, whatever): rails 7.0.4

Isolator Version: 0.8.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.