Skip to content

Commit 36b030d

Browse files
authored
Merge pull request rails#52632 from byroot/3.2-connection-lease-registry
Refactor query cache and connection lease registry for performance
2 parents ea585d6 + 99808b1 commit 36b030d

File tree

2 files changed

+41
-41
lines changed

2 files changed

+41
-41
lines changed

activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -118,42 +118,24 @@ def dirties_query_cache
118118
# * private methods that require being called in a +synchronize+ blocks
119119
# are now explicitly documented
120120
class ConnectionPool
121-
if ObjectSpace.const_defined?(:WeakKeyMap) # RUBY_VERSION >= 3.3
122-
WeakKeyMap = ::ObjectSpace::WeakKeyMap # :nodoc:
123-
else
124-
class WeakKeyMap # :nodoc:
125-
def initialize
126-
@map = ObjectSpace::WeakMap.new
127-
@values = nil
128-
@size = 0
129-
end
130-
131-
alias_method :clear, :initialize
132-
133-
def [](key)
134-
prune if @map.size != @size
135-
@map[key]
136-
end
121+
class WeakThreadKeyMap # :nodoc:
122+
# FIXME: On 3.3 we could use ObjectSpace::WeakKeyMap
123+
# but it currently cause GC crashes: https://github.com/byroot/rails/pull/3
124+
def initialize
125+
@map = {}
126+
end
137127

138-
def []=(key, value)
139-
@map[key] = value
140-
prune if @map.size != @size
141-
value
142-
end
128+
def clear
129+
@map.clear
130+
end
143131

144-
def delete(key)
145-
if value = self[key]
146-
self[key] = nil
147-
prune
148-
end
149-
value
150-
end
132+
def [](key)
133+
@map[key]
134+
end
151135

152-
private
153-
def prune(force = false)
154-
@values = @map.values
155-
@size = @map.size
156-
end
136+
def []=(key, value)
137+
@map.select! { |c, _| c.alive? }
138+
@map[key] = value
157139
end
158140
end
159141

@@ -186,7 +168,7 @@ def clear(connection)
186168
class LeaseRegistry # :nodoc:
187169
def initialize
188170
@mutex = Mutex.new
189-
@map = WeakKeyMap.new
171+
@map = WeakThreadKeyMap.new
190172
end
191173

192174
def [](context)
@@ -197,7 +179,7 @@ def [](context)
197179

198180
def clear
199181
@mutex.synchronize do
200-
@map = WeakKeyMap.new
182+
@map.clear
201183
end
202184
end
203185
end

activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ def [](key)
6666

6767
def compute_if_absent(key)
6868
check_version
69+
6970
return yield unless @enabled
7071

7172
if entry = @map.delete(key)
@@ -93,10 +94,30 @@ def check_version
9394
end
9495
end
9596

97+
class QueryCacheRegistry # :nodoc:
98+
def initialize
99+
@mutex = Mutex.new
100+
@map = ConnectionPool::WeakThreadKeyMap.new
101+
end
102+
103+
def compute_if_absent(context)
104+
@map[context] || @mutex.synchronize do
105+
@map[context] ||= yield
106+
end
107+
end
108+
109+
def clear
110+
@map.synchronize do
111+
@map.clear
112+
end
113+
end
114+
end
115+
96116
module ConnectionPoolConfiguration # :nodoc:
97117
def initialize(...)
98118
super
99119
@query_cache_version = Concurrent::AtomicFixnum.new
120+
@thread_query_caches = QueryCacheRegistry.new
100121
@query_cache_max_size = \
101122
case query_cache = db_config&.query_cache
102123
when 0, false
@@ -108,10 +129,6 @@ def initialize(...)
108129
end
109130
end
110131

111-
def query_cache_version
112-
synchronize { @query_cache_version }
113-
end
114-
115132
def checkout_and_verify(connection)
116133
super
117134
connection.query_cache ||= query_cache
@@ -166,8 +183,9 @@ def clear_query_cache
166183
end
167184

168185
def query_cache
169-
caches = ActiveSupport::IsolatedExecutionState[:active_record_query_caches] ||= ConnectionPool::WeakKeyMap.new
170-
caches[self] ||= Store.new(@query_cache_version, @query_cache_max_size)
186+
@thread_query_caches.compute_if_absent(ActiveSupport::IsolatedExecutionState.context) do
187+
Store.new(@query_cache_version, @query_cache_max_size)
188+
end
171189
end
172190
end
173191

0 commit comments

Comments
 (0)