hot_tub 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +20 -0
- data/README.md +30 -0
- data/Rakefile +1 -0
- data/http_hot_tub.gemspec +21 -0
- data/lib/hot_tub/clients/client.rb +41 -0
- data/lib/hot_tub/clients/em_synchrony_client.rb +45 -0
- data/lib/hot_tub/clients/excon_client.rb +27 -0
- data/lib/hot_tub/clients/http_client_client.rb +38 -0
- data/lib/hot_tub/session.rb +127 -0
- data/lib/hot_tub/version.rb +3 -0
- data/lib/hot_tub.rb +21 -0
- data/spec/em_synchrony_client_spec.rb +47 -0
- data/spec/excon_client_spec.rb +18 -0
- data/spec/http_client_client_spec.rb +16 -0
- data/spec/session_spec.rb +106 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test_helper_methods.rb +34 -0
- metadata +83 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in http_hot_tub.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
platform :ruby do
|
8
|
+
gem 'eventmachine'
|
9
|
+
gem 'em-http-request', '~> 1.0', :require => 'em-http'
|
10
|
+
gem 'em-synchrony', '~> 1.0', :require => ['em-synchrony', 'em-synchrony/em-http']
|
11
|
+
gem "excon"
|
12
|
+
end
|
13
|
+
platform :jruby do
|
14
|
+
gem 'jruby-openssl'
|
15
|
+
gem 'jruby-httpclient'
|
16
|
+
end
|
17
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Joshua T. Mckinney
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# HotTub
|
2
|
+
A simple thread-safe pooling gem to use with your preferred http library that support
|
3
|
+
keep-alive.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
HotTub is available through [Rubygems](https://rubygems.org/gems/hot_tub) and can be installed via:
|
8
|
+
|
9
|
+
$ gem install hot_tub
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
class MyClass
|
13
|
+
@@pool = HotTub::Session.new({:size => 2 :client => HotTub::Client::EmSynchronyClient.new('https://google.com'), :never_block => true})
|
14
|
+
|
15
|
+
def self.fetch_results(query)
|
16
|
+
@@pool.get(:query => query) # keepalive has be defaulted to true in the client
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
MyClass.fetch_results({:foo => "goo"})
|
21
|
+
|
22
|
+
## Contributing to HotTub
|
23
|
+
|
24
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
25
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
26
|
+
* Fork the project
|
27
|
+
* Start a feature/bugfix branch
|
28
|
+
* Commit and push until you are happy with your contribution
|
29
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
30
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "hot_tub/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "hot_tub"
|
7
|
+
s.version = HotTub::VERSION
|
8
|
+
s.authors = ["Joshua Mckinney"]
|
9
|
+
s.email = ["joshmckin@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/JoshMcKin/hot_tub"
|
11
|
+
s.summary = %q{A very simple ruby pool gem}
|
12
|
+
s.description = %q{A very simple ruby pool gem}
|
13
|
+
|
14
|
+
s.rubyforge_project = "hot_tub"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.add_development_dependency "rspec"
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "hot_tub"
|
2
|
+
require 'thread'
|
3
|
+
module HotTub
|
4
|
+
# Super class for all HotTub clients
|
5
|
+
# provides the 4 required methods to ensure compatibility
|
6
|
+
class Client
|
7
|
+
|
8
|
+
def client
|
9
|
+
@client
|
10
|
+
end
|
11
|
+
|
12
|
+
def method_missing(method, *args, &blk)
|
13
|
+
@client.send(method,*args,&blk)
|
14
|
+
end
|
15
|
+
|
16
|
+
def temporary?
|
17
|
+
@temporary == true
|
18
|
+
end
|
19
|
+
|
20
|
+
def mark_temporary
|
21
|
+
@temporary = true
|
22
|
+
end
|
23
|
+
|
24
|
+
# Override this method to perform the necessary action for ensure a client
|
25
|
+
# is clean for use.
|
26
|
+
def sanitize_hot_tub_client
|
27
|
+
@client
|
28
|
+
end
|
29
|
+
|
30
|
+
def close_hot_tub_client
|
31
|
+
@client
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def mutex
|
36
|
+
Mutex.new
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "hot_tub"
|
2
|
+
require "hot_tub/clients/client"
|
3
|
+
module HotTub
|
4
|
+
class EmSynchronyClient < HotTub::Client
|
5
|
+
|
6
|
+
def initialize(url,options={})
|
7
|
+
@url = url
|
8
|
+
@options = {:inactivity_timeout => 0}.merge(options)
|
9
|
+
@client = EM::HttpRequest.new(url,options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def sanitize_hot_tub_client
|
13
|
+
if @client.conn && @client.conn.error?
|
14
|
+
HotTub.logger.info "Sanitizing connection : #{EventMachine::report_connection_error_status(@client.conn.instance_variable_get(:@signature))}"
|
15
|
+
@client.conn.close_connection
|
16
|
+
@client.instance_variable_set(:@deferred, true)
|
17
|
+
end
|
18
|
+
@client
|
19
|
+
end
|
20
|
+
|
21
|
+
def close_hot_tub_client
|
22
|
+
@client.conn.close_connection if @client.conn
|
23
|
+
end
|
24
|
+
|
25
|
+
# Default keepalive true for HTTP requests
|
26
|
+
[:get,:head,:delete,:put,:post].each do |m|
|
27
|
+
define_method m do |options={},&blk|
|
28
|
+
options ={} if options.nil?
|
29
|
+
options[:keepalive] = true
|
30
|
+
@client.send(m,options,&blk)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dup
|
35
|
+
self.class.new(@url,@options)
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
# Use a fiber safe mutex
|
40
|
+
def mutex
|
41
|
+
EM::Synchrony::Thread::Mutex.new
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "hot_tub"
|
2
|
+
require "hot_tub/clients/client"
|
3
|
+
require "excon"
|
4
|
+
module HotTub
|
5
|
+
class ExconClient < HotTub::Client
|
6
|
+
|
7
|
+
def initialize(url,options={})
|
8
|
+
@url = url
|
9
|
+
#make sure we turn on keep-alive
|
10
|
+
@options = {:headers => {"Connection" => "keep-alive"}}.merge(options)
|
11
|
+
@client = Excon.new(url,options)
|
12
|
+
end
|
13
|
+
|
14
|
+
# pretty sure Excon handles this internally
|
15
|
+
def sanitize_hot_tub_client
|
16
|
+
@client
|
17
|
+
end
|
18
|
+
|
19
|
+
def close_hot_tub_client
|
20
|
+
@client.socket.close
|
21
|
+
end
|
22
|
+
|
23
|
+
def dup
|
24
|
+
self.class.new(@url,@options)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "hot_tub"
|
2
|
+
require "hot_tub/clients/client"
|
3
|
+
require 'uri'
|
4
|
+
require 'http_client'
|
5
|
+
module HotTub
|
6
|
+
|
7
|
+
class HttpClientClient < HotTub::Client
|
8
|
+
|
9
|
+
def initialize(options={})
|
10
|
+
options[:default_host] = options[:url] if (options[:url] && options[:default_host].nil?)
|
11
|
+
@options = options
|
12
|
+
@client = HTTP::Client.new(options)
|
13
|
+
end
|
14
|
+
|
15
|
+
# pretty sure HttpClient handles this internally
|
16
|
+
def sanitize_hot_tub_client
|
17
|
+
@client
|
18
|
+
end
|
19
|
+
|
20
|
+
def close_hot_tub_client
|
21
|
+
@client.shutdown
|
22
|
+
end
|
23
|
+
|
24
|
+
def dup
|
25
|
+
self.class.new(@options)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module HTTP
|
31
|
+
class Client
|
32
|
+
def read_response(request)
|
33
|
+
r = execute(request)
|
34
|
+
r.body
|
35
|
+
r
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module HotTub
|
2
|
+
class Session
|
3
|
+
AVAILABLE_CLIENTS = ["NetHttpClient","EmSynchronyClient"]
|
4
|
+
|
5
|
+
# OPTIONS
|
6
|
+
# * :size - number of connections for each pool
|
7
|
+
# * :inactivity_timeout - number of seconds to wait before disconnecting,
|
8
|
+
# setting to 0 means the connection will not be closed
|
9
|
+
# * :pool_timeout - the amount of seconds to block waiting for an availble connection,
|
10
|
+
# because this is blocking it should be an extremely short amount of
|
11
|
+
# time default to 0.5 seconds, if you need more consider enlarging your pool
|
12
|
+
# instead of raising this number
|
13
|
+
# :never_block - if set to true, a connection will always be returned, but
|
14
|
+
# these extra connections are not added to the pool when the request is completed
|
15
|
+
def initialize(options={})
|
16
|
+
@options = {
|
17
|
+
:size => 5,
|
18
|
+
:never_block => false,
|
19
|
+
:blocking_timeout => 0.5,
|
20
|
+
:client_options => {}
|
21
|
+
}.merge(options)
|
22
|
+
@pool = []
|
23
|
+
@pool_data = {:current_size => 0}
|
24
|
+
@client = @options[:client]
|
25
|
+
@mutex = (@client.respond_to?(:mutex) ? @client.mutex : Client.mutex)
|
26
|
+
end
|
27
|
+
|
28
|
+
def pool
|
29
|
+
@mutex.synchronize do
|
30
|
+
add if add?
|
31
|
+
@pool
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Fetches an avaible client from the pool.
|
36
|
+
# Hot tubs are not always clean... Make sure we have a good connection. The client
|
37
|
+
# should respond to sanitize_hot_tub_connection, which checks to make sure
|
38
|
+
# the connection is still viable, and resets if not
|
39
|
+
def fetch
|
40
|
+
client = nil
|
41
|
+
alarm = (Time.now + @options[:blocking_timeout])
|
42
|
+
# block until we get an available connection or Timeout::Error
|
43
|
+
while client.nil?
|
44
|
+
raise_alarm if alarm <= Time.now
|
45
|
+
|
46
|
+
client = pool.shift
|
47
|
+
if client.nil? && (@options[:never_block])
|
48
|
+
HotTub.logger.info "Adding never_block client for #{@client.class.name}, will not be returned to pool."
|
49
|
+
client = new_client
|
50
|
+
client.mark_temporary
|
51
|
+
end
|
52
|
+
end
|
53
|
+
client.sanitize_hot_tub_client
|
54
|
+
client
|
55
|
+
end
|
56
|
+
|
57
|
+
# return a client to the pool
|
58
|
+
def return_client(client)
|
59
|
+
if client.temporary?
|
60
|
+
client.close_hot_tub_client # Too hot in the hot tub...
|
61
|
+
else
|
62
|
+
@pool << client
|
63
|
+
end
|
64
|
+
@pool
|
65
|
+
end
|
66
|
+
|
67
|
+
# Run the block on the retrieved connection. Good for ensure the same client
|
68
|
+
# is used for mulitple requests. For HTTP requests make sure you request has
|
69
|
+
# keep-alive properly set for your client
|
70
|
+
# EX:
|
71
|
+
# @pool = HotTub.new("https://some_web_site.com")
|
72
|
+
# results = []
|
73
|
+
# @pool.run do |client|
|
74
|
+
# results.push (client.get(:query => {:foo => "bar"}, :keepalive => true))
|
75
|
+
# results.push (client.get(:query => {:bar => "foo"}, :keepalive => true)) # reuse client
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def run(&block)
|
79
|
+
client = fetch
|
80
|
+
if block_given?
|
81
|
+
block.call(client)
|
82
|
+
else
|
83
|
+
raise ArgumentError, 'Run requires a block.'
|
84
|
+
end
|
85
|
+
ensure
|
86
|
+
return_client(client) if client
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# Let pool instance respond to client methods. For HTTP request make sure you
|
91
|
+
# requests has keep-alive properly set for your client
|
92
|
+
# EX:
|
93
|
+
# @pool = HotTub.new("https://some_web_site.com")
|
94
|
+
# r1 = @pool.get(:query => {:foo => "bar"}, :keepalive => true)
|
95
|
+
# r2 = @pool.get(:query => {:bar => "foo"}, :keepalive => true) # uses a different client
|
96
|
+
#
|
97
|
+
def method_missing(method, *args, &blk)
|
98
|
+
client = fetch
|
99
|
+
client.send(method,*args,&blk)
|
100
|
+
ensure
|
101
|
+
return_client(client) if client
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def add?
|
107
|
+
(@pool.empty? && (@pool_data[:current_size] < @options[:size]))
|
108
|
+
end
|
109
|
+
|
110
|
+
def add
|
111
|
+
HotTub.logger.info "Adding HotTub client: #{@client.class.name} to pool"
|
112
|
+
@pool_data[:current_size] += 1
|
113
|
+
@pool << new_client
|
114
|
+
@pool
|
115
|
+
end
|
116
|
+
|
117
|
+
def new_client
|
118
|
+
@client.dup
|
119
|
+
end
|
120
|
+
|
121
|
+
def raise_alarm
|
122
|
+
message = "Could not fetch a free client in time. Consider increasing your pool size for #{@client.class.name}."
|
123
|
+
HotTub.logger.error message
|
124
|
+
raise Timeout::Error, message
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/hot_tub.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'timeout'
|
3
|
+
require 'logger'
|
4
|
+
require "hot_tub/version"
|
5
|
+
require "hot_tub/session"
|
6
|
+
require "hot_tub/clients/client"
|
7
|
+
require "hot_tub/clients/em_synchrony_client"
|
8
|
+
require "hot_tub/clients/excon_client"
|
9
|
+
require "hot_tub/clients/http_client_client" if RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
10
|
+
|
11
|
+
module HotTub
|
12
|
+
@@logger = Logger.new(STDOUT)
|
13
|
+
def self.logger
|
14
|
+
@@logger
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.logger=logger
|
18
|
+
@@logger = logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
unless RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
2
|
+
require "em-synchrony"
|
3
|
+
require "em-synchrony/em-http"
|
4
|
+
require "em-synchrony/fiber_iterator"
|
5
|
+
require "hot_tub/clients/em_synchrony_client"
|
6
|
+
require 'test_helper_methods'
|
7
|
+
include TestHelperMethods
|
8
|
+
|
9
|
+
describe HotTub::EmSynchronyClient do
|
10
|
+
before(:each) do
|
11
|
+
@url = "https://www.google.com"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Keep alive should work" do
|
15
|
+
EM.synchrony do
|
16
|
+
keep_alive_test HotTub::EmSynchronyClient.new(@url) do |connection|
|
17
|
+
connection.get.response_header.status
|
18
|
+
end
|
19
|
+
EM.stop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
# context 'integration test with parallel requests' do
|
23
|
+
# # 10 parallel requests
|
24
|
+
# it "should work" do
|
25
|
+
# @connection_pool = HotTub::Session.new(:client_options =>
|
26
|
+
# {:url => "https://www.google.com"},
|
27
|
+
# :never_block => true,
|
28
|
+
# :client => HotTub::EmSynchronyClient)
|
29
|
+
# EM.synchrony do
|
30
|
+
# concurrency = 10
|
31
|
+
# options = (0..19).to_a
|
32
|
+
# results = []
|
33
|
+
#
|
34
|
+
# EM::Synchrony::FiberIterator.new(options, concurrency).each do |count|
|
35
|
+
# resp = @connection_pool.get
|
36
|
+
# results.push resp.response_header.status
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# results.length.should eql(20)
|
40
|
+
# results.include?(200).should be_true
|
41
|
+
# @connection_pool.instance_variable_get(:@pool).length.should eql(@connection_pool.instance_variable_get(:@options)[:size])
|
42
|
+
# EM.stop
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
unless (RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE))
|
2
|
+
require "hot_tub/clients/excon_client"
|
3
|
+
require 'test_helper_methods'
|
4
|
+
require 'excon'
|
5
|
+
include TestHelperMethods
|
6
|
+
describe HotTub::ExconClient do
|
7
|
+
before(:each) do
|
8
|
+
@url = "https://www.google.com"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "Keep alive should work" do
|
12
|
+
keep_alive_test HotTub::ExconClient.new(@url) do |client|
|
13
|
+
client.get.status
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
if RUBY_VERSION < '1.9' or (defined? RUBY_ENGINE and 'jruby' == RUBY_ENGINE)
|
2
|
+
require "hot_tub/clients/http_client_client"
|
3
|
+
require 'test_helper_methods'
|
4
|
+
include TestHelperMethods
|
5
|
+
describe HotTub::HttpClientClient do
|
6
|
+
before(:each) do
|
7
|
+
@url = "https://www.google.com"
|
8
|
+
end
|
9
|
+
it "Keep alive should work" do
|
10
|
+
keep_alive_test HotTub::HttpClientClient.new(:url => @url) do |client|
|
11
|
+
c = client.get('')
|
12
|
+
c.status_code
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
class MocClient < HotTub::Client
|
3
|
+
def initialize(url,options={})
|
4
|
+
end
|
5
|
+
|
6
|
+
def get
|
7
|
+
sleep(0.05)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe HotTub::Session do
|
12
|
+
before(:each) do
|
13
|
+
@url = "http://www.testurl123.com/"
|
14
|
+
@tub = HotTub::Session.new(:client => MocClient.new(@url))
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'default configuration' do
|
18
|
+
it "should have @pool_size of 5" do
|
19
|
+
@tub.instance_variable_get(:@options)[:size].should eql(5)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have @pool_timeout of 0" do
|
23
|
+
@tub.instance_variable_get(:@options)[:blocking_timeout].should eql(0.5)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#add_connection?' do
|
28
|
+
it "should be true if @pool_data[:length] is less than desired pool size and
|
29
|
+
the pool is empty?"do
|
30
|
+
@tub.instance_variable_set(:@pool_data,{:current_size => 1})
|
31
|
+
@tub.send(:add?).should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be false pool has reached pool_size" do
|
35
|
+
@tub.instance_variable_set(:@pool_data,{:current_size => 5})
|
36
|
+
@tub.instance_variable_set(:@pool,
|
37
|
+
["connection","connection","connection","connection","connection"])
|
38
|
+
@tub.send(:add?).should be_false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#add_connection' do
|
43
|
+
it "should add connections for supplied url"do
|
44
|
+
@tub.send(:add)
|
45
|
+
@tub.instance_variable_get(:@pool).should_not be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#fetch_connection' do
|
50
|
+
it "should raise Timeout::Error if an available is not found in time"do
|
51
|
+
@tub.stub(:pool).and_return([])
|
52
|
+
lambda { @tub.fetch}.should raise_error(Timeout::Error)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should not raise Timeout::Error if an available is not found in time"do
|
56
|
+
@tub.instance_variable_get(:@options)[:never_block] = true
|
57
|
+
@tub.stub(:pool).and_return([])
|
58
|
+
lambda { @tub.fetch}.should_not raise_error(Timeout::Error)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should return an instance of the driver" do
|
62
|
+
@tub.fetch.should be_instance_of(MocClient)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#run' do
|
67
|
+
it "should fetch a connection and run the supplied block" do
|
68
|
+
@fetched = nil
|
69
|
+
|
70
|
+
@tub.run do |connection|
|
71
|
+
@fetched = connection.class.name
|
72
|
+
end
|
73
|
+
@fetched.should eql("MocClient")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should return the connection after use" do
|
77
|
+
@tub.run do |connection|
|
78
|
+
@connection = connection
|
79
|
+
@fetched = @tub.instance_variable_get(:@pool).include?(connection)
|
80
|
+
end
|
81
|
+
@fetched.should be_false # not in pool because its doing work
|
82
|
+
|
83
|
+
# returned to pool after work was done
|
84
|
+
@tub.instance_variable_get(:@pool).include?(@connection).should be_true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'thread safety' do
|
89
|
+
it "should work" do
|
90
|
+
threads = []
|
91
|
+
20.times.each do
|
92
|
+
threads << Thread.new do
|
93
|
+
@tub.run{|connection| connection.get}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
sleep(0.5)
|
98
|
+
|
99
|
+
threads.each do |t|
|
100
|
+
t.join
|
101
|
+
end
|
102
|
+
|
103
|
+
@tub.instance_variable_get(:@pool).length.should eql(5)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'hot_tub'
|
2
|
+
require 'rspec'
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
#Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
HotTub.logger.level = Logger::ERROR
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module TestHelperMethods
|
2
|
+
def keep_alive_test(client,&get_status_blk)
|
3
|
+
responses = []
|
4
|
+
start = Time.now
|
5
|
+
|
6
|
+
50.times.each do
|
7
|
+
c = client.dup # want a new client everytime
|
8
|
+
responses.push get_status_blk.call(c)
|
9
|
+
end
|
10
|
+
|
11
|
+
normal = Time.now - start
|
12
|
+
|
13
|
+
# we want a whole new pool to make sure we don't cheat
|
14
|
+
connection_pool = HotTub::Session.new(
|
15
|
+
:client => client)
|
16
|
+
|
17
|
+
start = Time.now
|
18
|
+
50.times.each do
|
19
|
+
#responses.push get_status_blk.call(connection_pool.get)
|
20
|
+
connection_pool.run do |c|
|
21
|
+
responses.push get_status_blk.call(c)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
keep_alive = Time.now - start
|
27
|
+
|
28
|
+
|
29
|
+
puts "#{client.class.name} keep-alive: #{keep_alive}; normal: #{normal};"
|
30
|
+
#puts responses.join(", ")
|
31
|
+
(keep_alive < normal).should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hot_tub
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joshua Mckinney
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-16 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
version_requirements: &2056 !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
none: false
|
22
|
+
requirement: *2056
|
23
|
+
prerelease: false
|
24
|
+
type: :development
|
25
|
+
description: A very simple ruby pool gem
|
26
|
+
email:
|
27
|
+
- joshmckin@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- .rspec
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE.txt
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- http_hot_tub.gemspec
|
39
|
+
- lib/hot_tub.rb
|
40
|
+
- lib/hot_tub/clients/client.rb
|
41
|
+
- lib/hot_tub/clients/em_synchrony_client.rb
|
42
|
+
- lib/hot_tub/clients/excon_client.rb
|
43
|
+
- lib/hot_tub/clients/http_client_client.rb
|
44
|
+
- lib/hot_tub/session.rb
|
45
|
+
- lib/hot_tub/version.rb
|
46
|
+
- spec/em_synchrony_client_spec.rb
|
47
|
+
- spec/excon_client_spec.rb
|
48
|
+
- spec/http_client_client_spec.rb
|
49
|
+
- spec/session_spec.rb
|
50
|
+
- spec/spec_helper.rb
|
51
|
+
- spec/test_helper_methods.rb
|
52
|
+
homepage: https://github.com/JoshMcKin/hot_tub
|
53
|
+
licenses: []
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
none: false
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
none: false
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project: hot_tub
|
72
|
+
rubygems_version: 1.8.15
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: A very simple ruby pool gem
|
76
|
+
test_files:
|
77
|
+
- spec/em_synchrony_client_spec.rb
|
78
|
+
- spec/excon_client_spec.rb
|
79
|
+
- spec/http_client_client_spec.rb
|
80
|
+
- spec/session_spec.rb
|
81
|
+
- spec/spec_helper.rb
|
82
|
+
- spec/test_helper_methods.rb
|
83
|
+
...
|