ezpool 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +11 -0
- data/Changes.md +131 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +165 -0
- data/Rakefile +6 -0
- data/ezpool.gemspec +21 -0
- data/lib/ezpool.rb +190 -0
- data/lib/ezpool/connection_manager.rb +37 -0
- data/lib/ezpool/connection_wrapper.rb +20 -0
- data/lib/ezpool/errors.rb +8 -0
- data/lib/ezpool/monotonic_time.rb +66 -0
- data/lib/ezpool/timed_stack.rb +207 -0
- data/lib/ezpool/version.rb +3 -0
- data/test/helper.rb +8 -0
- data/test/test_ezpool.rb +519 -0
- data/test/test_ezpool_timed_stack.rb +159 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a26d4a215e4a1e76ad3bfcdc570986a20e42f878
|
4
|
+
data.tar.gz: 5069ba506dcb424d68b7beb1d7c9843beb409fa9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d3538e8663f049aef366a5665e69d69f9ee3444b256fa41f0fa4c846370675bd6759f4c3a3353a5e07aa1aaa56e8cc21b2d92519d977c645b655d4d11761d4a
|
7
|
+
data.tar.gz: 67fdd2120df11acd5ce1856c33ab0a52c3abb8c354955f519b3daca7182a67093323a4501af947dbb56ef85dad1b764f5acbb77ad878e356fdfc229432113da3
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Changes.md
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
ezpool ChangeLog
|
2
|
+
================
|
3
|
+
|
4
|
+
1.0.0
|
5
|
+
-----
|
6
|
+
- Initial release of fork under the new name `ezpool`
|
7
|
+
- connect / disconnect methods are now arguments to the `EzPool` constructor
|
8
|
+
- Adds a new `max_age` parameter to gracefully recycle connections
|
9
|
+
- Removes re-entrant/recursive connection handling
|
10
|
+
|
11
|
+
|
12
|
+
Previous versions (pre-fork):
|
13
|
+
|
14
|
+
|
15
|
+
connection\_pool changelog
|
16
|
+
===========================
|
17
|
+
|
18
|
+
2.2.1
|
19
|
+
------
|
20
|
+
|
21
|
+
- Allow CP::Wrapper to use an existing pool [#87, etiennebarrie]
|
22
|
+
- Use monotonic time for more accurate timeouts [#84, jdantonio]
|
23
|
+
|
24
|
+
2.2.0
|
25
|
+
------
|
26
|
+
|
27
|
+
- Rollback `Timeout` handling introduced in 2.1.1 and 2.1.2. It seems
|
28
|
+
impossible to safely work around the issue. Please never, ever use
|
29
|
+
`Timeout.timeout` in your code or you will see rare but mysterious bugs. [#75]
|
30
|
+
|
31
|
+
2.1.3
|
32
|
+
------
|
33
|
+
|
34
|
+
- Don't increment created count until connection is successfully
|
35
|
+
created. [mylesmegyesi, #73]
|
36
|
+
|
37
|
+
2.1.2
|
38
|
+
------
|
39
|
+
|
40
|
+
- The connection\_pool will now close any connections which respond to
|
41
|
+
`close` (Dalli) or `disconnect!` (Redis). This ensures discarded connections
|
42
|
+
from the fix in 2.1.1 are torn down ASAP and don't linger open.
|
43
|
+
|
44
|
+
|
45
|
+
2.1.1
|
46
|
+
------
|
47
|
+
|
48
|
+
- Work around a subtle race condition with code which uses `Timeout.timeout` and
|
49
|
+
checks out a connection within the timeout block. This might cause
|
50
|
+
connections to get into a bad state and raise very odd errors. [tamird, #67]
|
51
|
+
|
52
|
+
|
53
|
+
2.1.0
|
54
|
+
------
|
55
|
+
|
56
|
+
- Refactoring to better support connection pool subclasses [drbrain,
|
57
|
+
#55]
|
58
|
+
- `with` should return value of the last expression [#59]
|
59
|
+
|
60
|
+
|
61
|
+
2.0.0
|
62
|
+
-----
|
63
|
+
|
64
|
+
- The connection pool is now lazy. Connections are created as needed
|
65
|
+
and retained until the pool is shut down. [drbrain, #52]
|
66
|
+
|
67
|
+
1.2.0
|
68
|
+
-----
|
69
|
+
|
70
|
+
- Add `with(options)` and `checkout(options)`. [mattcamuto]
|
71
|
+
Allows the caller to override the pool timeout.
|
72
|
+
```ruby
|
73
|
+
@pool.with(:timeout => 2) do |conn|
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
1.1.0
|
78
|
+
-----
|
79
|
+
|
80
|
+
- New `#shutdown` method (simao)
|
81
|
+
|
82
|
+
This method accepts a block and calls the block for each
|
83
|
+
connection in the pool. After calling this method, trying to get a
|
84
|
+
connection from the pool raises `PoolShuttingDownError`.
|
85
|
+
|
86
|
+
1.0.0
|
87
|
+
-----
|
88
|
+
|
89
|
+
- `#with_connection` is now gone in favor of `#with`.
|
90
|
+
|
91
|
+
- We no longer pollute the top level namespace with our internal
|
92
|
+
`TimedStack` class.
|
93
|
+
|
94
|
+
0.9.3
|
95
|
+
--------
|
96
|
+
|
97
|
+
- `#with_connection` is now deprecated in favor of `#with`.
|
98
|
+
|
99
|
+
A warning will be issued in the 0.9 series and the method will be
|
100
|
+
removed in 1.0.
|
101
|
+
|
102
|
+
- We now reuse objects when possible.
|
103
|
+
|
104
|
+
This means that under no contention, the same object will be checked
|
105
|
+
out from the pool after subsequent calls to `ConnectionPool#with`.
|
106
|
+
|
107
|
+
This change should have no impact on end user performance. If
|
108
|
+
anything, it should be an improvement, depending on what objects you
|
109
|
+
are pooling.
|
110
|
+
|
111
|
+
0.9.2
|
112
|
+
--------
|
113
|
+
|
114
|
+
- Fix reentrant checkout leading to early checkin.
|
115
|
+
|
116
|
+
0.9.1
|
117
|
+
--------
|
118
|
+
|
119
|
+
- Fix invalid superclass in version.rb
|
120
|
+
|
121
|
+
0.9.0
|
122
|
+
--------
|
123
|
+
|
124
|
+
- Move method\_missing magic into ConnectionPool::Wrapper (djanowski)
|
125
|
+
- Remove BasicObject superclass (djanowski)
|
126
|
+
|
127
|
+
0.1.0
|
128
|
+
--------
|
129
|
+
|
130
|
+
- More precise timeouts and better error message
|
131
|
+
- ConnectionPool now subclasses BasicObject so `method_missing` is more effective.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Mike Perham
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
ezpool
|
2
|
+
=================
|
3
|
+
[![Build Status](https://travis-ci.org/EasyPost/ezpool.svg?branch=master)](https://travis-ci.org/EasyPost/ezpool)
|
4
|
+
|
5
|
+
Generic connection pooling for Ruby. Originally forked from
|
6
|
+
[connection_pool](https://github.com/mperham/connection_pool), but with moderately different semantics.
|
7
|
+
|
8
|
+
MongoDB has its own connection pool. ActiveRecord has its own connection pool.
|
9
|
+
This is a generic connection pool that can be used with anything, e.g. Redis,
|
10
|
+
Dalli and other Ruby network clients.
|
11
|
+
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
Create a pool of objects to share amongst the fibers or threads in your Ruby
|
17
|
+
application:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
$memcached = EzPool.new(
|
21
|
+
size: 5,
|
22
|
+
timeout: 5,
|
23
|
+
connect: proc { Dalli::Client.new }
|
24
|
+
)
|
25
|
+
```
|
26
|
+
|
27
|
+
You can also pass your connection function as a block:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
$memcached = EzPool.new(size: 5, timeout: 5) { Dalli::Client.new }
|
31
|
+
```
|
32
|
+
|
33
|
+
Or you can configure it later:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
$memcached = EzPool.new(size: 5, timeout: 5)
|
37
|
+
$memcached.connect_with { Dalli::Client.new }
|
38
|
+
```
|
39
|
+
|
40
|
+
Then use the pool in your application:
|
41
|
+
|
42
|
+
``` ruby
|
43
|
+
$memcached.with do |conn|
|
44
|
+
conn.get('some-count')
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
If all the objects in the connection pool are in use, `with` will block
|
49
|
+
until one becomes available. If no object is available within `:timeout` seconds,
|
50
|
+
`with` will raise a `Timeout::Error`.
|
51
|
+
|
52
|
+
Optionally, you can specify a timeout override using the with-block semantics:
|
53
|
+
|
54
|
+
``` ruby
|
55
|
+
$memcached.with(timeout: 2.0) do |conn|
|
56
|
+
conn.get('some-count')
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
This will only modify the resource-get timeout for this particular
|
61
|
+
invocation. This is useful if you want to fail-fast on certain non critical
|
62
|
+
sections when a resource is not available, or conversely if you are comfortable
|
63
|
+
blocking longer on a particular resource. This is not implemented in the below
|
64
|
+
`EzPool::Wrapper` class.
|
65
|
+
|
66
|
+
Note that you can also explicitly check-in/check-out connections using the `#checkin`
|
67
|
+
and `#checkout` methods; however, there are no safety nets here! Once you check out a connection,
|
68
|
+
nobody else may use it until you check it back in, and if you leak it, it's gone for good;
|
69
|
+
we don't do anything clever like override the finalizer so that we can detect when
|
70
|
+
the connection goes out of scope and return it to the pool. Caveat emptor!
|
71
|
+
|
72
|
+
## Migrating to a Connection Pool
|
73
|
+
|
74
|
+
You can use `EzPool::Wrapper` to wrap a single global connection,
|
75
|
+
making it easier to migrate existing connection code over time:
|
76
|
+
|
77
|
+
``` ruby
|
78
|
+
$redis = EzPool::Wrapper.new(size: 5, timeout: 3) { Redis.connect }
|
79
|
+
$redis.sadd('foo', 1)
|
80
|
+
$redis.smembers('foo')
|
81
|
+
```
|
82
|
+
|
83
|
+
The wrapper uses `method_missing` to checkout a connection, run the requested
|
84
|
+
method and then immediately check the connection back into the pool. It's
|
85
|
+
**not** high-performance so you'll want to port your performance sensitive code
|
86
|
+
to use `with` as soon as possible.
|
87
|
+
|
88
|
+
``` ruby
|
89
|
+
$redis.with do |conn|
|
90
|
+
conn.sadd('foo', 1)
|
91
|
+
conn.smembers('foo')
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
And, of course, if there's any kind of load balancing or distribution on
|
96
|
+
the other end of the connection, there is no guarantee that two subsequent calls
|
97
|
+
to a `Wrapper`-wrapped object will go to the same database.
|
98
|
+
|
99
|
+
Once you've ported your entire system to use `with`, you can simply remove
|
100
|
+
`Wrapper` and use the simpler and faster `EzPool`.
|
101
|
+
|
102
|
+
## Thread-safety / Connection Multiplexing
|
103
|
+
|
104
|
+
`EzPool`s are thread-safe and re-entrant in that the pool itself can be shared between different
|
105
|
+
threads and it is guaranteed that the same connection will never be returned from overlapping calls
|
106
|
+
to `#checkout` / `#with`. Note that this is achieved through the judicious use of mutexes; this code
|
107
|
+
is not appropriate for systems with hard real-time requiremnts. Then again, neither is Ruby.
|
108
|
+
|
109
|
+
The original `connection_pool` library had special functionality so that if you checked out
|
110
|
+
a connection from a thread which already had a checked out connection, you would be guaranteed
|
111
|
+
to get the same connection back again. This logic prevents a number of valable use cases,
|
112
|
+
so no longer exists. Each overlapping call to `pool.checkout` / `pool.with` will get a different
|
113
|
+
connection.
|
114
|
+
|
115
|
+
In particular, this feature could be abused to assume that adjacent calls to `Wrap`ed connections
|
116
|
+
from the same thread would go to the same database connection and perhaps the same session/transaction.
|
117
|
+
Don't do that. If you care about transactions or database sessions, you need to be explicitly checking
|
118
|
+
out and passing around connections.
|
119
|
+
|
120
|
+
|
121
|
+
## Shutdown
|
122
|
+
|
123
|
+
You can shut down a EzPool instance once it should no longer be used.
|
124
|
+
Further checkout attempts will immediately raise an error but existing checkouts
|
125
|
+
will work.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
cp = EzPool.new(
|
129
|
+
connect: lambda { Redis.new },
|
130
|
+
disconnect: lambda { |conn| conn.quit }
|
131
|
+
)
|
132
|
+
cp.shutdown
|
133
|
+
```
|
134
|
+
|
135
|
+
Shutting down a connection pool will block until all connections are checked in and closed.
|
136
|
+
**Note that shutting down is completely optional**; Ruby's garbage collector will reclaim
|
137
|
+
unreferenced pools under normal circumstances.
|
138
|
+
|
139
|
+
## Connection recycling
|
140
|
+
|
141
|
+
If you specify the `max_age` parameter to a connection pool, it will attempt to gracefully recycle
|
142
|
+
connections once they reach a certain age. This can be beneficial for, e.g., load balancers where you
|
143
|
+
want client applications to eventually pick up new databases coming into service without having to
|
144
|
+
explicitly restart the client processes.
|
145
|
+
|
146
|
+
|
147
|
+
Notes
|
148
|
+
-----
|
149
|
+
|
150
|
+
- Connections are lazily created as needed.
|
151
|
+
- There is no provision for repairing or checking the health of a connection;
|
152
|
+
connections should be self-repairing. This is true of the Dalli and Redis
|
153
|
+
clients.
|
154
|
+
- **WARNING**: Don't ever use `Timeout.timeout` in your Ruby code or you will see
|
155
|
+
occasional silent corruption and mysterious errors. The Timeout API is unsafe
|
156
|
+
and cannot be used correctly, ever. Use proper socket timeout options as
|
157
|
+
exposed by Net::HTTP, Redis, Dalli, etc.
|
158
|
+
|
159
|
+
|
160
|
+
Author
|
161
|
+
------
|
162
|
+
|
163
|
+
Originally by Mike Perham, [@mperham](https://twitter.com/mperham), <http://mikeperham.com>
|
164
|
+
|
165
|
+
Forked and modified by engineers at [EasyPost](https://www.easypost.com).
|
data/Rakefile
ADDED
data/ezpool.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require "./lib/ezpool/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "ezpool"
|
6
|
+
s.version = EzPool::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Mike Perham", "Damian Janowski", "James Brown"]
|
9
|
+
s.email = ["oss@easypost.com"]
|
10
|
+
s.homepage = "https://github.com/EasyPost/ezpool"
|
11
|
+
s.description = s.summary = %q{More featureful generic connection pool for Ruby}
|
12
|
+
|
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) }
|
16
|
+
s.require_paths = ["lib"]
|
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'
|
21
|
+
end
|
data/lib/ezpool.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require_relative 'ezpool/version'
|
2
|
+
require_relative 'ezpool/timed_stack'
|
3
|
+
require_relative 'ezpool/errors'
|
4
|
+
require_relative 'ezpool/connection_manager'
|
5
|
+
|
6
|
+
|
7
|
+
# Generic connection pool class for e.g. sharing a limited number of network connections
|
8
|
+
# among many threads. Note: Connections are lazily created.
|
9
|
+
#
|
10
|
+
# Example usage with block (faster):
|
11
|
+
#
|
12
|
+
# @pool = EzPool.new { Redis.new }
|
13
|
+
#
|
14
|
+
# @pool.with do |redis|
|
15
|
+
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Using optional timeout override (for that single invocation)
|
19
|
+
#
|
20
|
+
# @pool.with(timeout: 2.0) do |redis|
|
21
|
+
# redis.lpop('my-list') if redis.llen('my-list') > 0
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Example usage replacing an existing connection (slower):
|
25
|
+
#
|
26
|
+
# $redis = EzPool.wrap { Redis.new }
|
27
|
+
#
|
28
|
+
# def do_work
|
29
|
+
# $redis.lpop('my-list') if $redis.llen('my-list') > 0
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# Note that there's no way to pass a disconnection function to this
|
33
|
+
# usage, nor any way to guarantee that subsequent calls will go to the
|
34
|
+
# same connection (if your connection has any concept of sessions, this
|
35
|
+
# may be important). We strongly recommend against using wrapped
|
36
|
+
# connections in production environments.
|
37
|
+
#
|
38
|
+
# Accepts the following options:
|
39
|
+
# - :size - number of connections to pool, defaults to 5
|
40
|
+
# - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
|
41
|
+
# - :max_age - maximum number of seconds that a connection may be alive for (will recycle on checkin/checkout)
|
42
|
+
# - :connect_with - callable for creating a connection
|
43
|
+
# - :disconnect-_with - callable for shutting down a connection
|
44
|
+
#
|
45
|
+
class EzPool
|
46
|
+
DEFAULTS = {size: 5, timeout: 1, max_age: Float::INFINITY}
|
47
|
+
|
48
|
+
def self.wrap(options, &block)
|
49
|
+
if block_given?
|
50
|
+
options[:connect_with] = block
|
51
|
+
end
|
52
|
+
Wrapper.new(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(options = {}, &block)
|
56
|
+
options = DEFAULTS.merge(options)
|
57
|
+
|
58
|
+
@size = options.fetch(:size)
|
59
|
+
@timeout = options.fetch(:timeout)
|
60
|
+
@max_age = options.fetch(:max_age).to_f
|
61
|
+
|
62
|
+
if @max_age <= 0
|
63
|
+
raise ArgumentError.new(":max_age must be > 0")
|
64
|
+
end
|
65
|
+
|
66
|
+
if block_given?
|
67
|
+
if options.include?(:connect_with)
|
68
|
+
raise ArgumentError.new("Block passed to EzPool *and* :connect_with in options")
|
69
|
+
else
|
70
|
+
options[:connect_with] = block
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@manager = EzPool::ConnectionManager.new(options[:connect_with], options[:disconnect_with])
|
75
|
+
|
76
|
+
@available = TimedStack.new(@manager, @size)
|
77
|
+
@key = :"current-#{@available.object_id}"
|
78
|
+
|
79
|
+
@checked_out_connections = Hash.new
|
80
|
+
@mutex = Mutex.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def connect_with(&block)
|
84
|
+
@manager.connect_with(&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def disconnect_with(&block)
|
88
|
+
@manager.disconnect_with(&block)
|
89
|
+
end
|
90
|
+
|
91
|
+
if Thread.respond_to?(:handle_interrupt)
|
92
|
+
# MRI
|
93
|
+
def with(options = {})
|
94
|
+
Thread.handle_interrupt(Exception => :never) do
|
95
|
+
conn = checkout(options)
|
96
|
+
begin
|
97
|
+
Thread.handle_interrupt(Exception => :immediate) do
|
98
|
+
yield conn
|
99
|
+
end
|
100
|
+
ensure
|
101
|
+
checkin conn
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
else
|
106
|
+
# non-MRI
|
107
|
+
def with(options = {})
|
108
|
+
conn = checkout(options)
|
109
|
+
begin
|
110
|
+
yield conn
|
111
|
+
ensure
|
112
|
+
checkin conn
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def checkout(options = {})
|
118
|
+
conn_wrapper = nil
|
119
|
+
while conn_wrapper.nil? do
|
120
|
+
timeout = options[:timeout] || @timeout
|
121
|
+
conn_wrapper = @available.pop(timeout: timeout)
|
122
|
+
if expired? conn_wrapper
|
123
|
+
@available.abandon(conn_wrapper)
|
124
|
+
conn_wrapper = nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
@mutex.synchronize do
|
129
|
+
@checked_out_connections[conn_wrapper.raw_conn.object_id] = conn_wrapper
|
130
|
+
end
|
131
|
+
conn_wrapper.raw_conn
|
132
|
+
end
|
133
|
+
|
134
|
+
def checkin(conn)
|
135
|
+
conn_wrapper = @mutex.synchronize do
|
136
|
+
@checked_out_connections.delete(conn.object_id)
|
137
|
+
end
|
138
|
+
if conn_wrapper.nil?
|
139
|
+
raise EzPool::CheckedInUnCheckedOutConnectionError
|
140
|
+
end
|
141
|
+
if expired? conn_wrapper
|
142
|
+
@available.abandon(conn_wrapper)
|
143
|
+
else
|
144
|
+
@available.push(conn_wrapper)
|
145
|
+
end
|
146
|
+
nil
|
147
|
+
end
|
148
|
+
|
149
|
+
def shutdown
|
150
|
+
if block_given?
|
151
|
+
raise ArgumentError.new("shutdown no longer accepts a block; call #disconnect_with to set the disconnect method, or pass the disconnect: option to the EzPool initializer")
|
152
|
+
end
|
153
|
+
@available.shutdown
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def expired?(connection_wrapper)
|
158
|
+
if @max_age.finite?
|
159
|
+
connection_wrapper.age > @max_age
|
160
|
+
else
|
161
|
+
false
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class Wrapper < ::BasicObject
|
166
|
+
METHODS = [:with, :pool_shutdown]
|
167
|
+
|
168
|
+
def initialize(options = {}, &block)
|
169
|
+
@pool = options.fetch(:pool) { ::EzPool.new(options, &block) }
|
170
|
+
end
|
171
|
+
|
172
|
+
def with(&block)
|
173
|
+
@pool.with(&block)
|
174
|
+
end
|
175
|
+
|
176
|
+
def pool_shutdown(&block)
|
177
|
+
@pool.shutdown(&block)
|
178
|
+
end
|
179
|
+
|
180
|
+
def respond_to?(id, *args)
|
181
|
+
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
|
182
|
+
end
|
183
|
+
|
184
|
+
def method_missing(name, *args, &block)
|
185
|
+
with do |connection|
|
186
|
+
connection.send(name, *args, &block)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|