Add Rails 6 backport for ActiveRecord

This fixes the "can't create Thread: Resource temporarily unavailable" error.
This commit is contained in:
Patrick Gansterer 2021-09-09 13:23:20 +02:00
parent 0d6a3c14e9
commit 06b035f2ea
2 changed files with 100 additions and 0 deletions

View File

@ -215,6 +215,7 @@ Lint/ShadowingOuterLocalVariable:
# Configuration parameters: AllowComments, AllowNil.
Lint/SuppressedException:
Exclude:
- 'config/initializers/rails6_backports.rb'
- 'lib/foodsoft_config.rb'
- 'lib/tasks/rspec.rake'
@ -1495,6 +1496,7 @@ Style/MultilineBlockChain:
Exclude:
- 'app/helpers/group_orders_helper.rb'
- 'app/models/order.rb'
- 'config/initializers/rails6_backports.rb'
# Offense count: 2
# Cop supports --auto-correct.

View File

@ -0,0 +1,98 @@
raise "Remove no-longer-needed #{__FILE__}!" if Rails::VERSION::MAJOR >= 6
require "weakref"
module ActiveRecord
# Backport https://github.com/rails/rails/pull/36998 and https://github.com/rails/rails/pull/36999
# to avoid `ThreadError: can't create Thread: Resource temporarily unavailable` issues
module ConnectionAdapters
class ConnectionPool
class Reaper
@mutex = Mutex.new
@pools = {}
@threads = {}
class << self
def register_pool(pool, frequency) # :nodoc:
@mutex.synchronize do
unless @threads[frequency]&.alive?
@threads[frequency] = spawn_thread(frequency)
end
@pools[frequency] ||= []
@pools[frequency] << WeakRef.new(pool)
end
end
private
def spawn_thread(frequency)
Thread.new(frequency) do |t|
running = true
while running
sleep t
@mutex.synchronize do
@pools[frequency].select!(&:weakref_alive?)
@pools[frequency].each do |p|
p.reap
p.flush
rescue WeakRef::RefError
end
if @pools[frequency].empty?
@pools.delete(frequency)
@threads.delete(frequency)
running = false
end
end
end
end
end
end
def run
return unless frequency && frequency > 0
self.class.register_pool(pool, frequency)
end
end
def reap
stale_connections = synchronize do
return unless @connections
@connections.select do |conn|
conn.in_use? && !conn.owner.alive?
end.each(&:steal!)
end
stale_connections.each do |conn|
if conn.active?
conn.reset!
checkin conn
else
remove conn
end
end
end
def flush(minimum_idle = @idle_timeout)
return if minimum_idle.nil?
idle_connections = synchronize do
return unless @connections
@connections.select do |conn|
!conn.in_use? && conn.seconds_idle >= minimum_idle
end.each do |conn|
conn.lease
@available.delete conn
@connections.delete conn
end
end
idle_connections.each(&:disconnect!)
end
end
end
end