Skip to content

Commit f7463bb

Browse files
authored
Ability to optionally drop all connections after fork (#177)
There was a recent feature to automatically drop all connections after fork. This is quite nice and makes sense. However, for some rails app that usually follow the fork model (like w/ unicorn/puma), and additionally have some logic to fork processes to perform internal business logic that doesn't rely or use ConnectionPool, the application can observe Redis connection issues or resets. These forks can happen during application run time. Like ours. In such a case, it'd be nice to not automatically drop all the connections, since the underlying process isn't working with Redis/ConnectionPool, and as a sideeffect the pool in the primary process is impacted. This PR proposes a new attribute auto_reload_after_fork as a config option. By default it is true. However, application users can turn it to false and not opt in for the feature to auto drop connections after fork. This could be quite useful for us
1 parent 3d284f8 commit f7463bb

2 files changed

Lines changed: 19 additions & 2 deletions

File tree

lib/connection_pool.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ class TimeoutError < ::Timeout::Error; end
3636
# Accepts the following options:
3737
# - :size - number of connections to pool, defaults to 5
3838
# - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
39+
# - :auto_reload_after_fork - automatically drop all connections after fork, defaults to true
3940
#
4041
class ConnectionPool
41-
DEFAULTS = {size: 5, timeout: 5}
42+
DEFAULTS = {size: 5, timeout: 5, auto_reload_after_fork: true}
4243

4344
def self.wrap(options, &block)
4445
Wrapper.new(options, &block)
@@ -50,11 +51,12 @@ def self.wrap(options, &block)
5051

5152
def self.after_fork
5253
INSTANCES.values.each do |pool|
54+
next unless pool.auto_reload_after_fork
55+
5356
# We're on after fork, so we know all other threads are dead.
5457
# All we need to do is to ensure the main thread doesn't have a
5558
# checked out connection
5659
pool.checkin(force: true)
57-
5860
pool.reload do |connection|
5961
# Unfortunately we don't know what method to call to close the connection,
6062
# so we try the most common one.
@@ -92,6 +94,7 @@ def initialize(options = {}, &block)
9294

9395
@size = Integer(options.fetch(:size))
9496
@timeout = options.fetch(:timeout)
97+
@auto_reload_after_fork = options.fetch(:auto_reload_after_fork)
9598

9699
@available = TimedStack.new(@size, &block)
97100
@key = :"pool-#{@available.object_id}"
@@ -159,6 +162,8 @@ def reload(&block)
159162

160163
# Size of this connection pool
161164
attr_reader :size
165+
# Automatically drop all connections after fork
166+
attr_reader :auto_reload_after_fork
162167

163168
# Number of pool entries available for checkout at this instant.
164169
def available

test/test_connection_pool.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,18 @@ def test_after_fork_callback
559559
refute_equal(prefork_connection, pool.with { |c| c })
560560
end
561561

562+
def test_after_fork_callback_being_skipped
563+
skip("MRI feature") unless Process.respond_to?(:fork)
564+
GC.start # cleanup instances created by other tests
565+
566+
pool = ConnectionPool.new(size: 2, auto_reload_after_fork: false) { NetworkConnection.new }
567+
prefork_connection = pool.with { |c| c }
568+
assert_equal(prefork_connection, pool.with { |c| c })
569+
ConnectionPool.after_fork
570+
assert_equal(prefork_connection, pool.with { |c| c })
571+
end
572+
573+
562574
def test_after_fork_callback_checkin
563575
skip("MRI feature") unless Process.respond_to?(:fork)
564576
GC.start # cleanup instances created by other tests

0 commit comments

Comments
 (0)