Skip to content

Commit 206345b

Browse files
SamSaffronkaspth
authored andcommitted
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.
1 parent fb6d5e0 commit 206345b

File tree

1 file changed

+12
-5
lines changed

1 file changed

+12
-5
lines changed

activerecord/lib/active_record/connection_adapters/abstract_adapter.rb

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22

3+
require "set"
34
require "active_record/connection_adapters/determine_if_preparable_visitor"
45
require "active_record/connection_adapters/schema_cache"
56
require "active_record/connection_adapters/sql_type_metadata"
@@ -10,7 +11,6 @@
1011
require "arel/collectors/composite"
1112
require "arel/collectors/sql_string"
1213
require "arel/collectors/substitute_binds"
13-
require "concurrent/atomic/thread_local_var"
1414

1515
module ActiveRecord
1616
module ConnectionAdapters # :nodoc:
@@ -92,10 +92,10 @@ def initialize(connection, logger = nil, config = {}) # :nodoc:
9292
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
9393

9494
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
95-
@prepared_statement_status = Concurrent::ThreadLocalVar.new(true)
95+
@prepared_statements = true
9696
@visitor.extend(DetermineIfPreparableVisitor)
9797
else
98-
@prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
98+
@prepared_statements = false
9999
end
100100

101101
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
@@ -139,7 +139,11 @@ def schema_migration # :nodoc:
139139
end
140140

141141
def prepared_statements
142-
@prepared_statement_status.value
142+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
143+
end
144+
145+
def prepared_statements_disabled_cache # :nodoc:
146+
Thread.current[:ar_prepared_statements_disabled_cache] ||= Set.new
143147
end
144148

145149
class Version
@@ -226,7 +230,10 @@ def seconds_idle # :nodoc:
226230
end
227231

228232
def unprepared_statement
229-
@prepared_statement_status.bind(false) { yield }
233+
cache = prepared_statements_disabled_cache.add(object_id) if @prepared_statements
234+
yield
235+
ensure
236+
cache&.delete(object_id)
230237
end
231238

232239
# Returns the human-readable name of the adapter. Use mixed case - one

0 commit comments

Comments
 (0)