palkan / isolator Goto Github PK
View Code? Open in Web Editor NEWDetect non-atomic interactions within DB transactions
License: MIT License
Detect non-atomic interactions within DB transactions
License: MIT License
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
.
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
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
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)
I would like the gem to raise an error with SidekiqJobClass.perform_async
when in a transaction
While doing this it might be good to modernize terminology in the rspec test - Worker
-> Job
(sidekiq/sidekiq#4971)
Our environment is as such:
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.
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
Gitlab implementation: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67918/diffs
Rails discussion: rails/rails#44518
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!
It should not raise errors for jobs enqueued or HTTP request performed on after_commit
callbacks.
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?
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
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.
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
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
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
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.
n/a
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?
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.
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?
Ruby Version:
3.2
Framework Version (Rails, whatever):
Rails main (7.1)
Isolator Version:
0.10.0
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:
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
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.
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.
It fails
Ruby Version:
Framework Version (Rails, whatever):
Isolator Version:
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 💚
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?
Add an attachment to an action_text field, saved.
No error.
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'"]
Is there a way to allow these kinds of requests other than coming up with some ignore logic per #24 (comment) ?
Ruby 2.6.6
Rails 6.1.3.2
aws-sdk-s3 1.83.1
isolator 0.7.0
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
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>'
Commenting out the disabling boots the app with no issue.
Ruby Version: 3.1.3
Framework Version: Rails 7.1.2 (previously 7.0.8)
Isolator Version: 1.0.1
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
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: 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
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.
Fixtures to work as normally.
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'
Ruby Version:
3.2.2
Framework Version (Rails, whatever):
Rails 7.0.7
Isolator Version:
0.10.0
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.
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?
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
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:
Please advise.
This is similar to #64, but I'm filing it separately since this one really is a bug.
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.
Ruby Version:
3.2
Framework Version (Rails, whatever):
Rails 7.1 (unreleased)
Isolator Version:
0.10.0
# 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'
The migration to complete safely.
It crashed.
I tried running ISOLATOR_DEBUG=true rake db:migrate
but didn't see any additional logs.
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)
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"
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!).
Added isolator and required it at the end of my initializers
It would catch sidekiq calls duringt transactions in my existing specs
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
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)
Ruby Version:
2.7.6
Framework Version (Rails, whatever):
rails 6.0.2
Isolator Version:
0.8.0
Releasing new version without proper git tag is bad habit.
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.
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]
?
Installed gem
add .isolator_ignore.yml where add
mailer: - app/concepts/api/v1/lib/operation/bookings/create_transaction.rb
my tests are passed
got
Isolator::MailerError ... ...
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
Ruby Version: 3.1.2
Framework Version (Rails, whatever): rails 7.0.4
Isolator Version: 0.8.0
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.