Module: Apartment::Patches::ConnectionRegistry::PoolManagerSync
- Defined in:
- lib/apartment/patches/connection_registry.rb
Overview
Guards AR::ConnectionAdapters::PoolManager. Fully qualified everywhere because the bare constant would resolve to Apartment::PoolManager.
Instance Method Summary collapse
-
#each_pool_config(role = nil, &block) ⇒ Object
Snapshot under the lock, yield outside it.
- #get_pool_config(role, shard) ⇒ Object
- #pool_configs(role = nil) ⇒ Object
- #remove_pool_config(role, shard) ⇒ Object
- #remove_role(role) ⇒ Object
- #role_names ⇒ Object
- #set_pool_config(role, shard, pool_config) ⇒ Object
- #shard_names ⇒ Object
Instance Method Details
#each_pool_config(role = nil, &block) ⇒ Object
Snapshot under the lock, yield outside it. Holding the registry lock across the caller's block is what makes this dangerous rather than merely slow: AR's own iterating callers disconnect pools and release connections inside the block, so the lock would be held across pool IO while cold creates queue behind it.
Collected by delegating to super rather than by reading the Hash, so
the snapshot is exactly what upstream would have yielded on this Rails
version. One visible consequence, deliberate: a shard registered
mid-iteration is not yielded (a snapshot, like Concurrent::Map's
iterators elsewhere in Apartment).
THE RETURN VALUE IS UPSTREAM'S, NOT THE SNAPSHOT. Measured identical on
7.2 / 8.0 / 8.1: with a block, upstream returns the very Hash it walked
— the inner shard map when a role is given, the outer role map when not
— and the block-less form returns an Enumerator for a role but the outer
Hash (having enumerated nothing) without one. A lock is no reason to
narrow the contract of the method it wraps, and this is a :nodoc:
internal that other gems wrap too, so returning our Array would be a
gratuitous difference for anything that ever reads it. AR's own single
caller (ConnectionHandler#each_connection_pool) discards it.
The two block-less forms need opposite treatment, which is why they are not one branch:
WITH a role, upstream's Enumerator is a live view of the inner Hash, and
iterating it later bypasses this wrapper completely — a concurrent
set_pool_config takes SYNC and still mutates the Hash being walked, so
MRI raises in the writer. Probed: the failure is IDENTICAL patched and
unpatched, i.e. delegating here left the original race fully intact on
this path. So we substitute an Enumerator over this method; its deferred
traversal re-enters with a block and goes through the snapshot path
above. Contract preserved — upstream's is an Enumerator too, and size
is supplied because upstream's reports the shard count rather than nil.
(Repeated iteration re-snapshots, so it stays a live view like
upstream's, not a frozen one.)
WITHOUT a role, upstream returns the outer role map and enumerates nothing whatsoever. There is no traversal to protect and no Enumerator to match, so substituting one would invent behavior; delegate untouched.
245 246 247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/apartment/patches/connection_registry.rb', line 245 def each_pool_config(role = nil, &block) unless block return enum_for(__method__, role) { pool_configs(role).size } if role return super end snapshot = [] upstream_result = SYNC.synchronize { super(role) { |pool_config| snapshot << pool_config } } snapshot.each(&block) upstream_result end |
#get_pool_config(role, shard) ⇒ Object
197 198 199 |
# File 'lib/apartment/patches/connection_registry.rb', line 197 def get_pool_config(role, shard) SYNC.synchronize { super } end |
#pool_configs(role = nil) ⇒ Object
185 186 187 |
# File 'lib/apartment/patches/connection_registry.rb', line 185 def pool_configs(role = nil) SYNC.synchronize { super } end |
#remove_pool_config(role, shard) ⇒ Object
193 194 195 |
# File 'lib/apartment/patches/connection_registry.rb', line 193 def remove_pool_config(role, shard) SYNC.synchronize { super } end |
#remove_role(role) ⇒ Object
189 190 191 |
# File 'lib/apartment/patches/connection_registry.rb', line 189 def remove_role(role) SYNC.synchronize { super } end |
#role_names ⇒ Object
181 182 183 |
# File 'lib/apartment/patches/connection_registry.rb', line 181 def role_names SYNC.synchronize { super } end |
#set_pool_config(role, shard, pool_config) ⇒ Object
201 202 203 |
# File 'lib/apartment/patches/connection_registry.rb', line 201 def set_pool_config(role, shard, pool_config) SYNC.synchronize { super } end |
#shard_names ⇒ Object
177 178 179 |
# File 'lib/apartment/patches/connection_registry.rb', line 177 def shard_names SYNC.synchronize { super } end |