lazy_connection_pool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|