lazy_connection_pool 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +2 -0
- data/LICENSE.md +25 -0
- data/README.md +109 -0
- data/lazy_connection_pool.gemspec +19 -0
- data/lib/lazy_connection_pool.rb +107 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NTI4OGY1M2Y5OGQ4ZDliZmJlMTEzNzg2MWJhZGU5YzI3YTlkODJmZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MTVhM2I2YWY5N2NjYWNkMzdmYTUwMWUwY2M0M2M0ZWU5Y2QwN2RiMg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDY0YjI0ZjQ0ZmFjZTVmNDg5MjAwODRlM2M5MWI2ZDJlNDhlM2NjYmFkY2Nh
|
10
|
+
NzJhNWI1Y2JjNDZhMTc3Mzk1MzdlYjI5ZTFhYTcwNmU5MDY3NDAyYzg2YTgx
|
11
|
+
MmUxYWFlYTg5ZjRmOWFmM2RhODYzYjA3OTVhODIzMjIzMDI2ODU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTQ4YzA5Y2RkOGQxZTQyMWRhOTBkYmJjODhlM2EyYmExZTEyOGFmNjg4Mzdj
|
14
|
+
Y2VhMGM0NDE3MmRkYzVhNTNhNzFhOWQyNWZlNDdjMGU0YjkxOTJjNzBhMWUw
|
15
|
+
NzJkYmI4MzQ4M2VmOTE2NTYyZTc4ZmFhNTZiMzczYWNiNmRiYTg=
|
data/.gitignore
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
LazyConnectionPool License
|
2
|
+
==========================
|
3
|
+
|
4
|
+
LazyConnectionPool is provided as-is, with the hopes (but not the promise)
|
5
|
+
that you find it useful. No warranties are provided, express or implied.
|
6
|
+
|
7
|
+
The license may change in the future to be more restrictive, but as of
|
8
|
+
this commit, you can copy, include, fork, or redistribute the code, in
|
9
|
+
whole or in part, in source or (somehow) compiled, with the request that
|
10
|
+
source distributions mention the originating webpage and a description of
|
11
|
+
how much the code has changed since you got your hands on it.
|
12
|
+
|
13
|
+
For example:
|
14
|
+
|
15
|
+
"This code was originally based on LazyConnectionPool, with
|
16
|
+
some minor fixes"
|
17
|
+
|
18
|
+
or,
|
19
|
+
|
20
|
+
"copied from LazyConnectionPool, and totally rewritten."
|
21
|
+
|
22
|
+
If you rewrite it so significantly as to be unrecognizable, this request
|
23
|
+
is waived. Please use your best judgement as to what "unrecognizeable"
|
24
|
+
means.
|
25
|
+
|
data/README.md
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
LazyConnectionPooler
|
2
|
+
====================
|
3
|
+
|
4
|
+
A lazy connection pooler for Ruby.
|
5
|
+
|
6
|
+
This gem is meant to provide functionality similar to Mike Perham's lovely
|
7
|
+
[connection_pool](http://github.com/mperham/connection_pool), with the
|
8
|
+
twist that connections will be lazily created whenever there's a shortage
|
9
|
+
in the pool. It is not, however, meant to be a drop-in replacement for
|
10
|
+
that library.
|
11
|
+
|
12
|
+
I've tested this lightly under CRuby 1.9.3 and JRuby 1.7.10, and it doesn't
|
13
|
+
appear to deadlock or step on itself, even when running hundreds of threads
|
14
|
+
battling for a handful of connections. But my testing should hardly be a
|
15
|
+
promise that it's bug-free, or even functional.
|
16
|
+
|
17
|
+
Usage
|
18
|
+
=====
|
19
|
+
|
20
|
+
The documentation is, uhm, coming real soon now. The following will have
|
21
|
+
to suffice in the meantime.
|
22
|
+
|
23
|
+
Pool creation
|
24
|
+
-------------
|
25
|
+
|
26
|
+
Initiate a pool, passing in a block that'll be used to initialize new
|
27
|
+
connections:
|
28
|
+
|
29
|
+
pool = LazyConnectionPool.new {
|
30
|
+
Net::HTTP.new('localhost')
|
31
|
+
}
|
32
|
+
|
33
|
+
Connection usage: inline
|
34
|
+
------------------------
|
35
|
+
|
36
|
+
Your new `LazyConnectionPool` object's `#get` method can take a block. It
|
37
|
+
will pass your block a connection:
|
38
|
+
|
39
|
+
response = pool.get { |sock|
|
40
|
+
sock.get('/')
|
41
|
+
}
|
42
|
+
|
43
|
+
When used this way, `#get` will return te result of your block. If your
|
44
|
+
block raises any exceptions, the connection will still be returned to the
|
45
|
+
pool, and the exception will be yours to handle.
|
46
|
+
|
47
|
+
Connection usage: get/release
|
48
|
+
-----------------------------
|
49
|
+
|
50
|
+
You can also request a connection, then release it when you're done with
|
51
|
+
it, by calling `#get` without a block:
|
52
|
+
|
53
|
+
sock = pool.get
|
54
|
+
response = sock.get('/')
|
55
|
+
pool.release(sock)
|
56
|
+
|
57
|
+
Of course, if you don't ever `#release` your connection, it'll just leak
|
58
|
+
onto the floor.
|
59
|
+
|
60
|
+
Pool limits
|
61
|
+
-----------
|
62
|
+
|
63
|
+
You can optionally limit the size of the pool:
|
64
|
+
|
65
|
+
pool.poolsize = 16
|
66
|
+
|
67
|
+
The default is a limit of `-1`, which is unlimited. If you specify `0` as
|
68
|
+
a limit, no pool objects will be available.
|
69
|
+
|
70
|
+
Blocking requests
|
71
|
+
-----------------
|
72
|
+
|
73
|
+
If you run out of connections in the pool, such that you run into your
|
74
|
+
`poolsize` limit, `#get` will block until a connection is available.
|
75
|
+
`#get` takes one optional boolean argument, indicating whether it should
|
76
|
+
wait for a connection to become available. If it's false, it'll return
|
77
|
+
`nil`. Note that if you use a block with `#get` and it also potentially
|
78
|
+
returns `nil`, it could be difficult to differentiate between the two
|
79
|
+
conditions, as below:
|
80
|
+
|
81
|
+
body = pool.get(false) { |sock|
|
82
|
+
response = sock.get('/')
|
83
|
+
if response.code.to_i == 200
|
84
|
+
JSON.parse(response.body)
|
85
|
+
end
|
86
|
+
}
|
87
|
+
|
88
|
+
Would it have blocked? Did it connect but the response was something other
|
89
|
+
than a 200? The world may never know.
|
90
|
+
|
91
|
+
Unhealthy connections
|
92
|
+
---------------------
|
93
|
+
|
94
|
+
LazyConnectionPool assumes that the connections it hands out are healthy,
|
95
|
+
or can be made healthy without replacing the connection object. If this
|
96
|
+
isn't true, LazyConnectionPool is not for you. Your code should detect and
|
97
|
+
heal the connection object whenever needed.
|
98
|
+
|
99
|
+
Shrinking the pool
|
100
|
+
------------------
|
101
|
+
|
102
|
+
LazyConnectionPool adds connections to deal with shortages; it does not
|
103
|
+
reap them again, unless `poolsize` shrinks somehow.
|
104
|
+
|
105
|
+
Contributions
|
106
|
+
=============
|
107
|
+
|
108
|
+
Bug? Feature? Contributions of any kind: Send me a pull request.
|
109
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Gem::Specification.new { |gem|
|
2
|
+
gem.name = "lazy_connection_pool"
|
3
|
+
gem.version = "1.0.0"
|
4
|
+
gem.platform = Gem::Platform::RUBY
|
5
|
+
gem.authors = ["Joel Boutros"]
|
6
|
+
gem.licenses = [ 'BSD' ]
|
7
|
+
|
8
|
+
gem.homepage = "http://github.com/jaydeebee/lazy_connection_pool"
|
9
|
+
gem.summary = "A lazy connection pooler for Ruby"
|
10
|
+
gem.description = "A lazy connection pooler for Ruby. Supports lazy connection allocation and dynamic pool sizing."
|
11
|
+
|
12
|
+
gem.required_rubygems_version = ">= 1.3.6"
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.executables = []
|
16
|
+
|
17
|
+
gem.require_path = 'lib'
|
18
|
+
}
|
19
|
+
|
@@ -0,0 +1,107 @@
|
|
1
|
+
#
|
2
|
+
# LazyConnectionPool
|
3
|
+
#
|
4
|
+
# A connection pool that lazily creates connections and can be
|
5
|
+
# resized dynamically. It attempts optimistic thread-safety.
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'thread'
|
9
|
+
|
10
|
+
class LazyConnectionPool
|
11
|
+
def initialize(poolsize=-1, &block)
|
12
|
+
if block.nil?
|
13
|
+
raise ArgumentError, "LazyConnectionPool requires a block"
|
14
|
+
end
|
15
|
+
|
16
|
+
@max_pool_size = poolsize
|
17
|
+
@cur_pool_size = 0
|
18
|
+
@block = block
|
19
|
+
@pool = Queue.new
|
20
|
+
@mutex = Mutex.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def poolsize=(poolsize)
|
24
|
+
to_release = 0
|
25
|
+
|
26
|
+
@mutex.synchronize {
|
27
|
+
@max_pool_size = poolsize
|
28
|
+
if @max_pool_size >= 0
|
29
|
+
to_release = (@max_pool_size - @cur_pool_size)
|
30
|
+
to_release = @pool.num_waiting if @pool.num_waiting < to_release
|
31
|
+
to_release = 0 if to_release < 0
|
32
|
+
else
|
33
|
+
to_release = @pool.num_waiting
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
if to_release > 0
|
38
|
+
to_release.times { |x|
|
39
|
+
self.release @block.call
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
return @max_pool_size
|
44
|
+
end
|
45
|
+
|
46
|
+
def poolsize
|
47
|
+
@max_pool_size
|
48
|
+
end
|
49
|
+
|
50
|
+
def get(should_wait=true, &block)
|
51
|
+
cx = nil
|
52
|
+
|
53
|
+
# get a cx from the pool
|
54
|
+
begin
|
55
|
+
# see if we can get one. don't wait if we can't.
|
56
|
+
cx = @pool.pop(true)
|
57
|
+
rescue ThreadError
|
58
|
+
# We did not get one. Can we grow?
|
59
|
+
if @max_pool_size < 0 or @cur_pool_size < @max_pool_size
|
60
|
+
@mutex.synchronize {
|
61
|
+
@cur_pool_size += 1
|
62
|
+
if @max_pool_size >= 0 and @cur_pool_size > @max_pool_size
|
63
|
+
# back out of our creation (unwind after detecting we lost a
|
64
|
+
# a comparison race condition)
|
65
|
+
@cur_pool_size -= 1
|
66
|
+
else
|
67
|
+
cx = @block.call
|
68
|
+
end
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if cx.nil?
|
74
|
+
begin
|
75
|
+
cx = @pool.pop(!should_wait)
|
76
|
+
rescue ThreadError
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# now we have a cx.
|
82
|
+
if block.nil?
|
83
|
+
return cx
|
84
|
+
end
|
85
|
+
|
86
|
+
begin
|
87
|
+
r = yield cx
|
88
|
+
ensure
|
89
|
+
self.release(cx)
|
90
|
+
end
|
91
|
+
return r
|
92
|
+
end
|
93
|
+
|
94
|
+
def release(cx)
|
95
|
+
if @max_pool_size < 0 or (@max_pool_size > 0 and @cur_pool_size <= @max_pool_size)
|
96
|
+
@pool.push cx
|
97
|
+
else
|
98
|
+
@mutex.synchronize {
|
99
|
+
@cur_pool_size -= 1
|
100
|
+
@cur_pool_size = 0 if @cur_pool_size < 0
|
101
|
+
}
|
102
|
+
# XXX: close cx somehow?
|
103
|
+
end
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lazy_connection_pool
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joel Boutros
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A lazy connection pooler for Ruby. Supports lazy connection allocation
|
14
|
+
and dynamic pool sizing.
|
15
|
+
email:
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- LICENSE.md
|
22
|
+
- README.md
|
23
|
+
- lazy_connection_pool.gemspec
|
24
|
+
- lib/lazy_connection_pool.rb
|
25
|
+
homepage: http://github.com/jaydeebee/lazy_connection_pool
|
26
|
+
licenses:
|
27
|
+
- BSD
|
28
|
+
metadata: {}
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.3.6
|
43
|
+
requirements: []
|
44
|
+
rubyforge_project:
|
45
|
+
rubygems_version: 2.1.11
|
46
|
+
signing_key:
|
47
|
+
specification_version: 4
|
48
|
+
summary: A lazy connection pooler for Ruby
|
49
|
+
test_files: []
|