Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow restarting pool #140

Merged
merged 2 commits into from Jan 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Changes.md
Expand Up @@ -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]

Expand Down
16 changes: 16 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down
14 changes: 14 additions & 0 deletions lib/connection_pool.rb
Expand Up @@ -95,10 +95,24 @@ def checkin
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

Expand Down
10 changes: 7 additions & 3 deletions lib/connection_pool/timed_stack.rb
Expand Up @@ -81,17 +81,20 @@ def pop(timeout = 0.5, options = {})
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
@shutdown_block = block
@resource.broadcast

shutdown_connections
@shutdown_block = nil if reload
end
end

Expand Down Expand Up @@ -143,6 +146,7 @@ def shutdown_connections(options = nil)
conn = fetch_connection(options)
@shutdown_block.call(conn)
end
@created = 0
end

##
Expand Down
10 changes: 10 additions & 0 deletions test/test_connection_pool_timed_stack.rb
Expand Up @@ -102,6 +102,16 @@ def test_pop_shutdown
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 }

Expand Down