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 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
@@ -0,0 +1,2 @@
1
+ .*.sw?
2
+
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: []