diff --git a/Changes.md b/Changes.md index 356a718..6ef7231 100644 --- a/Changes.md +++ b/Changes.md @@ -3,6 +3,7 @@ HEAD ------ +- Add `reload` to close all connections, recreating them afterwards [Andrew Marshall, #140] - Add `then` as a way to use a pool or a bare connection with the same code path [#138] diff --git a/README.md b/README.md index 132cc98..55c832f 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,22 @@ Shutting down a connection pool will block until all connections are checked in **Note that shutting down is completely optional**; Ruby's garbage collector will reclaim unreferenced pools under normal circumstances. +## Reload + +You can reload a ConnectionPool instance in the case it is desired to close all +connections to the pool and, unlike `shutdown`, afterwards recreate connections +so the pool may continue to be used. Reloading may be useful after forking the +process. + +```ruby +cp = ConnectionPool.new { Redis.new } +cp.reload { |conn| conn.quit } +cp.with { |conn| conn.get('some-count') } +``` + +Like `shutdown`, this will block until all connections are checked in and +closed. + ## Current State There are several methods that return information about a pool. diff --git a/lib/connection_pool.rb b/lib/connection_pool.rb index 4ce44e6..88d8ffb 100644 --- a/lib/connection_pool.rb +++ b/lib/connection_pool.rb @@ -95,10 +95,24 @@ class ConnectionPool nil end + ## + # Shuts down the ConnectionPool by passing each connection to +block+ and + # then removing it from the pool. Attempting to checkout a connection after + # shutdown will raise +ConnectionPool::PoolShuttingDownError+. + def shutdown(&block) @available.shutdown(&block) end + ## + # Reloads the ConnectionPool by passing each connection to +block+ and then + # removing it the pool. Subsequent checkouts will create new connections as + # needed. + + def reload(&block) + @available.shutdown(reload: true, &block) + end + # Size of this connection pool attr_reader :size diff --git a/lib/connection_pool/timed_stack.rb b/lib/connection_pool/timed_stack.rb index ad44070..a677577 100644 --- a/lib/connection_pool/timed_stack.rb +++ b/lib/connection_pool/timed_stack.rb @@ -81,10 +81,12 @@ class ConnectionPool::TimedStack end ## - # Shuts down the TimedStack which prevents connections from being checked - # out. The +block+ is called once for each connection on the stack. + # Shuts down the TimedStack by passing each connection to +block+ and then + # removing it from the pool. Attempting to checkout a connection after + # shutdown will raise +ConnectionPool::PoolShuttingDownError+ unless + # +:reload+ is +true+. - def shutdown(&block) + def shutdown(reload: false, &block) raise ArgumentError, "shutdown must receive a block" unless block_given? @mutex.synchronize do @@ -92,6 +94,7 @@ class ConnectionPool::TimedStack @resource.broadcast shutdown_connections + @shutdown_block = nil if reload end end @@ -143,6 +146,7 @@ class ConnectionPool::TimedStack conn = fetch_connection(options) @shutdown_block.call(conn) end + @created = 0 end ## diff --git a/test/test_connection_pool_timed_stack.rb b/test/test_connection_pool_timed_stack.rb index d8742de..6a9c6d9 100644 --- a/test/test_connection_pool_timed_stack.rb +++ b/test/test_connection_pool_timed_stack.rb @@ -102,6 +102,16 @@ class TestConnectionPoolTimedStack < Minitest::Test end end + def test_pop_shutdown_reload + stack = ConnectionPool::TimedStack.new(1) { Object.new } + object = stack.pop + stack.push(object) + + stack.shutdown(reload: true) {} + + refute_equal object, stack.pop + end + def test_push stack = ConnectionPool::TimedStack.new(1) { Object.new }