hot_tub 0.0.4 → 0.1.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.
- data/Gemfile +2 -3
- data/LICENSE.txt +1 -1
- data/README.md +79 -7
- data/http_hot_tub.gemspec +7 -4
- data/lib/hot_tub.rb +11 -7
- data/lib/hot_tub/pool.rb +188 -0
- data/lib/hot_tub/session.rb +58 -117
- data/lib/hot_tub/version.rb +1 -1
- data/spec/pool_spec.rb +241 -0
- data/spec/session_spec.rb +72 -83
- data/spec/spec_helper.rb +29 -1
- metadata +34 -20
- data/lib/hot_tub/clients/client.rb +0 -37
- data/lib/hot_tub/clients/em_synchrony_client.rb +0 -41
- data/lib/hot_tub/clients/excon_client.rb +0 -24
- data/lib/hot_tub/clients/http_client_client.rb +0 -38
- data/spec/em_synchrony_client_spec.rb +0 -47
- data/spec/excon_client_spec.rb +0 -18
- data/spec/http_client_client_spec.rb +0 -16
- data/spec/test_helper_methods.rb +0 -33
data/lib/hot_tub/version.rb
CHANGED
data/spec/pool_spec.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
unless HotTub.jruby?
|
3
|
+
require "em-synchrony"
|
4
|
+
require "em-synchrony/em-http"
|
5
|
+
end
|
6
|
+
describe HotTub::Pool do
|
7
|
+
|
8
|
+
context 'default settings' do
|
9
|
+
before(:each) do
|
10
|
+
@pool = HotTub::Pool.new()
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have :size of 5" do
|
14
|
+
@pool.instance_variable_get(:@size).should eql(5)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should have :blocking_timeout of 0.5" do
|
18
|
+
@pool.instance_variable_get(:@blocking_timeout).should eql(10)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should have default :client" do
|
22
|
+
@pool.instance_variable_get(:@client_block).call.should be_a(HTTPClient)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be true" do
|
26
|
+
@pool.instance_variable_get(:@never_block).should be_true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'custom settings' do
|
31
|
+
before(:each) do
|
32
|
+
@pool = HotTub::Pool.new(:size => 10, :blocking_timeout => 1.0, :never_block => false) { MocClient.new }
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have :size of 5" do
|
36
|
+
@pool.instance_variable_get(:@size).should eql(10)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should have :blocking_timeout of 0.5" do
|
40
|
+
@pool.instance_variable_get(:@blocking_timeout).should eql(1.0)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should have defult :client" do
|
44
|
+
@pool.instance_variable_get(:@client_block).call.should be_a(MocClient)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should be true" do
|
48
|
+
@pool.instance_variable_get(:@never_block).should be_false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#run' do
|
53
|
+
before(:each) do
|
54
|
+
@pool = HotTub::Pool.new
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should remove connection from pool when doing work" do
|
58
|
+
@pool.run do |connection|
|
59
|
+
@connection = connection
|
60
|
+
@fetched = @pool.instance_variable_get(:@pool).select{|c| c.object_id == @connection.object_id}.length.should eql(0) # not in pool because its doing work
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return the connection after use" do
|
65
|
+
@pool.run do |connection|
|
66
|
+
@connection = connection
|
67
|
+
end
|
68
|
+
# returned to pool after work was done
|
69
|
+
@pool.instance_variable_get(:@pool).select{|c| c.object_id == @connection.object_id}.length.should eql(1)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should work" do
|
73
|
+
status = 0
|
74
|
+
@pool.run{|clnt| status = clnt.head('https://www.google.com').status}
|
75
|
+
status.should eql(200)
|
76
|
+
end
|
77
|
+
|
78
|
+
context "block given" do
|
79
|
+
it "should call the supplied block" do
|
80
|
+
status = nil
|
81
|
+
@pool.run { |con| status = con.get('https://google.com').status}
|
82
|
+
status.should_not be_nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'block not given' do
|
87
|
+
it "should raise ArgumentError" do
|
88
|
+
lambda { @pool.run }.should raise_error(ArgumentError)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#close_all' do
|
94
|
+
before(:each) do
|
95
|
+
@pool = HotTub::Pool.new(:size => 5)
|
96
|
+
5.times do
|
97
|
+
@pool.send(:add)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
it "should reset pool" do
|
101
|
+
@pool.current_size.should eql(5)
|
102
|
+
@pool.instance_variable_get(:@clients).length.should eql(5)
|
103
|
+
@pool.instance_variable_get(:@pool).length.should eql(5)
|
104
|
+
@pool.close_all
|
105
|
+
@pool.instance_variable_get(:@clients).length.should eql(0)
|
106
|
+
@pool.instance_variable_get(:@pool).length.should eql(0)
|
107
|
+
@pool.current_size.should eql(0)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'private methods' do
|
112
|
+
before(:each) do
|
113
|
+
@url = "http://www.testurl123.com/"
|
114
|
+
@pool = HotTub::Pool.new()
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#client' do
|
118
|
+
it "should raise HotTub::BlockingTimeout if an available is not found in time"do
|
119
|
+
@pool.instance_variable_set(:@never_block,false)
|
120
|
+
@pool.instance_variable_set(:@blocking_timeout,0.1)
|
121
|
+
@pool.stub(:pop).and_return(nil)
|
122
|
+
lambda { puts @pool.send(:client) }.should raise_error(HotTub::BlockingTimeout)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should return an instance of the driver" do
|
126
|
+
@pool.send(:client).should be_instance_of(HTTPClient)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'add?' do
|
131
|
+
it "should be true if @pool_data[:length] is less than desired pool size and
|
132
|
+
the pool is empty?"do
|
133
|
+
@pool.instance_variable_set(:@pool,[])
|
134
|
+
@pool.send(:add?).should be_true
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should be false pool has reached pool_size" do
|
138
|
+
@pool.instance_variable_set(:@pool_data,{:current_size => 5})
|
139
|
+
@pool.instance_variable_set(:@pool,["connection","connection","connection","connection","connection"])
|
140
|
+
@pool.send(:add?).should be_false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#add' do
|
145
|
+
it "should add connections for supplied url"do
|
146
|
+
@pool.send(:add)
|
147
|
+
@pool.instance_variable_get(:@pool).should_not be_nil
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'thread safety' do
|
153
|
+
it "should work" do
|
154
|
+
url = "https://www.google.com/"
|
155
|
+
pool = HotTub::Pool.new({:size => 20})
|
156
|
+
failed = false
|
157
|
+
lambda {
|
158
|
+
threads = []
|
159
|
+
20.times.each do
|
160
|
+
threads << Thread.new do
|
161
|
+
pool.run{|connection| failed = true unless connection.head(url).status == 200}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
sleep(0.01)
|
165
|
+
threads.each do |t|
|
166
|
+
t.join
|
167
|
+
end
|
168
|
+
}.should_not raise_error
|
169
|
+
pool.instance_variable_get(:@pool).length.should eql(20) # make sure work got done
|
170
|
+
failed.should be_false # Make sure our requests woked
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "other http client" do
|
175
|
+
before(:each) do
|
176
|
+
@url = "https://www.google.com"
|
177
|
+
@pool = HotTub::Pool.new(:clean => lambda {|clnt| clnt.clean}) {MocClient.new(@url)}
|
178
|
+
end
|
179
|
+
|
180
|
+
it "should clean connections" do
|
181
|
+
@pool.run do |clnt|
|
182
|
+
clnt.cleaned?.should be_true
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
unless HotTub.jruby?
|
188
|
+
context 'EM:HTTPRequest' do
|
189
|
+
before(:each) do
|
190
|
+
@url = "https://www.google.com"
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should work" do
|
194
|
+
EM.synchrony do
|
195
|
+
status = []
|
196
|
+
c = HotTub::Pool.new {EM::HttpRequest.new(@url)}
|
197
|
+
c.run { |conn| status << conn.head(:keepalive => true).response_header.status}
|
198
|
+
c.run { |conn| status << conn.ahead(:keepalive => true).response_header.status}
|
199
|
+
c.run { |conn| status << conn.head(:keepalive => true).response_header.status}
|
200
|
+
status.should eql([200,0,200])
|
201
|
+
EM.stop
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'fibers' do
|
206
|
+
it "should work" do
|
207
|
+
EM.synchrony do
|
208
|
+
url = "https://www.google.com/"
|
209
|
+
pool = HotTub::Pool.new({:size => 5}) {EM::HttpRequest.new(@url)}
|
210
|
+
failed = false
|
211
|
+
fibers = []
|
212
|
+
lambda {
|
213
|
+
10.times.each do
|
214
|
+
fibers << Fiber.new do
|
215
|
+
pool.run{|connection| failed = true unless connection.head(:keepalive => true).response_header.status == 200}
|
216
|
+
end
|
217
|
+
end
|
218
|
+
fibers.each do |f|
|
219
|
+
f.resume
|
220
|
+
end
|
221
|
+
loop do
|
222
|
+
done = true
|
223
|
+
fibers.each do |f|
|
224
|
+
done = false if f.alive?
|
225
|
+
end
|
226
|
+
if done
|
227
|
+
break
|
228
|
+
else
|
229
|
+
EM::Synchrony.sleep(0.01)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
}.should_not raise_error
|
233
|
+
pool.instance_variable_get(:@pool).length.should eql(5) #make sure work got done
|
234
|
+
failed.should be_false # Make sure our requests worked
|
235
|
+
EM.stop
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
data/spec/session_spec.rb
CHANGED
@@ -1,108 +1,97 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
|
3
|
-
|
4
|
-
end
|
5
|
-
|
6
|
-
def get
|
7
|
-
sleep(0.05)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
2
|
+
require 'hot_tub/session'
|
3
|
+
require 'uri'
|
11
4
|
describe HotTub::Session do
|
12
5
|
|
13
|
-
|
14
|
-
|
15
|
-
@tub = HotTub::Session.new(MocClient.new(@url))
|
6
|
+
it "should raise error if block is not supplied" do
|
7
|
+
lambda {HotTub::Session.new}.should raise_error(ArgumentError)
|
16
8
|
end
|
17
9
|
|
18
|
-
context 'default
|
19
|
-
|
20
|
-
|
21
|
-
@tub.
|
10
|
+
context 'default settings' do
|
11
|
+
before(:each) do
|
12
|
+
@url = "http://www.testurl123.com/"
|
13
|
+
@tub = HotTub::Session.new { |url| MocClient.new(url) }
|
14
|
+
@options = @tub.instance_variable_get(:@options)
|
22
15
|
end
|
23
|
-
|
24
|
-
it "should have
|
25
|
-
@
|
16
|
+
|
17
|
+
it "should have :size of 5" do
|
18
|
+
@options[:size].should eql(5)
|
26
19
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
it "should be true if @pool_data[:length] is less than desired pool size and
|
31
|
-
the pool is empty?"do
|
32
|
-
@tub.instance_variable_set(:@pool_data,{:current_size => 1})
|
33
|
-
@tub.send(:add_client?).should be_true
|
20
|
+
|
21
|
+
it "should have :blocking_timeout of 10 seconds" do
|
22
|
+
@options[:blocking_timeout].should eql(10)
|
34
23
|
end
|
35
|
-
|
36
|
-
it "should
|
37
|
-
@
|
38
|
-
@tub.instance_variable_set(:@pool,
|
39
|
-
["connection","connection","connection","connection","connection"])
|
40
|
-
@tub.send(:add_client?).should be_false
|
24
|
+
|
25
|
+
it "should default never_block to true" do
|
26
|
+
@options[:never_block].should be_true
|
41
27
|
end
|
42
28
|
end
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@
|
47
|
-
@tub.
|
29
|
+
|
30
|
+
context 'passed options' do
|
31
|
+
before(:each) do
|
32
|
+
@url = "http://www.testurl123.com/"
|
33
|
+
@tub = HotTub::Session.new({:size => 21, :never_block => false}) { |url| MocClient.new(url) }
|
34
|
+
@options = @tub.instance_variable_get(:@options)
|
48
35
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
it "should raise Timeout::Error if an available is not found in time"do
|
53
|
-
@tub.stub(:pool).and_return([])
|
54
|
-
lambda { @tub.send(:fetch)}.should raise_error(Timeout::Error)
|
36
|
+
|
37
|
+
it "should have @pool_size of 21" do
|
38
|
+
@options[:size].should eql(21)
|
55
39
|
end
|
56
|
-
|
57
|
-
it "should
|
58
|
-
@
|
59
|
-
|
60
|
-
|
40
|
+
|
41
|
+
it "should have never_block be false" do
|
42
|
+
@options[:never_block].should be_false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#sessions' do
|
47
|
+
before(:each) do
|
48
|
+
@url = "https://www.google.com"
|
49
|
+
@uri = URI(@url)
|
50
|
+
@tub = HotTub::Session.new({:size => 21, :never_block => false}) { |url| MocClient.new(url) }
|
61
51
|
end
|
62
52
|
|
63
|
-
it "should
|
64
|
-
@tub.
|
53
|
+
it "should add a new pool for the url" do
|
54
|
+
@tub.sessions(@url)
|
55
|
+
sessions = @tub.instance_variable_get(:@sessions)
|
56
|
+
sessions.length.should eql(1)
|
57
|
+
sessions.first[1].should be_a(HotTub::Pool)
|
65
58
|
end
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
@tub.run do |connection|
|
73
|
-
@fetched = connection.class.name
|
59
|
+
|
60
|
+
context "passed URL string" do
|
61
|
+
it "should set key with URI scheme-domain" do
|
62
|
+
@tub.sessions(@url)
|
63
|
+
sessions = @tub.instance_variable_get(:@sessions)
|
64
|
+
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(HotTub::Pool)
|
74
65
|
end
|
75
|
-
@fetched.should eql("MocClient")
|
76
66
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
@
|
81
|
-
|
67
|
+
|
68
|
+
context "passed URI" do
|
69
|
+
it "should set key with URI scheme-domain" do
|
70
|
+
@tub.sessions(@uri)
|
71
|
+
sessions = @tub.instance_variable_get(:@sessions)
|
72
|
+
sessions["#{@uri.scheme}-#{@uri.host}"].should be_a(HotTub::Pool)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "invalid argument" do
|
77
|
+
it "should raise an ArgumentError" do
|
78
|
+
lambda { @tub.sessions(nil) }.should raise_error(ArgumentError)
|
79
|
+
|
80
|
+
end
|
81
|
+
it "should raise URI::InvalidURIError with bad url" do
|
82
|
+
lambda { @tub.sessions("bad url") }.should raise_error(URI::InvalidURIError)
|
82
83
|
end
|
83
|
-
@fetched.should be_false # not in pool because its doing work
|
84
|
-
|
85
|
-
# returned to pool after work was done
|
86
|
-
@tub.instance_variable_get(:@pool).include?(@connection).should be_true
|
87
84
|
end
|
88
85
|
end
|
89
|
-
|
90
|
-
context 'thread safety' do
|
86
|
+
describe '#run' do
|
91
87
|
it "should work" do
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
sleep(0.5)
|
100
|
-
|
101
|
-
threads.each do |t|
|
102
|
-
t.join
|
88
|
+
@url = "https://www.google.com"
|
89
|
+
@tub = HotTub::Session.new({:size => 21, :never_block => false}) { |url| HTTPClient.new }
|
90
|
+
status = 0
|
91
|
+
@tub.run(@url) do |conn|
|
92
|
+
status = conn.head(@url).status
|
103
93
|
end
|
104
|
-
|
105
|
-
@tub.instance_variable_get(:@pool).length.should eql(5)
|
94
|
+
status.should eql(200)
|
106
95
|
end
|
107
96
|
end
|
108
97
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -7,6 +7,34 @@ require 'logger'
|
|
7
7
|
# in ./support/ and its subdirectories.
|
8
8
|
#Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
9
|
HotTub.logger.level = Logger::ERROR
|
10
|
+
|
10
11
|
RSpec.configure do |config|
|
11
12
|
|
12
|
-
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MocClient
|
16
|
+
def initialize(url=nil,options={})
|
17
|
+
@close = false
|
18
|
+
@clean = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def get
|
22
|
+
sleep(0.05)
|
23
|
+
end
|
24
|
+
|
25
|
+
def close
|
26
|
+
@close = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def closed?
|
30
|
+
@close == true
|
31
|
+
end
|
32
|
+
|
33
|
+
def clean
|
34
|
+
@clean = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def cleaned?
|
38
|
+
@clean == true
|
39
|
+
end
|
40
|
+
end
|