hot_tub 0.3.0 → 0.4.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 +5 -13
- data/.travis.yml +6 -9
- data/HISTORY.md +8 -1
- data/LICENSE.txt +1 -1
- data/README.md +11 -23
- data/benchmarks/hot_tub.rb +116 -0
- data/hot_tub.gemspec +3 -1
- data/lib/hot_tub/known_clients.rb +6 -4
- data/lib/hot_tub/pool.rb +65 -61
- data/lib/hot_tub/reaper.rb +14 -5
- data/lib/hot_tub/sessions.rb +33 -60
- data/lib/hot_tub/version.rb +1 -1
- data/lib/hot_tub.rb +3 -2
- data/spec/helpers/moc_pool.rb +1 -0
- data/spec/helpers/server.rb +16 -14
- data/spec/hot_tub/integration/excon_spec.rb +139 -0
- data/spec/hot_tub/integration/net_http_spec.rb +104 -0
- data/spec/hot_tub/integration/sessions_spec.rb +40 -0
- data/spec/hot_tub/pool_spec.rb +247 -0
- data/spec/{reaper_mixin_spec.rb → hot_tub/reaper_mixin_spec.rb} +7 -8
- data/spec/hot_tub/reaper_spec.rb +27 -0
- data/spec/hot_tub/sessions_spec.rb +137 -0
- data/spec/hot_tub_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -3
- metadata +63 -28
- data/spec/pool_spec.rb +0 -386
- data/spec/reaper_spec.rb +0 -28
- data/spec/sessions_spec.rb +0 -223
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
ZjAwZWMwNWJkNzE4ZDhlYTg2MzIyYjdlMGM1YTIxY2JkZWQ1ZGY3ZA==
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 814f65fdb0c6f97026ed4dcabbe50d8deaab6a4c
|
4
|
+
data.tar.gz: 4c9744264dae35c80d089194de51e8c445193cdd
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
ODQ2ZTBkMThmZmU4YjIxNTg3YWM1OGNlZWRmYTExOTFlY2JkOTYyMzM0MThm
|
11
|
-
M2U4ODI2MmE0NWI3ZmFiZmEwMzU0ZTdlZTY2NjIyMTMyMDdjMzM=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
M2QyMzI2OWY0MDBlZWYzYWYwYmVmNmIzMGMyZWVhOWZmYzk4NDEyNGYzYWIz
|
14
|
-
OWZkNmQwMDFmOTFhOTEzNDgwYTVmYzBlMTFjNjk0MTg4YzVmNzk2NjlkY2E3
|
15
|
-
YmNiOTUyYWE2OTNhYzFiY2EwYTc4MzhlZDg2OGYwMWU5NGE2Y2M=
|
6
|
+
metadata.gz: b343c114ffc19c435e44d981eebc68f85d31fa6f7f82461c75e95f292993bc6cf312a4172666b1aa85b1b4bb83be90002d5a347ac8bafd763d3511f0c57eaedd
|
7
|
+
data.tar.gz: 33e1b92fc75d10504679660ed36618d25dbe302e9229b0afba10a585fc5de9b3b4904088aed630f82bfb1eed11804573c6c7fefcdc73bcdc3f0a96be8ff75130
|
data/.travis.yml
CHANGED
data/HISTORY.md
CHANGED
@@ -4,7 +4,14 @@ HotTub Changelog
|
|
4
4
|
Head
|
5
5
|
=======
|
6
6
|
|
7
|
-
|
7
|
+
0.4.0
|
8
|
+
=======
|
9
|
+
- Hide HotTub::Sessions, should only be used with HotTub::Pool, otherwise just use ThreadSafe::Cache directly
|
10
|
+
- Reaper is now just a thread, and make sure we abort on exception
|
11
|
+
- add #reset! to Pool and Session for use after forking
|
12
|
+
- Test slow shutdowns
|
13
|
+
- Move integration tests and isolate
|
14
|
+
- General refactoring
|
8
15
|
|
9
16
|
0.3.0
|
10
17
|
=======
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -15,7 +15,7 @@ A thread safe, lazy pool.
|
|
15
15
|
* Support for closing resources on shutdown
|
16
16
|
|
17
17
|
### HotTub::Sessions
|
18
|
-
A [ThreadSafe::Cache](https://github.com/headius/thread_safe) where URLs are mapped to a
|
18
|
+
A [ThreadSafe::Cache](https://github.com/headius/thread_safe) where URLs are mapped to a HotTub::Pool
|
19
19
|
|
20
20
|
### Requirements
|
21
21
|
HotTub is tested on MRI, JRUBY and Rubinius
|
@@ -43,7 +43,6 @@ Configure Logger by creating `config\initializers\hot_tub.rb` and adding the fol
|
|
43
43
|
|
44
44
|
# Usage
|
45
45
|
|
46
|
-
## HotTub
|
47
46
|
For convenience you can initialize a new HotTub::Pool by calling HotTub.new or HotTub::Pool.new directly.
|
48
47
|
Returns an instance of HotTub::Pool.
|
49
48
|
|
@@ -69,6 +68,16 @@ Returns an instance of HotTub::Pool.
|
|
69
68
|
}
|
70
69
|
pool.run {|clnt| puts clnt.head('/').code }
|
71
70
|
|
71
|
+
### Excon
|
72
|
+
|
73
|
+
require 'hot_tub'
|
74
|
+
require 'excon'
|
75
|
+
|
76
|
+
pool = HotTub.new(:size => 10) {
|
77
|
+
Excon.new("http://somewebservice.com", :thread_safe_sockets => false)
|
78
|
+
}
|
79
|
+
pool.run {|clnt| puts clnt.head('/').status }
|
80
|
+
|
72
81
|
### HotTub Options
|
73
82
|
**size**: Default is 5. An integer that sets the size of the pool. Could be describe as minimum size the pool should grow to.
|
74
83
|
|
@@ -78,8 +87,6 @@ Returns an instance of HotTub::Pool.
|
|
78
87
|
|
79
88
|
**reap_timeout**: Default is 600 seconds. An integer that represents the timeout for reaping the pool in seconds.
|
80
89
|
|
81
|
-
**close_out**: Default is false. A boolean value that if true force close_client to be called on checkout clients when #drain! is called
|
82
|
-
|
83
90
|
**close**: Default is nil. Can be a symbol representing an method to call on a client to close the client or a lambda that accepts the client as a parameter that will close a client. The close option is performed on clients on reaping and shutdown after the client has been removed from the pool. When nil, as is the default, no action is performed.
|
84
91
|
|
85
92
|
**clean**: Default is nil. Can be a symbol representing an method to call on a client to clean the client or a lambda that accepts the client as a parameter that will clean a client. When nil, as is the default, no action is performed.
|
@@ -120,25 +127,6 @@ You can use any library you want with `HotTub::Pool`.
|
|
120
127
|
hot_tub = HotTub.new({:size => 10, :close => lambda {|clnt| clnt.close}, :clean => :clean, :reap => :reap?}) { MyHttpLib.new }
|
121
128
|
hot_tub.run { |clnt| clnt.get(url,query).body }
|
122
129
|
|
123
|
-
## Sessions only
|
124
|
-
Returns a `HotTub::Sessions` instance.
|
125
|
-
|
126
|
-
[Excon](https://github.com/geemus/excon) is thread safe but you set a single url at the client level so sessions
|
127
|
-
are handy if you need to access multiple URLs from a single instances
|
128
|
-
|
129
|
-
require 'hot_tub'
|
130
|
-
require 'excon'
|
131
|
-
# Our client block must accept the url argument
|
132
|
-
sessions = HotTub::Sessions.new {|url| Excon.new(url) }
|
133
|
-
|
134
|
-
sessions.run("http://somewebservice.com") do |clnt|
|
135
|
-
puts clnt.get(:query => {:some => 'stuff'}).response_header.status
|
136
|
-
end
|
137
|
-
|
138
|
-
sessions.run("https://someotherwebservice.com") do |clnt|
|
139
|
-
puts clnt.get(:query => {:other => 'stuff'}).response_header.status
|
140
|
-
end
|
141
|
-
|
142
130
|
## Dependencies
|
143
131
|
|
144
132
|
* [ThreadSafe](https://github.com/headius/thread_safe)
|
@@ -0,0 +1,116 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'benchmark'
|
3
|
+
require 'hot_tub'
|
4
|
+
|
5
|
+
class MocClient
|
6
|
+
def initialize(url=nil,options={})
|
7
|
+
end
|
8
|
+
|
9
|
+
def get
|
10
|
+
sleep(0.01)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
puts `ruby -v`
|
15
|
+
Benchmark.bmbm do |b|
|
16
|
+
|
17
|
+
b.report("single thread") do
|
18
|
+
hot_tub = HotTub::Pool.new(:size => 1, :max_size => 1, :no_reaper => true) { MocClient.new }
|
19
|
+
1000.times.each do
|
20
|
+
hot_tub.run do |conn|
|
21
|
+
conn.get
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
b.report("threaded size 5") do
|
27
|
+
hot_tub = HotTub::Pool.new(:size => 5, :max_size => 5) { MocClient.new }
|
28
|
+
threads = []
|
29
|
+
1000.times.each do
|
30
|
+
threads << Thread.new do
|
31
|
+
hot_tub.run do |conn|
|
32
|
+
conn.get
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
threads.each do |t|
|
37
|
+
t.join
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
b.report("threaded size 5, max 10") do
|
42
|
+
hot_tub = HotTub::Pool.new(:size => 5, :max_size => 10) { MocClient.new }
|
43
|
+
threads = []
|
44
|
+
1000.times.each do
|
45
|
+
threads << Thread.new do
|
46
|
+
hot_tub.run do |conn|
|
47
|
+
conn.get
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
threads.each do |t|
|
52
|
+
t.join
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
b.report("threaded, size 5, no max") do
|
57
|
+
hot_tub = HotTub::Pool.new(:size => 5) { MocClient.new }
|
58
|
+
threads = []
|
59
|
+
1000.times.each do
|
60
|
+
threads << Thread.new do
|
61
|
+
hot_tub.run do |conn|
|
62
|
+
conn.get
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
threads.each do |t|
|
67
|
+
t.join
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin14]
|
74
|
+
# Rehearsal ------------------------------------------------------------
|
75
|
+
# single thread 0.110000 0.040000 0.150000 ( 10.998645)
|
76
|
+
# threaded size 5 0.250000 0.320000 0.570000 ( 2.490553)
|
77
|
+
# threaded size 5, max 10 0.220000 0.290000 0.510000 ( 1.372965)
|
78
|
+
# threaded, size 5, no max 0.140000 0.130000 0.270000 ( 0.240067)
|
79
|
+
# --------------------------------------------------- total: 1.500000sec
|
80
|
+
|
81
|
+
# user system total real
|
82
|
+
# single thread 0.110000 0.050000 0.160000 ( 11.009793)
|
83
|
+
# threaded size 5 0.320000 0.320000 0.640000 ( 2.559244)
|
84
|
+
# threaded size 5, max 10 0.300000 0.270000 0.570000 ( 1.454562)
|
85
|
+
# threaded, size 5, no max 0.200000 0.130000 0.330000 ( 0.280806)
|
86
|
+
|
87
|
+
|
88
|
+
# rubinius 2.5.8 (2.1.0 bef51ae3 2015-07-14 3.5.1 JI) [x86_64-darwin14.4.0]
|
89
|
+
# Rehearsal ------------------------------------------------------------
|
90
|
+
# single thread 0.182959 0.057407 0.240366 ( 11.014685)
|
91
|
+
# threaded size 5 0.374537 0.274944 0.649481 ( 2.251464)
|
92
|
+
# threaded size 5, max 10 0.216058 0.215593 0.431651 ( 1.137440)
|
93
|
+
# threaded, size 5, no max 0.173595 0.120422 0.294017 ( 0.086586)
|
94
|
+
# --------------------------------------------------- total: 1.615515sec
|
95
|
+
|
96
|
+
# user system total real
|
97
|
+
# single thread 0.177439 0.056605 0.234044 ( 11.031641)
|
98
|
+
# threaded size 5 0.273678 0.302949 0.576627 ( 2.274915)
|
99
|
+
# threaded size 5, max 10 0.212687 0.204235 0.416922 ( 1.130088)
|
100
|
+
# threaded, size 5, no max 0.138528 0.095331 0.233859 ( 0.065469)
|
101
|
+
|
102
|
+
|
103
|
+
# jruby 9.0.3.0 (2.2.2) 2015-10-21 633c9aa Java HotSpot(TM) 64-Bit Server VM 23.5-b02 on 1.7.0_09-b05 +jit [darwin-x86_64]
|
104
|
+
# Rehearsal ------------------------------------------------------------
|
105
|
+
# single thread 1.160000 0.070000 1.230000 ( 11.521177)
|
106
|
+
# threaded size 5 1.280000 0.370000 1.650000 ( 2.450701)
|
107
|
+
# threaded size 5, max 10 0.840000 0.310000 1.150000 ( 1.167782)
|
108
|
+
# threaded, size 5, no max 0.600000 0.160000 0.760000 ( 0.173616)
|
109
|
+
# --------------------------------------------------- total: 4.790000sec
|
110
|
+
|
111
|
+
# user system total real
|
112
|
+
# single thread 0.780000 0.080000 0.860000 ( 11.264802)
|
113
|
+
# threaded size 5 0.560000 0.340000 0.900000 ( 2.269302)
|
114
|
+
# threaded size 5, max 10 0.610000 0.320000 0.930000 ( 1.168468)
|
115
|
+
# threaded, size 5, no max 0.300000 0.150000 0.450000 ( 0.150489)
|
116
|
+
|
data/hot_tub.gemspec
CHANGED
@@ -17,8 +17,10 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.add_runtime_dependency "thread_safe"
|
18
18
|
|
19
19
|
s.add_development_dependency "rspec"
|
20
|
+
s.add_development_dependency "rspec-autotest"
|
21
|
+
s.add_development_dependency "autotest"
|
20
22
|
s.add_development_dependency "sinatra"
|
21
|
-
s.add_development_dependency "puma", "~> 2.0
|
23
|
+
s.add_development_dependency "puma", "~> 2.0"
|
22
24
|
s.add_development_dependency "excon"
|
23
25
|
|
24
26
|
s.files = `git ls-files`.split("\n")
|
@@ -21,8 +21,9 @@ module HotTub
|
|
21
21
|
action = (@clean_client || known_client_action(clnt,:clean))
|
22
22
|
preform_client_action(clnt,action) if action
|
23
23
|
rescue => e
|
24
|
-
HotTub.logger.error "There was an error cleaning one of your #{self.class.name} clients: #{e}"
|
24
|
+
HotTub.logger.error "There was an error cleaning one of your #{self.class.name} clients: #{e}" if HotTub.logger
|
25
25
|
end
|
26
|
+
clnt
|
26
27
|
end
|
27
28
|
|
28
29
|
# Attempts to close the provided client, checking the options first for a close block
|
@@ -32,8 +33,9 @@ module HotTub
|
|
32
33
|
action = (@close_client || known_client_action(clnt,:close))
|
33
34
|
preform_client_action(clnt,action) if action
|
34
35
|
rescue => e
|
35
|
-
HotTub.logger.error "There was an error closing one of your #{self.class.name} clients: #{e}"
|
36
|
+
HotTub.logger.error "There was an error closing one of your #{self.class.name} clients: #{e}" if HotTub.logger
|
36
37
|
end
|
38
|
+
nil
|
37
39
|
end
|
38
40
|
|
39
41
|
# Attempts to determine if a client should be reaped, block should return a boolean
|
@@ -42,9 +44,9 @@ module HotTub
|
|
42
44
|
action = (@reap_client || known_client_action(clnt,:reap))
|
43
45
|
return preform_client_action(clnt,action) if action
|
44
46
|
rescue => e
|
45
|
-
HotTub.logger.error "There was an error reaping one of your #{self.class.name} clients: #{e}"
|
47
|
+
HotTub.logger.error "There was an error reaping one of your #{self.class.name} clients: #{e}" if HotTub.logger
|
46
48
|
end
|
47
|
-
|
49
|
+
false
|
48
50
|
end
|
49
51
|
|
50
52
|
private
|
data/lib/hot_tub/pool.rb
CHANGED
@@ -61,9 +61,6 @@ module HotTub
|
|
61
61
|
# in seconds. After said time a HotTub::Pool::Timeout exception will be thrown
|
62
62
|
# [:reap_timeout]
|
63
63
|
# Default is 600 seconds. An integer that represents the timeout for reaping the pool in seconds.
|
64
|
-
# [:close_out]
|
65
|
-
# Default is nil. A boolean like value that if it can be interpreted as true force close_client to be called
|
66
|
-
# on checkout clients when #drain! is called
|
67
64
|
# [:close]
|
68
65
|
# Default is nil. Can be a symbol representing an method to call on a client to close the client or a lambda
|
69
66
|
# that accepts the client as a parameter that will close a client. The close option is performed on clients
|
@@ -84,9 +81,8 @@ module HotTub
|
|
84
81
|
raise ArgumentError, 'a block that initializes a new client is required' unless block_given?
|
85
82
|
|
86
83
|
@size = (opts[:size] || 5) # in seconds
|
87
|
-
@wait_timeout = (opts[:wait_timeout] || 10)
|
84
|
+
@wait_timeout = (opts[:wait_timeout] || 10) # in seconds
|
88
85
|
@reap_timeout = (opts[:reap_timeout] || 600) # the interval to reap connections in seconds
|
89
|
-
@close_out = opts[:close_out] # if true on drain! call close_client block on checked out clients
|
90
86
|
@max_size = (opts[:max_size] || 0) # maximum size of pool when non-blocking, 0 means no limit
|
91
87
|
|
92
88
|
@close_client = opts[:close] # => lambda {|clnt| clnt.close} or :close
|
@@ -94,10 +90,10 @@ module HotTub
|
|
94
90
|
@reap_client = opts[:reap] # => lambda {|clnt| clnt.reap?} or :reap? # should return boolean
|
95
91
|
@new_client = new_client
|
96
92
|
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
93
|
+
@_pool = [] # stores available clients
|
94
|
+
@_pool.taint
|
95
|
+
@_out = [] # stores all checked out clients
|
96
|
+
@_out.taint
|
101
97
|
|
102
98
|
@mutex = Mutex.new
|
103
99
|
@cond = ConditionVariable.new
|
@@ -109,10 +105,10 @@ module HotTub
|
|
109
105
|
end
|
110
106
|
|
111
107
|
# Hand off to client.run
|
112
|
-
def run
|
108
|
+
def run
|
113
109
|
if block_given?
|
114
110
|
clnt = client
|
115
|
-
return
|
111
|
+
return yield clnt if clnt
|
116
112
|
else
|
117
113
|
raise ArgumentError, 'Run requires a block.'
|
118
114
|
end
|
@@ -124,7 +120,7 @@ module HotTub
|
|
124
120
|
# Its possible clients may be returned to the pool after cleaning
|
125
121
|
def clean!
|
126
122
|
@mutex.synchronize do
|
127
|
-
@
|
123
|
+
@_pool.each do |clnt|
|
128
124
|
clean_client(clnt)
|
129
125
|
end
|
130
126
|
end
|
@@ -136,30 +132,54 @@ module HotTub
|
|
136
132
|
# Its possible clients may be returned to the pool after cleaning
|
137
133
|
def drain!
|
138
134
|
@mutex.synchronize do
|
139
|
-
|
140
|
-
|
135
|
+
begin
|
136
|
+
while clnt = (@_pool.pop || @_out.pop)
|
137
|
+
close_client(clnt)
|
138
|
+
end
|
139
|
+
ensure
|
140
|
+
@cond.broadcast
|
141
141
|
end
|
142
|
-
@cond.broadcast
|
143
142
|
end
|
144
143
|
end
|
145
144
|
alias :close! :drain!
|
146
|
-
|
145
|
+
|
146
|
+
# Reset the pool and re-spawn reaper.
|
147
|
+
# or if shutdown allow threads to quickly finish their work
|
148
|
+
# Clients from the previous pool will not return to pool.
|
149
|
+
def reset!
|
150
|
+
@mutex.synchronize do
|
151
|
+
begin
|
152
|
+
while clnt = (@_pool.pop || @_out.pop)
|
153
|
+
close_client(clnt)
|
154
|
+
end
|
155
|
+
if @reaper
|
156
|
+
kill_reaper
|
157
|
+
@reaper = Reaper.spawn(self)
|
158
|
+
end
|
159
|
+
ensure
|
160
|
+
@cond.broadcast
|
161
|
+
end
|
162
|
+
end
|
163
|
+
nil
|
164
|
+
end
|
147
165
|
|
148
166
|
# Kills the reaper and drains the pool.
|
149
167
|
def shutdown!
|
150
168
|
@shutdown = true
|
169
|
+
kill_reaper if @reaper
|
170
|
+
ensure
|
151
171
|
drain!
|
152
172
|
end
|
153
173
|
|
154
174
|
# Remove and close extra clients
|
155
175
|
# Releases mutex each iteration because
|
156
|
-
# reaping is low priority action
|
176
|
+
# reaping is a low priority action
|
157
177
|
def reap!
|
158
|
-
start = Time.now
|
159
178
|
loop do
|
179
|
+
break if @shutdown
|
160
180
|
reaped = nil
|
161
181
|
@mutex.synchronize do
|
162
|
-
reaped = @
|
182
|
+
reaped = @_pool.shift if _reap?
|
163
183
|
end
|
164
184
|
if reaped
|
165
185
|
close_client(reaped)
|
@@ -173,6 +193,12 @@ module HotTub
|
|
173
193
|
(@max_size == 0)
|
174
194
|
end
|
175
195
|
|
196
|
+
def current_size
|
197
|
+
@mutex.synchronize do
|
198
|
+
_total_current_size
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
176
202
|
private
|
177
203
|
|
178
204
|
# Returns an instance of the client for this pool.
|
@@ -190,9 +216,11 @@ module HotTub
|
|
190
216
|
(time <= Time.now)
|
191
217
|
end
|
192
218
|
|
219
|
+
ALARM_MESSAGE = "Could not fetch a free client in time. Consider increasing your pool size."
|
220
|
+
|
193
221
|
def raise_alarm
|
194
|
-
message =
|
195
|
-
HotTub.logger.error message
|
222
|
+
message = ALARM_MESSAGE
|
223
|
+
HotTub.logger.error message if HotTub.logger
|
196
224
|
raise Timeout, message
|
197
225
|
end
|
198
226
|
|
@@ -201,13 +229,18 @@ module HotTub
|
|
201
229
|
def push(clnt)
|
202
230
|
if clnt
|
203
231
|
@mutex.synchronize do
|
204
|
-
|
205
|
-
|
206
|
-
|
232
|
+
begin
|
233
|
+
if @_out.delete(clnt)
|
234
|
+
unless @shutdown
|
235
|
+
@_pool << clnt
|
236
|
+
end
|
237
|
+
end
|
238
|
+
ensure
|
207
239
|
@cond.signal
|
208
240
|
end
|
209
241
|
end
|
210
242
|
close_client(clnt) if @shutdown
|
243
|
+
reap! unless @reaper
|
211
244
|
end
|
212
245
|
nil
|
213
246
|
end
|
@@ -220,8 +253,8 @@ module HotTub
|
|
220
253
|
break if @shutdown
|
221
254
|
raise_alarm if raise_alarm?(alarm)
|
222
255
|
@mutex.synchronize do
|
223
|
-
if (
|
224
|
-
@
|
256
|
+
if clnt = (@_pool.pop || _fetch_new)
|
257
|
+
@_out << clnt
|
225
258
|
else
|
226
259
|
@cond.wait(@mutex,@wait_timeout)
|
227
260
|
end
|
@@ -232,23 +265,11 @@ module HotTub
|
|
232
265
|
|
233
266
|
### START VOLATILE METHODS ###
|
234
267
|
|
235
|
-
# _empty? is volatile; and may cause be inaccurate
|
236
|
-
# if called outside @mutex.synchronize {}
|
237
|
-
def _empty?
|
238
|
-
@pool.empty?
|
239
|
-
end
|
240
|
-
|
241
|
-
# _space? is volatile; and may be inaccurate
|
242
|
-
# if called outside @mutex.synchronize {}
|
243
|
-
def _space?
|
244
|
-
!_empty?
|
245
|
-
end
|
246
|
-
|
247
268
|
# Returns the total number of clients in the pool
|
248
269
|
# and checked out. _total_current_size is volatile and
|
249
270
|
# may be inaccurate if called outside @mutex.synchronize {}
|
250
271
|
def _total_current_size
|
251
|
-
(@
|
272
|
+
(@_pool.length + @_out.length)
|
252
273
|
end
|
253
274
|
|
254
275
|
# Return true if we have reached our limit set by the :size option
|
@@ -265,23 +286,14 @@ module HotTub
|
|
265
286
|
(_total_current_size < @max_size)
|
266
287
|
end
|
267
288
|
|
268
|
-
# We only want to add a client if the pool is empty in keeping with
|
269
|
-
# a lazy model. If the pool is empty we can only add clients if
|
270
|
-
# never_block? is true or there is room to grow. _add? is volatile;
|
271
|
-
# and may be in accurate if called outside @mutex.synchronize {}
|
272
|
-
def _add?
|
273
|
-
(_empty? && (never_block? || _less_than_size?|| _less_than_max?))
|
274
|
-
end
|
275
|
-
|
276
289
|
# Adds a new client to the pool if its allowed
|
277
290
|
# _add is volatile; and may cause threading issues
|
278
291
|
# if called outside @mutex.synchronize {}
|
279
|
-
def
|
280
|
-
return
|
292
|
+
def _fetch_new
|
293
|
+
return nil unless (@_pool.empty? && (never_block? || _less_than_size?|| _less_than_max?))
|
281
294
|
nc = @new_client.call
|
282
|
-
HotTub.logger.info "Adding HotTub client: #{nc.class.name} to pool"
|
283
|
-
|
284
|
-
true
|
295
|
+
HotTub.logger.info "Adding HotTub client: #{nc.class.name} to pool" if HotTub.logger
|
296
|
+
nc
|
285
297
|
end
|
286
298
|
|
287
299
|
# Returns true if we have clients in the pool, the pool
|
@@ -290,15 +302,7 @@ module HotTub
|
|
290
302
|
# volatile; and may be inaccurate if called outside
|
291
303
|
# @mutex.synchronize {}
|
292
304
|
def _reap?
|
293
|
-
(
|
294
|
-
end
|
295
|
-
|
296
|
-
# Returns true if the pool is greater than the :size option and the
|
297
|
-
# pool has been stagnant long enough to allow for reaping (we don't
|
298
|
-
# want to reap under load). _overflow_expired? is volatile; and may
|
299
|
-
# be inaccurate if called outside @mutex.synchronize {}
|
300
|
-
def _overflow?
|
301
|
-
(@pool.length > @size)
|
305
|
+
( !@_pool.empty? && !@shutdown && ((@_pool.length > @size) || reap_client?(@_pool[0])))
|
302
306
|
end
|
303
307
|
|
304
308
|
### END VOLATILE METHODS ###
|
data/lib/hot_tub/reaper.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module HotTub
|
2
|
-
class Reaper
|
2
|
+
class Reaper
|
3
3
|
|
4
4
|
# Creates a new Reaper thread for work.
|
5
5
|
# Expects an object that responses to: :reap!
|
@@ -8,20 +8,21 @@ module HotTub
|
|
8
8
|
# so we rescue, log, and kill the reaper when an exception occurs
|
9
9
|
# https://bugs.ruby-lang.org/issues/6647
|
10
10
|
def self.spawn(obj)
|
11
|
-
th = new {
|
11
|
+
th = Thread.new {
|
12
12
|
loop do
|
13
13
|
begin
|
14
14
|
obj.reap!
|
15
15
|
break if obj.shutdown
|
16
16
|
sleep(obj.reap_timeout || 600)
|
17
17
|
rescue Exception => e
|
18
|
-
HotTub.logger.error "HotTub::Reaper for #{obj.class.name} terminated with exception: #{e.message}"
|
19
|
-
HotTub.logger.error e.backtrace.map {|line| " #{line}"}
|
18
|
+
HotTub.logger.error "HotTub::Reaper for #{obj.class.name} terminated with exception: #{e.message}" if HotTub.logger
|
19
|
+
HotTub.logger.error e.backtrace.map {|line| " #{line}"} if HotTub.logger
|
20
20
|
break
|
21
21
|
end
|
22
22
|
end
|
23
23
|
}
|
24
24
|
th[:name] = "HotTub::Reaper"
|
25
|
+
th.abort_on_exception = true
|
25
26
|
th
|
26
27
|
end
|
27
28
|
|
@@ -30,7 +31,15 @@ module HotTub
|
|
30
31
|
attr_reader :reap_timeout, :reaper, :shutdown
|
31
32
|
|
32
33
|
def reap!
|
33
|
-
raise NoMethodError.new('
|
34
|
+
raise NoMethodError.new('#reap! must be redefined in your class')
|
35
|
+
end
|
36
|
+
|
37
|
+
def kill_reaper
|
38
|
+
if @reaper
|
39
|
+
@reaper.kill
|
40
|
+
@reaper.join
|
41
|
+
@reaper = nil
|
42
|
+
end
|
34
43
|
end
|
35
44
|
end
|
36
45
|
end
|