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

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_namesObject



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_namesObject



177
178
179
# File 'lib/apartment/patches/connection_registry.rb', line 177

def shard_names
  SYNC.synchronize { super }
end