r18_connection_pool 2.2.2a
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 +10 -0
- data/Changes.md +123 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +107 -0
- data/Rakefile +6 -0
- data/devjournal +15 -0
- data/lib/connection_pool/monotonic_time.rb +66 -0
- data/lib/connection_pool/timed_stack.rb +176 -0
- data/lib/connection_pool/version.rb +3 -0
- data/lib/connection_pool.rb +161 -0
- data/r18_connection_pool.gemspec +21 -0
- data/test/helper.rb +8 -0
- data/test/test_connection_pool.rb +516 -0
- data/test/test_connection_pool_timed_stack.rb +149 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c24bd0aef22d03af8f4cb6d589696e5c6bbe8f0e0f4c6d3be3f96e8a1a4a21a0
|
4
|
+
data.tar.gz: 17778a4163543a80f269f39edbe26e20e026734f747294b8c01a7c307ad78661
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a87b9e013ac7dd203c70aa4269183161850f9c017c369f4a150dd0875383104f3627239e88de1cdac3a3c86773a62969b101f9065edf25497f134911b208094a
|
7
|
+
data.tar.gz: c659492935abc74d1757b1d187a0c7a08b87cd11608b27d41ce705ea1dc0d710b6920d664edd9c1eddf034af19ac5d7c9e760a439f1a401b64ec3fa811b42569
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Changes.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
connection\_pool changelog
|
2
|
+
---------------------------
|
3
|
+
|
4
|
+
2.2.2
|
5
|
+
------
|
6
|
+
|
7
|
+
- Add pool `size` and `available` accessors for metrics and monitoring
|
8
|
+
purposes [#97, robholland]
|
9
|
+
|
10
|
+
2.2.1
|
11
|
+
------
|
12
|
+
|
13
|
+
- Allow CP::Wrapper to use an existing pool [#87, etiennebarrie]
|
14
|
+
- Use monotonic time for more accurate timeouts [#84, jdantonio]
|
15
|
+
|
16
|
+
2.2.0
|
17
|
+
------
|
18
|
+
|
19
|
+
- Rollback `Timeout` handling introduced in 2.1.1 and 2.1.2. It seems
|
20
|
+
impossible to safely work around the issue. Please never, ever use
|
21
|
+
`Timeout.timeout` in your code or you will see rare but mysterious bugs. [#75]
|
22
|
+
|
23
|
+
2.1.3
|
24
|
+
------
|
25
|
+
|
26
|
+
- Don't increment created count until connection is successfully
|
27
|
+
created. [mylesmegyesi, #73]
|
28
|
+
|
29
|
+
2.1.2
|
30
|
+
------
|
31
|
+
|
32
|
+
- The connection\_pool will now close any connections which respond to
|
33
|
+
`close` (Dalli) or `disconnect!` (Redis). This ensures discarded connections
|
34
|
+
from the fix in 2.1.1 are torn down ASAP and don't linger open.
|
35
|
+
|
36
|
+
|
37
|
+
2.1.1
|
38
|
+
------
|
39
|
+
|
40
|
+
- Work around a subtle race condition with code which uses `Timeout.timeout` and
|
41
|
+
checks out a connection within the timeout block. This might cause
|
42
|
+
connections to get into a bad state and raise very odd errors. [tamird, #67]
|
43
|
+
|
44
|
+
|
45
|
+
2.1.0
|
46
|
+
------
|
47
|
+
|
48
|
+
- Refactoring to better support connection pool subclasses [drbrain,
|
49
|
+
#55]
|
50
|
+
- `with` should return value of the last expression [#59]
|
51
|
+
|
52
|
+
|
53
|
+
2.0.0
|
54
|
+
-----
|
55
|
+
|
56
|
+
- The connection pool is now lazy. Connections are created as needed
|
57
|
+
and retained until the pool is shut down. [drbrain, #52]
|
58
|
+
|
59
|
+
1.2.0
|
60
|
+
-----
|
61
|
+
|
62
|
+
- Add `with(options)` and `checkout(options)`. [mattcamuto]
|
63
|
+
Allows the caller to override the pool timeout.
|
64
|
+
```ruby
|
65
|
+
@pool.with(:timeout => 2) do |conn|
|
66
|
+
end
|
67
|
+
```
|
68
|
+
|
69
|
+
1.1.0
|
70
|
+
-----
|
71
|
+
|
72
|
+
- New `#shutdown` method (simao)
|
73
|
+
|
74
|
+
This method accepts a block and calls the block for each
|
75
|
+
connection in the pool. After calling this method, trying to get a
|
76
|
+
connection from the pool raises `PoolShuttingDownError`.
|
77
|
+
|
78
|
+
1.0.0
|
79
|
+
-----
|
80
|
+
|
81
|
+
- `#with_connection` is now gone in favor of `#with`.
|
82
|
+
|
83
|
+
- We no longer pollute the top level namespace with our internal
|
84
|
+
`TimedStack` class.
|
85
|
+
|
86
|
+
0.9.3
|
87
|
+
--------
|
88
|
+
|
89
|
+
- `#with_connection` is now deprecated in favor of `#with`.
|
90
|
+
|
91
|
+
A warning will be issued in the 0.9 series and the method will be
|
92
|
+
removed in 1.0.
|
93
|
+
|
94
|
+
- We now reuse objects when possible.
|
95
|
+
|
96
|
+
This means that under no contention, the same object will be checked
|
97
|
+
out from the pool after subsequent calls to `ConnectionPool#with`.
|
98
|
+
|
99
|
+
This change should have no impact on end user performance. If
|
100
|
+
anything, it should be an improvement, depending on what objects you
|
101
|
+
are pooling.
|
102
|
+
|
103
|
+
0.9.2
|
104
|
+
--------
|
105
|
+
|
106
|
+
- Fix reentrant checkout leading to early checkin.
|
107
|
+
|
108
|
+
0.9.1
|
109
|
+
--------
|
110
|
+
|
111
|
+
- Fix invalid superclass in version.rb
|
112
|
+
|
113
|
+
0.9.0
|
114
|
+
--------
|
115
|
+
|
116
|
+
- Move method\_missing magic into ConnectionPool::Wrapper (djanowski)
|
117
|
+
- Remove BasicObject superclass (djanowski)
|
118
|
+
|
119
|
+
0.1.0
|
120
|
+
--------
|
121
|
+
|
122
|
+
- More precise timeouts and better error message
|
123
|
+
- 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,107 @@
|
|
1
|
+
connection\_pool
|
2
|
+
=================
|
3
|
+
[![Build Status](https://travis-ci.org/mperham/connection_pool.svg)](https://travis-ci.org/mperham/connection_pool)
|
4
|
+
|
5
|
+
Generic connection pooling for Ruby.
|
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
|
+
|
11
|
+
|
12
|
+
Usage
|
13
|
+
-----
|
14
|
+
|
15
|
+
Create a pool of objects to share amongst the fibers or threads in your Ruby
|
16
|
+
application:
|
17
|
+
|
18
|
+
``` ruby
|
19
|
+
$memcached = ConnectionPool.new(size: 5, timeout: 5) { Dalli::Client.new }
|
20
|
+
```
|
21
|
+
|
22
|
+
Then use the pool in your application:
|
23
|
+
|
24
|
+
``` ruby
|
25
|
+
$memcached.with do |conn|
|
26
|
+
conn.get('some-count')
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
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`.
|
33
|
+
|
34
|
+
Optionally, you can specify a timeout override using the with-block semantics:
|
35
|
+
|
36
|
+
``` ruby
|
37
|
+
$memcached.with(timeout: 2.0) do |conn|
|
38
|
+
conn.get('some-count')
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
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.
|
47
|
+
|
48
|
+
## Migrating to a Connection Pool
|
49
|
+
|
50
|
+
You can use `ConnectionPool::Wrapper` to wrap a single global connection,
|
51
|
+
making it easier to migrate existing connection code over time:
|
52
|
+
|
53
|
+
``` ruby
|
54
|
+
$redis = ConnectionPool::Wrapper.new(size: 5, timeout: 3) { Redis.connect }
|
55
|
+
$redis.sadd('foo', 1)
|
56
|
+
$redis.smembers('foo')
|
57
|
+
```
|
58
|
+
|
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.
|
63
|
+
|
64
|
+
``` ruby
|
65
|
+
$redis.with do |conn|
|
66
|
+
conn.sadd('foo', 1)
|
67
|
+
conn.smembers('foo')
|
68
|
+
end
|
69
|
+
```
|
70
|
+
|
71
|
+
Once you've ported your entire system to use `with`, you can simply remove
|
72
|
+
`Wrapper` and use the simpler and faster `ConnectionPool`.
|
73
|
+
|
74
|
+
|
75
|
+
## Shutdown
|
76
|
+
|
77
|
+
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.
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
cp = ConnectionPool.new { Redis.new }
|
83
|
+
cp.shutdown { |conn| conn.quit }
|
84
|
+
```
|
85
|
+
|
86
|
+
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
|
+
|
90
|
+
|
91
|
+
Notes
|
92
|
+
-----
|
93
|
+
|
94
|
+
- Connections are lazily created as needed.
|
95
|
+
- 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
|
97
|
+
clients.
|
98
|
+
- **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
|
101
|
+
exposed by Net::HTTP, Redis, Dalli, etc.
|
102
|
+
|
103
|
+
|
104
|
+
Author
|
105
|
+
------
|
106
|
+
|
107
|
+
Mike Perham, [@mperham](https://twitter.com/mperham), <http://mikeperham.com>
|
data/Rakefile
ADDED
data/devjournal
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
--------------------------------
|
2
|
+
2023/08/04 takeshi:
|
3
|
+
|
4
|
+
I need connection_pool to run on ruby 1.8.
|
5
|
+
So I searched for a suitable gem version and it seems 2.2.3 (8b830d21fdc4c9ae1a41cc587e436478a280513c) will work with some small adjustment.
|
6
|
+
|
7
|
+
However, eventually, 2.2.3 required
|
8
|
+
Process::CLOCK_MONOTONIC
|
9
|
+
which we don't have in ruby 1.8
|
10
|
+
|
11
|
+
But 2.2.2 uses
|
12
|
+
Process::CLOCK_MONOTONIC
|
13
|
+
only if available so lets try to switch to it.
|
14
|
+
|
15
|
+
So this branch was created based on 2.2.2 (608f0f407161275d95e044adad48b9ae44d55ec1).
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Global monotonic clock from Concurrent Ruby 1.0.
|
2
|
+
# Copyright (c) Jerry D'Antonio -- released under the MIT license.
|
3
|
+
# Slightly modified; used with permission.
|
4
|
+
# https://github.com/ruby-concurrency/concurrent-ruby
|
5
|
+
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
class ConnectionPool
|
9
|
+
|
10
|
+
class_definition = Class.new do
|
11
|
+
|
12
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
13
|
+
|
14
|
+
# @!visibility private
|
15
|
+
def get_time
|
16
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
17
|
+
end
|
18
|
+
|
19
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
def get_time
|
23
|
+
java.lang.System.nanoTime() / 1_000_000_000.0
|
24
|
+
end
|
25
|
+
|
26
|
+
else
|
27
|
+
|
28
|
+
# @!visibility private
|
29
|
+
def initialize
|
30
|
+
@mutex = Mutex.new
|
31
|
+
@last_time = Time.now.to_f
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!visibility private
|
35
|
+
def get_time
|
36
|
+
@mutex.synchronize do
|
37
|
+
now = Time.now.to_f
|
38
|
+
if @last_time < now
|
39
|
+
@last_time = now
|
40
|
+
else # clock has moved back in time
|
41
|
+
@last_time += 0.000_001
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# Clock that cannot be set and represents monotonic time since
|
50
|
+
# some unspecified starting point.
|
51
|
+
#
|
52
|
+
# @!visibility private
|
53
|
+
GLOBAL_MONOTONIC_CLOCK = class_definition.new
|
54
|
+
#private_constant :GLOBAL_MONOTONIC_CLOCK # MayamaTakeshi to permit to use in ruby 1.8
|
55
|
+
|
56
|
+
class << self
|
57
|
+
##
|
58
|
+
# Returns the current time a tracked by the application monotonic clock.
|
59
|
+
#
|
60
|
+
# @return [Float] The current monotonic time when `since` not given else
|
61
|
+
# the elapsed monotonic time between `since` and the current time
|
62
|
+
def monotonic_time
|
63
|
+
GLOBAL_MONOTONIC_CLOCK.get_time
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'timeout'
|
3
|
+
require 'connection_pool/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
|
+
##
|
12
|
+
# The TimedStack manages a pool of homogeneous connections (or any resource
|
13
|
+
# you wish to manage). Connections are created lazily up to a given maximum
|
14
|
+
# number.
|
15
|
+
|
16
|
+
# Examples:
|
17
|
+
#
|
18
|
+
# ts = TimedStack.new(1) { MyConnection.new }
|
19
|
+
#
|
20
|
+
# # fetch a connection
|
21
|
+
# conn = ts.pop
|
22
|
+
#
|
23
|
+
# # return a connection
|
24
|
+
# ts.push conn
|
25
|
+
#
|
26
|
+
# conn = ts.pop
|
27
|
+
# ts.pop timeout: 5
|
28
|
+
# #=> raises Timeout::Error after 5 seconds
|
29
|
+
|
30
|
+
class ConnectionPool::TimedStack
|
31
|
+
attr_reader :max
|
32
|
+
|
33
|
+
##
|
34
|
+
# Creates a new pool with +size+ connections that are created from the given
|
35
|
+
# +block+.
|
36
|
+
|
37
|
+
def initialize(size = 0, &block)
|
38
|
+
@create_block = block
|
39
|
+
@created = 0
|
40
|
+
@que = []
|
41
|
+
@max = size
|
42
|
+
@mutex = Mutex.new
|
43
|
+
@resource = ConditionVariable.new
|
44
|
+
@shutdown_block = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns +obj+ to the stack. +options+ is ignored in TimedStack but may be
|
49
|
+
# used by subclasses that extend TimedStack.
|
50
|
+
|
51
|
+
def push(obj, options = {})
|
52
|
+
@mutex.synchronize do
|
53
|
+
if @shutdown_block
|
54
|
+
@shutdown_block.call(obj)
|
55
|
+
else
|
56
|
+
store_connection obj, options
|
57
|
+
end
|
58
|
+
|
59
|
+
@resource.broadcast
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method :<<, :push
|
63
|
+
|
64
|
+
##
|
65
|
+
# Retrieves a connection from the stack. If a connection is available it is
|
66
|
+
# immediately returned. If no connection is available within the given
|
67
|
+
# timeout a Timeout::Error is raised.
|
68
|
+
#
|
69
|
+
# +:timeout+ is the only checked entry in +options+ and is preferred over
|
70
|
+
# the +timeout+ argument (which will be removed in a future release). Other
|
71
|
+
# options may be used by subclasses that extend TimedStack.
|
72
|
+
|
73
|
+
def pop(timeout = 0.5, options = {})
|
74
|
+
options, timeout = timeout, 0.5 if Hash === timeout
|
75
|
+
timeout = options.fetch :timeout, timeout
|
76
|
+
|
77
|
+
deadline = ConnectionPool.monotonic_time + timeout
|
78
|
+
@mutex.synchronize do
|
79
|
+
loop do
|
80
|
+
raise ConnectionPool::PoolShuttingDownError if @shutdown_block
|
81
|
+
return fetch_connection(options) if connection_stored?(options)
|
82
|
+
|
83
|
+
connection = try_create(options)
|
84
|
+
return connection if connection
|
85
|
+
|
86
|
+
to_wait = deadline - ConnectionPool.monotonic_time
|
87
|
+
raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
|
88
|
+
@resource.wait(@mutex, to_wait)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Shuts down the TimedStack which prevents connections from being checked
|
95
|
+
# out. The +block+ is called once for each connection on the stack.
|
96
|
+
|
97
|
+
def shutdown(&block)
|
98
|
+
raise ArgumentError, "shutdown must receive a block" unless block_given?
|
99
|
+
|
100
|
+
@mutex.synchronize do
|
101
|
+
@shutdown_block = block
|
102
|
+
@resource.broadcast
|
103
|
+
|
104
|
+
shutdown_connections
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Returns +true+ if there are no available connections.
|
110
|
+
|
111
|
+
def empty?
|
112
|
+
(@created - @que.length) >= @max
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# The number of connections available on the stack.
|
117
|
+
|
118
|
+
def length
|
119
|
+
@max - @created + @que.length
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
##
|
125
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
126
|
+
#
|
127
|
+
# This method must returns true if a connection is available on the stack.
|
128
|
+
|
129
|
+
def connection_stored?(options = nil)
|
130
|
+
!@que.empty?
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
135
|
+
#
|
136
|
+
# This method must return a connection from the stack.
|
137
|
+
|
138
|
+
def fetch_connection(options = nil)
|
139
|
+
@que.pop
|
140
|
+
end
|
141
|
+
|
142
|
+
##
|
143
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
144
|
+
#
|
145
|
+
# This method must shut down all connections on the stack.
|
146
|
+
|
147
|
+
def shutdown_connections(options = nil)
|
148
|
+
while connection_stored?(options)
|
149
|
+
conn = fetch_connection(options)
|
150
|
+
@shutdown_block.call(conn)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
156
|
+
#
|
157
|
+
# This method must return +obj+ to the stack.
|
158
|
+
|
159
|
+
def store_connection(obj, options = nil)
|
160
|
+
@que.push obj
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# This is an extension point for TimedStack and is called with a mutex.
|
165
|
+
#
|
166
|
+
# This method must create a connection if and only if the total number of
|
167
|
+
# connections allowed has not been met.
|
168
|
+
|
169
|
+
def try_create(options = nil)
|
170
|
+
unless @created == @max
|
171
|
+
object = @create_block.call
|
172
|
+
@created += 1
|
173
|
+
object
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|