hot_tub 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,4 +3,4 @@
3
3
  Gemfile.lock
4
4
  pkg/*
5
5
  .DS_Store
6
- nbproject/
6
+ coverage/
data/.travis.yml CHANGED
@@ -9,7 +9,5 @@ rvm:
9
9
  - jruby-head
10
10
  matrix:
11
11
  allow_failures:
12
- - rvm: jruby-19mode
13
- - rvm: rbx-19mode
14
12
  - rvm: jruby-head
15
13
  - rvm: ruby-head
data/Gemfile CHANGED
@@ -3,9 +3,10 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in http_hot_tub.gemspec
4
4
  gemspec
5
5
  gem 'rake'
6
- gem 'coveralls', require: false
6
+
7
7
  group :development do
8
8
  platform :ruby do
9
+ gem 'coveralls', :require => false
9
10
  gem 'eventmachine'
10
11
  gem 'em-http-request', '~> 1.0', :require => 'em-http'
11
12
  gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
data/HISTORY.md ADDED
@@ -0,0 +1,9 @@
1
+ HotTub Changelog
2
+ =====================
3
+
4
+ HEAD
5
+ =======
6
+
7
+ - Rename unsafe methods with leading "_"
8
+ - Add register to pool to track all connections
9
+ - EM.add_shutdown_hook for EM connections
data/README.md CHANGED
@@ -6,18 +6,22 @@ A simple thread-safe connection pool and sessions gem. Out-of-the-box support fo
6
6
  ## Features
7
7
 
8
8
  ### HotTub::Pool
9
- * Thread safe
9
+ * Thread safe / Fiber safe (with EM::HttpRequest + EM::Synchrony)
10
10
  * Lazy clients/connections (created only when necessary)
11
11
  * Can be used with any client library
12
12
  * Support for cleaning dirty resources
13
13
  * Set to expand pool under load that is eventually reaped back down to set size (never_block), can be disabled
14
- * Attempts to close clients/connections at_exit
14
+ * Attempts to close clients/connections on shutdown
15
15
 
16
16
  ### HotTub::Session
17
- * Thread safe
17
+ * Thread safe / Fiber safe (with EM::HttpRequest + EM::Synchrony)
18
18
  * The same api as HotTub::Pool
19
19
  * Can be used with HotTub::Pool or any client library
20
- * Attempts to close clients/connections at_exit
20
+ * Attempts to close clients/connections on shutdown
21
+
22
+ ## Requirements
23
+ HotTub is tested on MRI, JRUBY and Rubinius
24
+ * Ruby >= 1.9
21
25
 
22
26
  ## Installation
23
27
 
@@ -25,7 +29,7 @@ HotTub is available through [Rubygems](https://rubygems.org/gems/hot_tub) and ca
25
29
 
26
30
  $ gem install hot_tub
27
31
 
28
- ## Rails setup
32
+ ### Rails setup
29
33
 
30
34
  Add hot_tub to your gemfile:
31
35
 
@@ -49,19 +53,20 @@ Configure Logger by creating a hot_tub.rb initializer and adding the following:
49
53
  require 'em-synchrony/em-http'
50
54
  EM.synchrony do {
51
55
  pool = HotTub::Pool.new(:size => 12) { EM::HttpRequest.new("http://somewebservice.com") }
56
+ # Make sure we set :keepalive as true
52
57
  pool.run { |clnt| clnt.aget(:query => results, :keepalive => true) }
53
58
  EM.stop
54
59
  }
55
60
 
56
61
  ### Other
57
- You can use any library you want with HotTub::Pool, regardless of that clients thread-safety status.
58
- Close and clean can be defined at initialization with lambdas, if they are not defined they are ignored.
62
+ You can use any library you want with HotTub::Pool. Close and clean can be defined at initialization
63
+ with lambdas, if they are not defined they are ignored.
59
64
 
60
65
  url = "http://test12345.com"
61
66
  pool = HotTub::Pool.new({:size => 10, :close => lambda {|clnt| clnt.close}}) { MyHttpLib.new }
62
- pool.run { |clnt| clnt.get(@@url,query).body }
67
+ pool.run { |clnt| clnt.get(url,query).body }
63
68
 
64
- ## HotTub::Session Usage
69
+ ## HotTub::Session
65
70
  HotTub::Sessions are a synchronized hash of clients/pools and are implemented similar HotTub::Pool.
66
71
  For example, Excon is thread safe but you set a single url at the client level so sessions
67
72
  are handy if you need to access multiple urls but would prefer a single object.
@@ -82,22 +87,22 @@ are handy if you need to access multiple urls but would prefer a single object.
82
87
  end
83
88
 
84
89
  ### HotTub::Session with HotTub::Pool
85
- Suppose you have a client that is not thread safe, you can use HotTub::Pool with HotTub::Sessions to get what you need.
90
+ Suppose you have a client that lacks pooling and session features you can use HotTub::Pool with HotTub::Sessions to get what you need.
86
91
 
87
92
  require 'hot_tub'
88
93
  require "em-synchrony"
89
94
  require "em-synchrony/em-http"
90
95
 
91
- # We ust tell HotTub::Session to use HotTub::Pool, pass any pool options in our
96
+ # We must tell HotTub::Session to use HotTub::Pool, pass any pool options in our
92
97
  # options has, and our client block must accept the url argument
93
98
  EM.synchrony do {
94
99
  sessions = HotTub::Session.new(:with_pool => true, :size => 12) {|url| EM::HttpRequest.new(url, :inactivity_timeout => 0) }
95
100
 
96
101
  sessions.run("http://somewebservice.com") do |clnt|
97
- puts clnt.get(:query => results).response_header.status
102
+ puts clnt.get(:query => results, :keepalive => true).response_header.status
98
103
  end
99
104
  sessions.run("https://someotherwebservice.com") do |clnt|
100
- puts clnt.get(:query => results).response_header.status
105
+ puts clnt.get(:query => results, :keepalive => true).response_header.status
101
106
  end
102
107
  EM.stop
103
108
  }
data/hot_tub.gemspec CHANGED
@@ -10,13 +10,13 @@ Gem::Specification.new do |s|
10
10
  s.homepage = "https://github.com/JoshMcKin/hot_tub"
11
11
  s.license = "MIT"
12
12
  s.summary = %q{A simple thread-safe http connection pooling gem.}
13
- s.description = %q{A simple thread-safe http connection pooling gem. Http client options include HTTPClient and EM-Http-Request}
13
+ s.description = %q{A simple thread-safe http connection pooling gem. Out-of-the-box support for Excon and EM-Http-Request}
14
14
 
15
15
  s.rubyforge_project = "hot_tub"
16
16
 
17
17
  s.add_development_dependency "rspec"
18
18
  s.add_development_dependency "sinatra"
19
- s.add_development_dependency "puma", "~> 2.0.0.b7"
19
+ s.add_development_dependency "puma", "~> 2.0.0"
20
20
  s.add_development_dependency "excon"
21
21
 
22
22
  s.files = `git ls-files`.split("\n")
data/lib/hot_tub/pool.rb CHANGED
@@ -26,7 +26,6 @@ module HotTub
26
26
  #
27
27
  def initialize(options={},&client_block)
28
28
  raise ArgumentError, 'a block that initializes a new client is required' unless block_given?
29
- at_exit { close_all } # close connections at exit
30
29
  @client_block = client_block
31
30
  @options = {
32
31
  :size => 5,
@@ -35,11 +34,13 @@ module HotTub
35
34
  :close => nil, # => lambda {|clnt| clnt.close}
36
35
  :clean => nil # => lambda {|clnt| clnt.clean}
37
36
  }.merge(options)
38
- @pool = []
39
- @current_size = 0
40
- @mutex = (fiber_mutex? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
41
- @last_activity = Time.now
42
- @fetching_client = false
37
+ @pool = [] # stores available connection
38
+ @register = [] # stores all connections at all times
39
+ @current_size = 0
40
+ @pool_mutex = (em_client? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
41
+ @last_activity = Time.now
42
+ @fetching_client = false
43
+ HotTub.hot_at_exit( em_client? ) {close_all}
43
44
  end
44
45
 
45
46
  # Hand off to client.run
@@ -56,8 +57,9 @@ module HotTub
56
57
 
57
58
  # Calls close on all connections and reset the pools
58
59
  def close_all
59
- @mutex.synchronize do
60
- while clnt = @pool.pop
60
+ @pool_mutex.synchronize do
61
+ while clnt = @register.pop
62
+ @pool.delete(clnt)
61
63
  begin
62
64
  close_client(clnt)
63
65
  rescue => e
@@ -70,7 +72,7 @@ module HotTub
70
72
 
71
73
  private
72
74
 
73
- def fiber_mutex?
75
+ def em_client?
74
76
  begin
75
77
  (HotTub.em_synchrony? && @client_block.call.is_a?(EventMachine::HttpConnection))
76
78
  rescue
@@ -97,10 +99,15 @@ module HotTub
97
99
  raise BlockingTimeout, message
98
100
  end
99
101
 
100
- # Safely add client back to pool
102
+ # Safely add client back to pool, only if
103
+ # that clnt is registered
101
104
  def push(clnt)
102
- @mutex.synchronize do
103
- @pool << clnt
105
+ @pool_mutex.synchronize do
106
+ if @register.include?(clnt)
107
+ @pool << clnt
108
+ else
109
+ close_client(clnt)
110
+ end
104
111
  end
105
112
  nil # make sure never return the pool
106
113
  end
@@ -108,11 +115,11 @@ module HotTub
108
115
  # Safely pull client from pool, adding if allowed
109
116
  def pop
110
117
  @fetching_client = true # kill reap_pool
111
- @mutex.synchronize do
112
- add if add?
118
+ @pool_mutex.synchronize do
119
+ _add if add?
113
120
  clnt = @pool.pop # get warm connection
114
121
  if (clnt.nil? && @options[:never_block])
115
- add
122
+ _add
116
123
  clnt = @pool.pop
117
124
  end
118
125
  @fetching_client = false
@@ -133,27 +140,32 @@ module HotTub
133
140
  (@pool.length == 0 && (@options[:size] > @current_size))
134
141
  end
135
142
 
136
- def add
137
- @last_activity = Time.now
138
- @current_size += 1
139
- nc = new_client
140
- HotTub.logger.info "Adding HotTub client: #{nc.class.name} to pool"
141
- @pool << nc
142
- end
143
-
144
143
  def reap_pool?
145
144
  (!@fetching_client && (@current_size > @options[:size]) && ((@last_activity + (600)) < Time.now))
146
145
  end
147
146
 
148
147
  # Remove extra connections from front of pool
149
148
  def reap_pool
150
- @mutex.synchronize do
149
+ @pool_mutex.synchronize do
151
150
  if reap_pool? && clnt = @pool.shift
151
+ @register.delete(clnt)
152
152
  @current_size -= 1
153
153
  close_client(clnt)
154
154
  end
155
155
  end
156
156
  end
157
+
158
+ # _add is volatile; and may cause theading issues
159
+ # if called outside @pool_mutex.synchronize {}
160
+ def _add
161
+ @last_activity = Time.now
162
+ @current_size += 1
163
+ nc = new_client
164
+ HotTub.logger.info "Adding HotTub client: #{nc.class.name} to pool"
165
+ @register << nc
166
+ @pool << nc
167
+ end
168
+ # end volatile
157
169
  end
158
170
  class BlockingTimeout < StandardError;end
159
171
  end
@@ -33,11 +33,11 @@ module HotTub
33
33
  #
34
34
  def initialize(options={},&client_block)
35
35
  raise ArgumentError, "HotTub::Sessions requre a block on initialization that accepts a single argument" unless block_given?
36
- at_exit { close_all } # close connections at exit
37
36
  @options = options || {}
38
37
  @client_block = client_block
39
38
  @sessions = Hash.new
40
- @mutex = (fiber_mutex? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
39
+ @mutex = (em_client? ? EM::Synchrony::Thread::Mutex.new : Mutex.new)
40
+ HotTub.hot_at_exit( em_client? ) {close_all}
41
41
  end
42
42
 
43
43
  # Synchronizes initialization of our sessions
@@ -81,7 +81,7 @@ module HotTub
81
81
 
82
82
  private
83
83
 
84
- def fiber_mutex?
84
+ def em_client?
85
85
  begin
86
86
  (HotTub.em_synchrony? && @client_block.call("http://moc").is_a?(EventMachine::HttpConnection))
87
87
  rescue
@@ -1,3 +1,3 @@
1
1
  module HotTub
2
- VERSION = "0.2.5"
2
+ VERSION = "0.2.6"
3
3
  end
data/lib/hot_tub.rb CHANGED
@@ -30,4 +30,12 @@ module HotTub
30
30
  def self.rbx?
31
31
  defined?(RUBY_ENGINE) and RUBY_ENGINE == 'rbx'
32
32
  end
33
+
34
+ def self.hot_at_exit with_em=false, &blk
35
+ if with_em
36
+ EM.add_shutdown_hook &blk
37
+ else
38
+ at_exit &blk
39
+ end
40
+ end
33
41
  end
data/spec/pool_spec.rb CHANGED
@@ -82,9 +82,18 @@ describe HotTub::Pool do
82
82
  before(:each) do
83
83
  @pool = HotTub::Pool.new(:size => 5) { MocClient.new }
84
84
  5.times do
85
- @pool.send(:add)
85
+ @pool.send(:_add)
86
86
  end
87
87
  end
88
+
89
+ it "should reset register" do
90
+ @pool.current_size.should eql(5)
91
+ @pool.instance_variable_get(:@register).length.should eql(5)
92
+ @pool.close_all
93
+ @pool.instance_variable_get(:@register).length.should eql(0)
94
+ @pool.current_size.should eql(0)
95
+ end
96
+
88
97
  it "should reset pool" do
89
98
  @pool.current_size.should eql(5)
90
99
  @pool.instance_variable_get(:@pool).length.should eql(5)
@@ -126,10 +135,31 @@ describe HotTub::Pool do
126
135
  end
127
136
  end
128
137
 
129
- describe '#add' do
138
+ describe '#_add' do
130
139
  it "should add connections for supplied url"do
131
- @pool.send(:add)
132
- @pool.instance_variable_get(:@pool).should_not be_nil
140
+ pre_add_length = @pool.instance_variable_get(:@pool).length
141
+ @pool.send(:_add)
142
+ @pool.instance_variable_get(:@pool).length.should be > pre_add_length
143
+ end
144
+ end
145
+
146
+ describe '#push' do
147
+ context "connection is registered" do
148
+ it "should push connection back to pool" do
149
+ @pool.send(:_add)
150
+ clnt = @pool.instance_variable_get(:@pool).pop
151
+ @pool.send(:push,clnt)
152
+ @pool.instance_variable_get(:@pool).include?(clnt).should be_true
153
+ end
154
+ end
155
+ context "connection is not registered" do
156
+ it "should not push connection back to pool" do
157
+ @pool.send(:_add)
158
+ clnt = @pool.instance_variable_get(:@pool).pop
159
+ @pool.instance_variable_get(:@register).delete(clnt)
160
+ @pool.send(:push,clnt)
161
+ @pool.instance_variable_get(:@pool).include?(clnt).should be_false
162
+ end
133
163
  end
134
164
  end
135
165
  end
@@ -260,7 +290,7 @@ describe HotTub::Pool do
260
290
  end
261
291
 
262
292
  unless HotTub.jruby?
263
- describe "fiber_mutex?" do
293
+ describe "em_client?" do
264
294
  context 'EM::HttpRequest as client' do
265
295
  before(:each) do
266
296
  @pool = HotTub::Pool.new { EM::HttpRequest.new(HotTub::Server.url) }
@@ -268,20 +298,20 @@ describe HotTub::Pool do
268
298
  context "EM::Synchrony is present" do
269
299
  it "should be true" do
270
300
  HotTub.stub(:em_synchrony?).and_return(true)
271
- @pool.send(:fiber_mutex?).should be_true
301
+ @pool.send(:em_client?).should be_true
272
302
  end
273
303
  end
274
304
  context "EM::Synchrony is not present" do
275
305
  it "should be false" do
276
306
  HotTub.stub(:em_synchrony?).and_return(false)
277
- @pool.send(:fiber_mutex?).should be_false
307
+ @pool.send(:em_client?).should be_false
278
308
  end
279
309
  end
280
310
  end
281
311
  context 'client is not EM::HttpRequest' do
282
312
  it "should be false" do
283
313
  pool = HotTub::Pool.new {|url| MocClient.new}
284
- pool.send(:fiber_mutex?).should be_false
314
+ pool.send(:em_client?).should be_false
285
315
  end
286
316
  end
287
317
  end
@@ -294,7 +324,7 @@ describe HotTub::Pool do
294
324
  it "should work" do
295
325
  EM.synchrony do
296
326
  status = []
297
- c = HotTub::Pool.new(:fiber_mutex => true) {EM::HttpRequest.new(@url)}
327
+ c = HotTub::Pool.new {EM::HttpRequest.new(@url)}
298
328
  c.run { |conn| status << conn.head(:keepalive => true).response_header.status}
299
329
  c.run { |conn| status << conn.ahead(:keepalive => true).response_header.status}
300
330
  c.run { |conn| status << conn.head(:keepalive => true).response_header.status}
data/spec/session_spec.rb CHANGED
@@ -120,7 +120,6 @@ describe HotTub::Session do
120
120
  failed = false
121
121
  start_time = Time.now
122
122
  stop_time = nil
123
- mutex = Mutex.new
124
123
  threads = []
125
124
  lambda {
126
125
  10.times.each do
@@ -146,7 +145,7 @@ describe HotTub::Session do
146
145
 
147
146
  unless HotTub.jruby?
148
147
 
149
- describe "fiber_mutex?" do
148
+ describe "em_client?" do
150
149
 
151
150
  context 'EM::HttpRequest as client' do
152
151
  before(:each) do
@@ -155,20 +154,20 @@ describe HotTub::Session do
155
154
  context "EM::Synchrony is present" do
156
155
  it "should be true" do
157
156
  HotTub.stub(:em_synchrony?).and_return(true)
158
- @session.send(:fiber_mutex?).should be_true
157
+ @session.send(:em_client?).should be_true
159
158
  end
160
159
  end
161
160
  context "EM::Synchrony is not present" do
162
161
  it "should be false" do
163
162
  HotTub.stub(:em_synchrony?).and_return(false)
164
- @session.send(:fiber_mutex?).should be_false
163
+ @session.send(:em_client?).should be_false
165
164
  end
166
165
  end
167
166
  end
168
167
  context 'client is not EM::HttpRequest' do
169
168
  it "should be false" do
170
169
  session = HotTub::Session.new {|url| MocClient.new}
171
- session.send(:fiber_mutex?).should be_false
170
+ session.send(:em_client?).should be_false
172
171
  end
173
172
  end
174
173
  end
@@ -205,7 +204,6 @@ describe HotTub::Session do
205
204
  sessions.instance_variable_get(:@sessions).keys.length.should eql(1)
206
205
  (sessions.sessions(@url).instance_variable_get(:@pool).length >= 5).should be_true #make sure work got done
207
206
  failed.should be_false # Make sure our requests worked
208
- sessions.close_all
209
207
  EM.stop
210
208
  end
211
209
  end
data/spec/spec_helper.rb CHANGED
@@ -6,8 +6,10 @@ require 'excon'
6
6
  require 'helpers/moc_client'
7
7
  require 'helpers/server'
8
8
  require 'net/https'
9
- require 'coveralls'
10
- Coveralls.wear! unless HotTub.jruby? || HotTub.rbx?
9
+ unless HotTub.jruby? || HotTub.rbx?
10
+ require 'coveralls'
11
+ Coveralls.wear!
12
+ end
11
13
 
12
14
  # Requires supporting files with custom matchers and macros, etc,
13
15
  # in ./support/ and its subdirectories.
@@ -15,12 +17,12 @@ Coveralls.wear! unless HotTub.jruby? || HotTub.rbx?
15
17
  HotTub.logger.level = Logger::ERROR
16
18
 
17
19
  RSpec.configure do |config|
18
- config.before(:suite) do
19
- HotTub::Server.run
20
- HotTub::Server2.run
21
- end
22
- config.after(:suite) do
23
- HotTub::Server.teardown
24
- HotTub::Server2.teardown
25
- end
26
- end
20
+ config.before(:suite) do
21
+ HotTub::Server.run
22
+ HotTub::Server2.run
23
+ end
24
+ config.after(:suite) do
25
+ HotTub::Server.teardown
26
+ HotTub::Server2.teardown
27
+ end
28
+ 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.2.5
4
+ version: 0.2.6
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-04-08 00:00:00.000000000 Z
12
+ date: 2013-05-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 2.0.0.b7
53
+ version: 2.0.0
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,7 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 2.0.0.b7
61
+ version: 2.0.0
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: excon
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -75,8 +75,8 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
- description: A simple thread-safe http connection pooling gem. Http client options
79
- include HTTPClient and EM-Http-Request
78
+ description: A simple thread-safe http connection pooling gem. Out-of-the-box support
79
+ for Excon and EM-Http-Request
80
80
  email:
81
81
  - joshmckin@gmail.com
82
82
  executables: []
@@ -87,6 +87,7 @@ files:
87
87
  - .rspec
88
88
  - .travis.yml
89
89
  - Gemfile
90
+ - HISTORY.md
90
91
  - LICENSE.txt
91
92
  - README.md
92
93
  - Rakefile
@@ -116,7 +117,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
117
  version: '0'
117
118
  segments:
118
119
  - 0
119
- hash: -1839309947224613127
120
+ hash: -3131859420697003965
120
121
  required_rubygems_version: !ruby/object:Gem::Requirement
121
122
  none: false
122
123
  requirements:
@@ -125,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
126
  version: '0'
126
127
  segments:
127
128
  - 0
128
- hash: -1839309947224613127
129
+ hash: -3131859420697003965
129
130
  requirements: []
130
131
  rubyforge_project: hot_tub
131
132
  rubygems_version: 1.8.25