hot_tub 0.2.5 → 0.2.6

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/.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