connection_pool 2.2.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cf8177bf07f6c8e68b13f28416f6ccc0c64803af
4
- data.tar.gz: ff9c836ce5576d4d0cb1edf8d858a761f982e437
2
+ SHA256:
3
+ metadata.gz: e87682a6e57e8b0214de9c0713a7466a2b06399dc52d700fbf182cdf9e5b6606
4
+ data.tar.gz: d12337513b62d4677663403c512afad165275bdf848987182ce03637a1de6482
5
5
  SHA512:
6
- metadata.gz: 73bda4d8d0b3cc9daef5c93bb98b7649d811bd5c8f467e25c3aaf7d3353541225e7b30e1efd481a14e45f5c56b5fc846d49dabdd9a167702a71c4a9631a9b6a2
7
- data.tar.gz: a331275f67b3635e09792e646113e32bdc74254a2cdff032e972c6f2ebedd6391a1e508c36be7c4e33b40e2a07efe029a2ad0267c3ba4dc20b22813df86427bb
6
+ metadata.gz: eca2c1f8ebe52039f00df70ddfe5525cc0408acd8ae9849a8d1412bec670e7d3a05d3609db933e3e906cd83307d39f754dd5f1d7b48b50b847090b5bf485b55a
7
+ data.tar.gz: fc9a62b4b0ba5a406543e8f6399bd566f2f31c6c9a6ce2772e3550c1c2a75945c8a34defeda10c83e23cbfe5fe50d0e8861f57ea23935d7c97540aa97b04a635
data/Changes.md CHANGED
@@ -1,5 +1,34 @@
1
- connection\_pool changelog
2
- ---------------------------
1
+ # connection_pool Changelog
2
+
3
+ 2.4.0
4
+ ------
5
+
6
+ - Automatically drop all connections after fork [#166]
7
+
8
+ 2.3.0
9
+ ------
10
+
11
+ - Minimum Ruby version is now 2.5.0
12
+ - Add pool size to TimeoutError message
13
+
14
+ 2.2.5
15
+ ------
16
+
17
+ - Fix argument forwarding on Ruby 2.7 [#149]
18
+
19
+ 2.2.4
20
+ ------
21
+
22
+ - Add `reload` to close all connections, recreating them afterwards [Andrew Marshall, #140]
23
+ - Add `then` as a way to use a pool or a bare connection with the same code path [#138]
24
+
25
+ 2.2.3
26
+ ------
27
+
28
+ - Pool now throws `ConnectionPool::TimeoutError` on timeout. [#130]
29
+ - Use monotonic clock present in all modern Rubies [Tero Tasanen, #109]
30
+ - Remove code hacks necessary for JRuby 1.7
31
+ - Expose wrapped pool from ConnectionPool::Wrapper [Thomas Lecavelier, #113]
3
32
 
4
33
  2.2.2
5
34
  ------
data/README.md CHANGED
@@ -1,19 +1,17 @@
1
1
  connection\_pool
2
2
  =================
3
- [![Build Status](https://travis-ci.org/mperham/connection_pool.svg)](https://travis-ci.org/mperham/connection_pool)
3
+ [![Build Status](https://github.com/mperham/connection_pool/actions/workflows/ci.yml/badge.svg)](https://github.com/mperham/connection_pool/actions/workflows/ci.yml)
4
4
 
5
5
  Generic connection pooling for Ruby.
6
6
 
7
- MongoDB has its own connection pool. ActiveRecord has its own connection pool.
8
- This is a generic connection pool that can be used with anything, e.g. Redis,
9
- Dalli and other Ruby network clients.
10
-
7
+ MongoDB has its own connection pool.
8
+ ActiveRecord has its own connection pool.
9
+ This is a generic connection pool that can be used with anything, e.g. Redis, Dalli and other Ruby network clients.
11
10
 
12
11
  Usage
13
12
  -----
14
13
 
15
- Create a pool of objects to share amongst the fibers or threads in your Ruby
16
- application:
14
+ Create a pool of objects to share amongst the fibers or threads in your Ruby application:
17
15
 
18
16
  ``` ruby
19
17
  $memcached = ConnectionPool.new(size: 5, timeout: 5) { Dalli::Client.new }
@@ -28,8 +26,17 @@ end
28
26
  ```
29
27
 
30
28
  If all the objects in the connection pool are in use, `with` will block
31
- until one becomes available. If no object is available within `:timeout` seconds,
32
- `with` will raise a `Timeout::Error`.
29
+ until one becomes available.
30
+ If no object is available within `:timeout` seconds,
31
+ `with` will raise a `ConnectionPool::TimeoutError` (a subclass of `Timeout::Error`).
32
+
33
+ You can also use `ConnectionPool#then` to support _both_ a
34
+ connection pool and a raw client.
35
+
36
+ ```ruby
37
+ # Compatible with a raw Redis::Client, and ConnectionPool Redis
38
+ $redis.then { |r| r.set 'foo' 'bar' }
39
+ ```
33
40
 
34
41
  Optionally, you can specify a timeout override using the with-block semantics:
35
42
 
@@ -40,26 +47,23 @@ end
40
47
  ```
41
48
 
42
49
  This will only modify the resource-get timeout for this particular
43
- invocation. This is useful if you want to fail-fast on certain non critical
44
- sections when a resource is not available, or conversely if you are comfortable
45
- blocking longer on a particular resource. This is not implemented in the below
46
- `ConnectionPool::Wrapper` class.
50
+ invocation.
51
+ This is useful if you want to fail-fast on certain non-critical
52
+ sections when a resource is not available, or conversely if you are comfortable blocking longer on a particular resource.
53
+ This is not implemented in the `ConnectionPool::Wrapper` class.
47
54
 
48
55
  ## Migrating to a Connection Pool
49
56
 
50
- You can use `ConnectionPool::Wrapper` to wrap a single global connection,
51
- making it easier to migrate existing connection code over time:
57
+ You can use `ConnectionPool::Wrapper` to wrap a single global connection, making it easier to migrate existing connection code over time:
52
58
 
53
59
  ``` ruby
54
- $redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.connect }
60
+ $redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.new }
55
61
  $redis.sadd('foo', 1)
56
62
  $redis.smembers('foo')
57
63
  ```
58
64
 
59
- The wrapper uses `method_missing` to checkout a connection, run the requested
60
- method and then immediately check the connection back into the pool. It's
61
- **not** high-performance so you'll want to port your performance sensitive code
62
- to use `with` as soon as possible.
65
+ The wrapper uses `method_missing` to checkout a connection, run the requested method and then immediately check the connection back into the pool.
66
+ It's **not** high-performance so you'll want to port your performance sensitive code to use `with` as soon as possible.
63
67
 
64
68
  ``` ruby
65
69
  $redis.with do |conn|
@@ -68,40 +72,64 @@ $redis.with do |conn|
68
72
  end
69
73
  ```
70
74
 
71
- Once you've ported your entire system to use `with`, you can simply remove
72
- `Wrapper` and use the simpler and faster `ConnectionPool`.
75
+ Once you've ported your entire system to use `with`, you can simply remove `Wrapper` and use the simpler and faster `ConnectionPool`.
73
76
 
74
77
 
75
78
  ## Shutdown
76
79
 
77
80
  You can shut down a ConnectionPool instance once it should no longer be used.
78
- Further checkout attempts will immediately raise an error but existing checkouts
79
- will work.
81
+ Further checkout attempts will immediately raise an error but existing checkouts will work.
80
82
 
81
83
  ```ruby
82
84
  cp = ConnectionPool.new { Redis.new }
83
- cp.shutdown { |conn| conn.quit }
85
+ cp.shutdown { |c| c.close }
84
86
  ```
85
87
 
86
88
  Shutting down a connection pool will block until all connections are checked in and closed.
87
- **Note that shutting down is completely optional**; Ruby's garbage collector will reclaim
88
- unreferenced pools under normal circumstances.
89
+ **Note that shutting down is completely optional**; Ruby's garbage collector will reclaim unreferenced pools under normal circumstances.
90
+
91
+ ## Reload
92
+
93
+ 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.
94
+ Reloading may be useful after forking the process.
95
+
96
+ ```ruby
97
+ cp = ConnectionPool.new { Redis.new }
98
+ cp.reload { |conn| conn.quit }
99
+ cp.with { |conn| conn.get('some-count') }
100
+ ```
101
+
102
+ Like `shutdown`, this will block until all connections are checked in and closed.
103
+
104
+ ## Current State
105
+
106
+ There are several methods that return information about a pool.
89
107
 
108
+ ```ruby
109
+ cp = ConnectionPool.new(size: 10) { Redis.new }
110
+ cp.size # => 10
111
+ cp.available # => 10
112
+
113
+ cp.with do |conn|
114
+ cp.size # => 10
115
+ cp.available # => 9
116
+ end
117
+ ```
90
118
 
91
119
  Notes
92
120
  -----
93
121
 
94
122
  - Connections are lazily created as needed.
95
123
  - There is no provision for repairing or checking the health of a connection;
96
- connections should be self-repairing. This is true of the Dalli and Redis
124
+ connections should be self-repairing. This is true of the Dalli and Redis
97
125
  clients.
98
126
  - **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see
99
- occasional silent corruption and mysterious errors. The Timeout API is unsafe
100
- and cannot be used correctly, ever. Use proper socket timeout options as
127
+ occasional silent corruption and mysterious errors. The Timeout API is unsafe
128
+ and cannot be used correctly, ever. Use proper socket timeout options as
101
129
  exposed by Net::HTTP, Redis, Dalli, etc.
102
130
 
103
131
 
104
132
  Author
105
133
  ------
106
134
 
107
- Mike Perham, [@mperham](https://twitter.com/mperham), <http://mikeperham.com>
135
+ Mike Perham, [@getajobmike](https://twitter.com/getajobmike), <https://www.mikeperham.com>
@@ -1,21 +1,22 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require "./lib/connection_pool/version"
3
2
 
4
3
  Gem::Specification.new do |s|
5
- s.name = "connection_pool"
6
- s.version = ConnectionPool::VERSION
7
- s.platform = Gem::Platform::RUBY
8
- s.authors = ["Mike Perham", "Damian Janowski"]
9
- s.email = ["mperham@gmail.com", "damian@educabilia.com"]
10
- s.homepage = "https://github.com/mperham/connection_pool"
11
- s.description = s.summary = %q{Generic connection pool for Ruby}
4
+ s.name = "connection_pool"
5
+ s.version = ConnectionPool::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Mike Perham", "Damian Janowski"]
8
+ s.email = ["mperham@gmail.com", "damian@educabilia.com"]
9
+ s.homepage = "https://github.com/mperham/connection_pool"
10
+ s.description = s.summary = "Generic connection pool for Ruby"
12
11
 
13
- s.files = `git ls-files`.split("\n")
14
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ s.files = ["Changes.md", "LICENSE", "README.md", "connection_pool.gemspec",
13
+ "lib/connection_pool.rb", "lib/connection_pool/timed_stack.rb",
14
+ "lib/connection_pool/version.rb", "lib/connection_pool/wrapper.rb"]
15
+ s.executables = []
16
16
  s.require_paths = ["lib"]
17
17
  s.license = "MIT"
18
- s.add_development_dependency 'bundler'
19
- s.add_development_dependency 'minitest', '>= 5.0.0'
20
- s.add_development_dependency 'rake'
18
+ s.add_development_dependency "bundler"
19
+ s.add_development_dependency "minitest", ">= 5.0.0"
20
+ s.add_development_dependency "rake"
21
+ s.required_ruby_version = ">= 2.5.0"
21
22
  end
@@ -1,13 +1,3 @@
1
- require 'thread'
2
- require 'timeout'
3
- require_relative 'monotonic_time'
4
-
5
- ##
6
- # Raised when you attempt to retrieve a connection from a pool that has been
7
- # shut down.
8
-
9
- class ConnectionPool::PoolShuttingDownError < RuntimeError; end
10
-
11
1
  ##
12
2
  # The TimedStack manages a pool of homogeneous connections (or any resource
13
3
  # you wish to manage). Connections are created lazily up to a given maximum
@@ -25,7 +15,7 @@ class ConnectionPool::PoolShuttingDownError < RuntimeError; end
25
15
  #
26
16
  # conn = ts.pop
27
17
  # ts.pop timeout: 5
28
- # #=> raises Timeout::Error after 5 seconds
18
+ # #=> raises ConnectionPool::TimeoutError after 5 seconds
29
19
 
30
20
  class ConnectionPool::TimedStack
31
21
  attr_reader :max
@@ -39,8 +29,8 @@ class ConnectionPool::TimedStack
39
29
  @created = 0
40
30
  @que = []
41
31
  @max = size
42
- @mutex = Mutex.new
43
- @resource = ConditionVariable.new
32
+ @mutex = Thread::Mutex.new
33
+ @resource = Thread::ConditionVariable.new
44
34
  @shutdown_block = nil
45
35
  end
46
36
 
@@ -64,7 +54,7 @@ class ConnectionPool::TimedStack
64
54
  ##
65
55
  # Retrieves a connection from the stack. If a connection is available it is
66
56
  # immediately returned. If no connection is available within the given
67
- # timeout a Timeout::Error is raised.
57
+ # timeout a ConnectionPool::TimeoutError is raised.
68
58
  #
69
59
  # +:timeout+ is the only checked entry in +options+ and is preferred over
70
60
  # the +timeout+ argument (which will be removed in a future release). Other
@@ -74,7 +64,7 @@ class ConnectionPool::TimedStack
74
64
  options, timeout = timeout, 0.5 if Hash === timeout
75
65
  timeout = options.fetch :timeout, timeout
76
66
 
77
- deadline = ConnectionPool.monotonic_time + timeout
67
+ deadline = current_time + timeout
78
68
  @mutex.synchronize do
79
69
  loop do
80
70
  raise ConnectionPool::PoolShuttingDownError if @shutdown_block
@@ -83,25 +73,28 @@ class ConnectionPool::TimedStack
83
73
  connection = try_create(options)
84
74
  return connection if connection
85
75
 
86
- to_wait = deadline - ConnectionPool.monotonic_time
87
- raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
76
+ to_wait = deadline - current_time
77
+ raise ConnectionPool::TimeoutError, "Waited #{timeout} sec, #{length}/#{@max} available" if to_wait <= 0
88
78
  @resource.wait(@mutex, to_wait)
89
79
  end
90
80
  end
91
81
  end
92
82
 
93
83
  ##
94
- # Shuts down the TimedStack which prevents connections from being checked
95
- # out. The +block+ is called once for each connection on the stack.
84
+ # Shuts down the TimedStack by passing each connection to +block+ and then
85
+ # removing it from the pool. Attempting to checkout a connection after
86
+ # shutdown will raise +ConnectionPool::PoolShuttingDownError+ unless
87
+ # +:reload+ is +true+.
96
88
 
97
- def shutdown(&block)
98
- raise ArgumentError, "shutdown must receive a block" unless block_given?
89
+ def shutdown(reload: false, &block)
90
+ raise ArgumentError, "shutdown must receive a block" unless block
99
91
 
100
92
  @mutex.synchronize do
101
93
  @shutdown_block = block
102
94
  @resource.broadcast
103
95
 
104
96
  shutdown_connections
97
+ @shutdown_block = nil if reload
105
98
  end
106
99
  end
107
100
 
@@ -121,6 +114,10 @@ class ConnectionPool::TimedStack
121
114
 
122
115
  private
123
116
 
117
+ def current_time
118
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
119
+ end
120
+
124
121
  ##
125
122
  # This is an extension point for TimedStack and is called with a mutex.
126
123
  #
@@ -149,6 +146,7 @@ class ConnectionPool::TimedStack
149
146
  conn = fetch_connection(options)
150
147
  @shutdown_block.call(conn)
151
148
  end
149
+ @created = 0
152
150
  end
153
151
 
154
152
  ##
@@ -1,3 +1,3 @@
1
1
  class ConnectionPool
2
- VERSION = "2.2.2"
2
+ VERSION = "2.4.0"
3
3
  end
@@ -0,0 +1,56 @@
1
+ class ConnectionPool
2
+ class Wrapper < ::BasicObject
3
+ METHODS = [:with, :pool_shutdown, :wrapped_pool]
4
+
5
+ def initialize(options = {}, &block)
6
+ @pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) }
7
+ end
8
+
9
+ def wrapped_pool
10
+ @pool
11
+ end
12
+
13
+ def with(&block)
14
+ @pool.with(&block)
15
+ end
16
+
17
+ def pool_shutdown(&block)
18
+ @pool.shutdown(&block)
19
+ end
20
+
21
+ def pool_size
22
+ @pool.size
23
+ end
24
+
25
+ def pool_available
26
+ @pool.available
27
+ end
28
+
29
+ def respond_to?(id, *args)
30
+ METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
31
+ end
32
+
33
+ # rubocop:disable Style/MissingRespondToMissing
34
+ if ::RUBY_VERSION >= "3.0.0"
35
+ def method_missing(name, *args, **kwargs, &block)
36
+ with do |connection|
37
+ connection.send(name, *args, **kwargs, &block)
38
+ end
39
+ end
40
+ elsif ::RUBY_VERSION >= "2.7.0"
41
+ ruby2_keywords def method_missing(name, *args, &block)
42
+ with do |connection|
43
+ connection.send(name, *args, &block)
44
+ end
45
+ end
46
+ else
47
+ def method_missing(name, *args, &block)
48
+ with do |connection|
49
+ connection.send(name, *args, &block)
50
+ end
51
+ end
52
+ end
53
+ # rubocop:enable Style/MethodMissingSuper
54
+ # rubocop:enable Style/MissingRespondToMissing
55
+ end
56
+ end
@@ -1,14 +1,20 @@
1
- require_relative 'connection_pool/version'
2
- require_relative 'connection_pool/timed_stack'
1
+ require "timeout"
2
+ require_relative "connection_pool/version"
3
3
 
4
+ class ConnectionPool
5
+ class Error < ::RuntimeError; end
6
+
7
+ class PoolShuttingDownError < ::ConnectionPool::Error; end
8
+
9
+ class TimeoutError < ::Timeout::Error; end
10
+ end
4
11
 
5
- # Generic connection pool class for e.g. sharing a limited number of network connections
6
- # among many threads. Note: Connections are lazily created.
12
+ # Generic connection pool class for sharing a limited number of objects or network connections
13
+ # among many threads. Note: pool elements are lazily created.
7
14
  #
8
15
  # Example usage with block (faster):
9
16
  #
10
17
  # @pool = ConnectionPool.new { Redis.new }
11
- #
12
18
  # @pool.with do |redis|
13
19
  # redis.lpop('my-list') if redis.llen('my-list') > 0
14
20
  # end
@@ -34,29 +40,65 @@ require_relative 'connection_pool/timed_stack'
34
40
  class ConnectionPool
35
41
  DEFAULTS = {size: 5, timeout: 5}
36
42
 
37
- class Error < RuntimeError
38
- end
39
-
40
43
  def self.wrap(options, &block)
41
44
  Wrapper.new(options, &block)
42
45
  end
43
46
 
47
+ if Process.respond_to?(:fork)
48
+ INSTANCES = ObjectSpace::WeakMap.new
49
+ private_constant :INSTANCES
50
+
51
+ def self.after_fork
52
+ INSTANCES.values.each do |pool|
53
+ # We're on after fork, so we know all other threads are dead.
54
+ # All we need to do is to ensure the main thread doesn't have a
55
+ # checked out connection
56
+ pool.checkin(force: true)
57
+
58
+ pool.reload do |connection|
59
+ # Unfortunately we don't know what method to call to close the connection,
60
+ # so we try the most common one.
61
+ connection.close if connection.respond_to?(:close)
62
+ end
63
+ end
64
+ nil
65
+ end
66
+
67
+ if ::Process.respond_to?(:_fork) # MRI 3.1+
68
+ module ForkTracker
69
+ def _fork
70
+ pid = super
71
+ if pid == 0
72
+ ConnectionPool.after_fork
73
+ end
74
+ pid
75
+ end
76
+ end
77
+ Process.singleton_class.prepend(ForkTracker)
78
+ end
79
+ else
80
+ INSTANCES = nil
81
+ private_constant :INSTANCES
82
+
83
+ def self.after_fork
84
+ # noop
85
+ end
86
+ end
87
+
44
88
  def initialize(options = {}, &block)
45
- raise ArgumentError, 'Connection pool requires a block' unless block
89
+ raise ArgumentError, "Connection pool requires a block" unless block
46
90
 
47
91
  options = DEFAULTS.merge(options)
48
92
 
49
- @size = options.fetch(:size)
93
+ @size = Integer(options.fetch(:size))
50
94
  @timeout = options.fetch(:timeout)
51
95
 
52
96
  @available = TimedStack.new(@size, &block)
53
- @key = :"current-#{@available.object_id}"
54
- @key_count = :"current-#{@available.object_id}-count"
97
+ @key = :"pool-#{@available.object_id}"
98
+ @key_count = :"pool-#{@available.object_id}-count"
99
+ INSTANCES[self] = self if INSTANCES
55
100
  end
56
101
 
57
- if Thread.respond_to?(:handle_interrupt)
58
-
59
- # MRI
60
102
  def with(options = {})
61
103
  Thread.handle_interrupt(Exception => :never) do
62
104
  conn = checkout(options)
@@ -69,93 +111,60 @@ if Thread.respond_to?(:handle_interrupt)
69
111
  end
70
112
  end
71
113
  end
72
-
73
- else
74
-
75
- # jruby 1.7.x
76
- def with(options = {})
77
- conn = checkout(options)
78
- begin
79
- yield conn
80
- ensure
81
- checkin
82
- end
83
- end
84
-
85
- end
114
+ alias_method :then, :with
86
115
 
87
116
  def checkout(options = {})
88
117
  if ::Thread.current[@key]
89
- ::Thread.current[@key_count]+= 1
118
+ ::Thread.current[@key_count] += 1
90
119
  ::Thread.current[@key]
91
120
  else
92
- ::Thread.current[@key_count]= 1
93
- ::Thread.current[@key]= @available.pop(options[:timeout] || @timeout)
121
+ ::Thread.current[@key_count] = 1
122
+ ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout)
94
123
  end
95
124
  end
96
125
 
97
- def checkin
126
+ def checkin(force: false)
98
127
  if ::Thread.current[@key]
99
- if ::Thread.current[@key_count] == 1
128
+ if ::Thread.current[@key_count] == 1 || force
100
129
  @available.push(::Thread.current[@key])
101
- ::Thread.current[@key]= nil
130
+ ::Thread.current[@key] = nil
131
+ ::Thread.current[@key_count] = nil
102
132
  else
103
- ::Thread.current[@key_count]-= 1
133
+ ::Thread.current[@key_count] -= 1
104
134
  end
105
- else
106
- raise ConnectionPool::Error, 'no connections are checked out'
135
+ elsif !force
136
+ raise ConnectionPool::Error, "no connections are checked out"
107
137
  end
108
138
 
109
139
  nil
110
140
  end
111
141
 
142
+ ##
143
+ # Shuts down the ConnectionPool by passing each connection to +block+ and
144
+ # then removing it from the pool. Attempting to checkout a connection after
145
+ # shutdown will raise +ConnectionPool::PoolShuttingDownError+.
146
+
112
147
  def shutdown(&block)
113
148
  @available.shutdown(&block)
114
149
  end
115
150
 
116
- # Size of this connection pool
117
- def size
118
- @size
151
+ ##
152
+ # Reloads the ConnectionPool by passing each connection to +block+ and then
153
+ # removing it the pool. Subsequent checkouts will create new connections as
154
+ # needed.
155
+
156
+ def reload(&block)
157
+ @available.shutdown(reload: true, &block)
119
158
  end
120
159
 
160
+ # Size of this connection pool
161
+ attr_reader :size
162
+
121
163
  # Number of pool entries available for checkout at this instant.
122
164
  def available
123
165
  @available.length
124
166
  end
125
-
126
- private
127
-
128
- class Wrapper < ::BasicObject
129
- METHODS = [:with, :pool_shutdown]
130
-
131
- def initialize(options = {}, &block)
132
- @pool = options.fetch(:pool) { ::ConnectionPool.new(options, &block) }
133
- end
134
-
135
- def with(&block)
136
- @pool.with(&block)
137
- end
138
-
139
- def pool_shutdown(&block)
140
- @pool.shutdown(&block)
141
- end
142
-
143
- def pool_size
144
- @pool.size
145
- end
146
-
147
- def pool_available
148
- @pool.available
149
- end
150
-
151
- def respond_to?(id, *args)
152
- METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
153
- end
154
-
155
- def method_missing(name, *args, &block)
156
- with do |connection|
157
- connection.send(name, *args, &block)
158
- end
159
- end
160
- end
161
167
  end
168
+
169
+ require_relative "connection_pool/timed_stack"
170
+ require_relative "connection_pool/wrapper"