hot_tub 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -0
- data/Gemfile +1 -1
- data/README.md +3 -2
- data/Rakefile +4 -0
- data/{http_hot_tub.gemspec → hot_tub.gemspec} +0 -1
- data/lib/hot_tub/pool.rb +9 -12
- data/lib/hot_tub/session.rb +10 -8
- data/lib/hot_tub/version.rb +1 -1
- data/spec/pool_spec.rb +8 -9
- data/spec/session_spec.rb +26 -0
- metadata +10 -3
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -2,10 +2,10 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in http_hot_tub.gemspec
|
4
4
|
gemspec
|
5
|
+
gem 'rake'
|
5
6
|
|
6
7
|
group :development do
|
7
8
|
platform :ruby do
|
8
|
-
gem 'debugger'
|
9
9
|
gem 'eventmachine'
|
10
10
|
gem 'em-http-request', '~> 1.0', :require => 'em-http'
|
11
11
|
gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# HotTub
|
1
|
+
# HotTub [![Build Status](https://travis-ci.org/JoshMcKin/hot_tub.png?branch=master)](https://travis-ci.org/JoshMcKin/hot_tub)
|
2
2
|
A simple thread-safe connection pooling gem. Supports [HTTPClient](https://github.com/nahi/httpclient) (default) and
|
3
3
|
[EM-Http-Requests](https://github.com/igrigorik/em-http-request) via [EM-Synchrony](https://github.com/igrigorik/em-synchrony).
|
4
4
|
There are a couple Ruby connection pool libraries out there but HotTub differs from most in that its connections are lazy
|
5
|
-
(created only when necessary), accomidates libraries that do not clean their dirty connections automatically, and manages unexpected usage increases by opening new connections rather than just blocking or
|
5
|
+
(created only when necessary), accomidates libraries that do not clean their dirty connections automatically, and manages unexpected usage increases by opening new connections rather than just blocking or throwing exceptions (never_block), although never_block can be disabled.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -69,6 +69,7 @@ seperate pools for you various domains based on URI.
|
|
69
69
|
# Assuming EM is running
|
70
70
|
require 'hot_tub/clients/em_http_request_client'
|
71
71
|
class EMClass
|
72
|
+
# Our client block must accept the url argument
|
72
73
|
@@sessons = HotTub::Sessions.new {|url| HotTub::EmHttpRequestClient.new(url,{:connect_timeout => 5}) }
|
73
74
|
def async_post_results(query = {})
|
74
75
|
@@sessons.run("http://somewebservice.com") do |connection|
|
data/Rakefile
CHANGED
@@ -12,7 +12,6 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.summary = %q{A simple thread-safe http connection pooling gem.}
|
13
13
|
s.description = %q{A simple thread-safe http connection pooling gem. Http client options include HTTPClient and EM-Http-Request}
|
14
14
|
|
15
|
-
|
16
15
|
s.rubyforge_project = "hot_tub"
|
17
16
|
s.add_runtime_dependency "httpclient"
|
18
17
|
s.add_development_dependency "rspec"
|
data/lib/hot_tub/pool.rb
CHANGED
@@ -55,21 +55,19 @@ module HotTub
|
|
55
55
|
# end
|
56
56
|
#
|
57
57
|
def initialize(options={},&client_block)
|
58
|
+
at_exit { close_all } # close connections at exit
|
58
59
|
@client_block = (block_given? ? client_block : lambda { HTTPClient.new })
|
59
60
|
@options = {
|
60
61
|
:size => 5,
|
61
|
-
:never_block => true,
|
62
|
-
:blocking_timeout => 10,
|
63
|
-
:close => nil,
|
64
|
-
:clean => nil
|
62
|
+
:never_block => true, # Return new client if we run out
|
63
|
+
:blocking_timeout => 10, # in seconds
|
64
|
+
:close => nil, # => lambda {|clnt| clnt.close}
|
65
|
+
:clean => nil # => lambda {|clnt| clnt.clean}
|
65
66
|
}.merge(options)
|
66
67
|
@pool = []
|
67
68
|
@current_size = 0
|
68
69
|
@clients = []
|
69
70
|
@mutex = (HotTub.em? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
|
70
|
-
@blocking_timeout = @options[:blocking_timeout]
|
71
|
-
@never_block = @options[:never_block]
|
72
|
-
@size = @options[:size]
|
73
71
|
end
|
74
72
|
|
75
73
|
# Hand off to client.run
|
@@ -104,7 +102,7 @@ module HotTub
|
|
104
102
|
# Returns an instance of the client for this pool.
|
105
103
|
def client
|
106
104
|
clnt = nil
|
107
|
-
alarm = (Time.now + @blocking_timeout)
|
105
|
+
alarm = (Time.now + @options[:blocking_timeout])
|
108
106
|
# block until we get an available client or raise Timeout::Error
|
109
107
|
while clnt.nil?
|
110
108
|
raise_alarm if alarm <= Time.now
|
@@ -123,7 +121,6 @@ module HotTub
|
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
126
|
-
|
127
124
|
# Attempts to close the provided client, checking the options first for a close block
|
128
125
|
# then checking the known clients
|
129
126
|
def close_client(clnt)
|
@@ -142,7 +139,7 @@ module HotTub
|
|
142
139
|
# Safely add client back to pool
|
143
140
|
def push(clnt)
|
144
141
|
@mutex.synchronize do
|
145
|
-
if @pool.length < @size
|
142
|
+
if @pool.length < @options[:size]
|
146
143
|
@pool << clnt
|
147
144
|
else
|
148
145
|
@clients.delete(clnt)
|
@@ -157,7 +154,7 @@ module HotTub
|
|
157
154
|
@mutex.synchronize do
|
158
155
|
add if add?
|
159
156
|
clnt = @pool.pop
|
160
|
-
if (clnt.nil? && @never_block)
|
157
|
+
if (clnt.nil? && @options[:never_block])
|
161
158
|
HotTub.logger.info "Adding never_block client for #{@client.class.name}."
|
162
159
|
clnt = new_client
|
163
160
|
end
|
@@ -175,7 +172,7 @@ module HotTub
|
|
175
172
|
# Only want to add a client if the pool is empty in keeping with
|
176
173
|
# a lazy model.
|
177
174
|
def add?
|
178
|
-
(@pool.length == 0 && @current_size <= @size)
|
175
|
+
(@pool.length == 0 && @current_size <= @options[:size])
|
179
176
|
end
|
180
177
|
|
181
178
|
def add
|
data/lib/hot_tub/session.rb
CHANGED
@@ -49,15 +49,17 @@ module HotTub
|
|
49
49
|
# Synchronize access to our key hash
|
50
50
|
# expects a url string or URI
|
51
51
|
def sessions(url)
|
52
|
+
if url.is_a?(String)
|
53
|
+
uri = URI(url)
|
54
|
+
elsif url.is_a?(URI)
|
55
|
+
uri = url
|
56
|
+
else
|
57
|
+
raise ArgumentError, "you must pass a string or a URI object"
|
58
|
+
end
|
59
|
+
key = "#{uri.scheme}-#{uri.host}"
|
60
|
+
return @sessions[key] if @sessions[key]
|
52
61
|
@mutex.synchronize do
|
53
|
-
|
54
|
-
uri = URI(url)
|
55
|
-
elsif url.is_a?(URI)
|
56
|
-
uri = url
|
57
|
-
else
|
58
|
-
raise ArgumentError, "you must pass a string or a URI object"
|
59
|
-
end
|
60
|
-
@sessions["#{uri.scheme}-#{uri.host}"] ||= HotTub::Pool.new(@options) { @client_block.call(url) }
|
62
|
+
@sessions[key] ||= HotTub::Pool.new(@options) { @client_block.call(url) }
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
data/lib/hot_tub/version.rb
CHANGED
data/spec/pool_spec.rb
CHANGED
@@ -11,11 +11,11 @@ describe HotTub::Pool do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should have :size of 5" do
|
14
|
-
@pool.instance_variable_get(:@size
|
14
|
+
@pool.instance_variable_get(:@options)[:size].should eql(5)
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should have :blocking_timeout of 0.5" do
|
18
|
-
@pool.instance_variable_get(:@blocking_timeout
|
18
|
+
@pool.instance_variable_get(:@options)[:blocking_timeout].should eql(10)
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should have default :client" do
|
@@ -23,7 +23,7 @@ describe HotTub::Pool do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should be true" do
|
26
|
-
@pool.instance_variable_get(:@never_block
|
26
|
+
@pool.instance_variable_get(:@options)[:never_block].should be_true
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -33,11 +33,11 @@ describe HotTub::Pool do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it "should have :size of 5" do
|
36
|
-
@pool.instance_variable_get(:@size
|
36
|
+
@pool.instance_variable_get(:@options)[:size].should eql(10)
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should have :blocking_timeout of 0.5" do
|
40
|
-
@pool.instance_variable_get(:@blocking_timeout
|
40
|
+
@pool.instance_variable_get(:@options)[:blocking_timeout].should eql(1.0)
|
41
41
|
end
|
42
42
|
|
43
43
|
it "should have defult :client" do
|
@@ -45,7 +45,7 @@ describe HotTub::Pool do
|
|
45
45
|
end
|
46
46
|
|
47
47
|
it "should be true" do
|
48
|
-
@pool.instance_variable_get(:@never_block
|
48
|
+
@pool.instance_variable_get(:@options)[:never_block].should be_false
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -116,8 +116,7 @@ describe HotTub::Pool do
|
|
116
116
|
|
117
117
|
describe '#client' do
|
118
118
|
it "should raise HotTub::BlockingTimeout if an available is not found in time"do
|
119
|
-
@pool.instance_variable_set(:@never_block,
|
120
|
-
@pool.instance_variable_set(:@blocking_timeout,0.1)
|
119
|
+
@pool.instance_variable_set(:@options, {:never_block => false, :blocking_timeout => 0.1})
|
121
120
|
@pool.stub(:pop).and_return(nil)
|
122
121
|
lambda { puts @pool.send(:client) }.should raise_error(HotTub::BlockingTimeout)
|
123
122
|
end
|
@@ -135,7 +134,7 @@ describe HotTub::Pool do
|
|
135
134
|
end
|
136
135
|
|
137
136
|
it "should be false pool has reached pool_size" do
|
138
|
-
@pool.instance_variable_set(:@
|
137
|
+
@pool.instance_variable_set(:@options,{:size => 5})
|
139
138
|
@pool.instance_variable_set(:@pool,["connection","connection","connection","connection","connection"])
|
140
139
|
@pool.send(:add?).should be_false
|
141
140
|
end
|
data/spec/session_spec.rb
CHANGED
@@ -94,4 +94,30 @@ describe HotTub::Session do
|
|
94
94
|
status.should eql(200)
|
95
95
|
end
|
96
96
|
end
|
97
|
+
|
98
|
+
context 'thread safety' do
|
99
|
+
it "should work" do
|
100
|
+
url = "https://www.google.com/"
|
101
|
+
url2 = "https://www.yahoo.com/"
|
102
|
+
session = HotTub::Session.new({:size => 20}) { |a_url| HTTPClient.new}
|
103
|
+
failed = false
|
104
|
+
lambda {
|
105
|
+
threads = []
|
106
|
+
20.times.each do
|
107
|
+
threads << Thread.new do
|
108
|
+
session.run(url){|connection| failed = true unless connection.head(url).status == 200}
|
109
|
+
session.run(url2){|connection| failed = true unless connection.head(url).status == 200}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
sleep(0.01)
|
113
|
+
threads.each do |t|
|
114
|
+
t.join
|
115
|
+
end
|
116
|
+
}.should_not raise_error
|
117
|
+
session.instance_variable_get(:@sessions).keys.length.should eql(2) # make sure work got done
|
118
|
+
session.instance_variable_get(:@sessions).values.first.instance_variable_get(:@pool).length.should eql(20) # make sure work got done
|
119
|
+
session.instance_variable_get(:@sessions).values.last.instance_variable_get(:@pool).length.should eql(20) # make sure work got done
|
120
|
+
failed.should be_false # Make sure our requests woked
|
121
|
+
end
|
122
|
+
end
|
97
123
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot_tub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httpclient
|
@@ -53,11 +53,12 @@ extra_rdoc_files: []
|
|
53
53
|
files:
|
54
54
|
- .gitignore
|
55
55
|
- .rspec
|
56
|
+
- .travis.yml
|
56
57
|
- Gemfile
|
57
58
|
- LICENSE.txt
|
58
59
|
- README.md
|
59
60
|
- Rakefile
|
60
|
-
-
|
61
|
+
- hot_tub.gemspec
|
61
62
|
- lib/hot_tub.rb
|
62
63
|
- lib/hot_tub/pool.rb
|
63
64
|
- lib/hot_tub/session.rb
|
@@ -78,12 +79,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
79
|
- - ! '>='
|
79
80
|
- !ruby/object:Gem::Version
|
80
81
|
version: '0'
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
hash: 518626986870371684
|
81
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
86
|
none: false
|
83
87
|
requirements:
|
84
88
|
- - ! '>='
|
85
89
|
- !ruby/object:Gem::Version
|
86
90
|
version: '0'
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
hash: 518626986870371684
|
87
94
|
requirements: []
|
88
95
|
rubyforge_project: hot_tub
|
89
96
|
rubygems_version: 1.8.25
|