mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Avoid expensive tracking objects for prepared statements
Per #36949 we introduce a race condition fix for #36763 This refines the fix to avoid using Concurrent::ThreadLocalVar The implementation in the concurrent lib is rather expensive, culminating in a finalizer per object that spins off a thread to do cleanup work. None of this expense is needed as we can simply implement the desired behavior using Ruby primitives. Additionally this moves to a Fiber bound implementation vs a thread bound implementation, something that is not desired for this particular usage.
This commit is contained in:
parent
fb6d5e0473
commit
206345b989
1 changed files with 12 additions and 5 deletions
|
@ -1,5 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "set"
|
||||||
require "active_record/connection_adapters/determine_if_preparable_visitor"
|
require "active_record/connection_adapters/determine_if_preparable_visitor"
|
||||||
require "active_record/connection_adapters/schema_cache"
|
require "active_record/connection_adapters/schema_cache"
|
||||||
require "active_record/connection_adapters/sql_type_metadata"
|
require "active_record/connection_adapters/sql_type_metadata"
|
||||||
|
@ -10,7 +11,6 @@ require "arel/collectors/bind"
|
||||||
require "arel/collectors/composite"
|
require "arel/collectors/composite"
|
||||||
require "arel/collectors/sql_string"
|
require "arel/collectors/sql_string"
|
||||||
require "arel/collectors/substitute_binds"
|
require "arel/collectors/substitute_binds"
|
||||||
require "concurrent/atomic/thread_local_var"
|
|
||||||
|
|
||||||
module ActiveRecord
|
module ActiveRecord
|
||||||
module ConnectionAdapters # :nodoc:
|
module ConnectionAdapters # :nodoc:
|
||||||
|
@ -92,10 +92,10 @@ module ActiveRecord
|
||||||
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
||||||
|
|
||||||
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
||||||
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
|
@prepared_statements = true
|
||||||
@visitor.extend(DetermineIfPreparableVisitor)
|
@visitor.extend(DetermineIfPreparableVisitor)
|
||||||
else
|
else
|
||||||
@prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
|
@prepared_statements = false
|
||||||
end
|
end
|
||||||
|
|
||||||
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
||||||
|
@ -139,7 +139,11 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepared_statements
|
def prepared_statements
|
||||||
@prepared_statement_status.value
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepared_statements_disabled_cache # :nodoc:
|
||||||
|
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
|
||||||
end
|
end
|
||||||
|
|
||||||
class Version
|
class Version
|
||||||
|
@ -226,7 +230,10 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def unprepared_statement
|
def unprepared_statement
|
||||||
@prepared_statement_status.bind(false) { yield }
|
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
cache&.delete(object_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the human-readable name of the adapter. Use mixed case - one
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
||||||
|
|
Loading…
Reference in a new issue