cassilds-connection-pool 0.0.2

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 ADDED
@@ -0,0 +1,2 @@
1
+ pools*.gem
2
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) Michael Rykov
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
+ cassilds-connection-pool
2
+ -----
3
+ (forked from rykov/pools)
4
+
5
+ Our interest in on Cassandra connection pooling.
6
+
7
+ See Cassilds, and Cassilds-Model.
8
+
9
+ -----
10
+
11
+ Provides connection pooling for multiple services that use persistent connections.
12
+
13
+ Installation
14
+ ============
15
+
16
+ $ gem install cassilds-connection-pool
17
+
18
+
19
+ Notes
20
+ =====
21
+
22
+ We have copied ActiveSupport::Concern to our initializer directory in Rails config!!
23
+
24
+
25
+ Author
26
+ =====
27
+
28
+ Michael Rykov :: mrykov@gmail.com
29
+
30
+ Umanni :: contato@umanni.com.br
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ require 'rspec/core'
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default => :spec
6
+ task :test => :spec
7
+
8
+ task :noop do; end
9
+
10
+ desc "Run all specs in spec directory (excluding plugin specs)"
11
+ RSpec::Core::RakeTask.new(:spec => :noop)
12
+
13
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "pools/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cassilds-connection-pool"
7
+ s.version = Pools::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.date = Time.now.strftime('%Y-%m-%d')
10
+ s.summary = "Generalized connection pooling"
11
+ s.homepage = "http://github.com/umanni/cassilds-connection-pool"
12
+ s.email = ["mrykov@gmail", "contato@umanni.com.br"]
13
+ s.authors = [ "Michael Rykov", "Umanni" ]
14
+ s.has_rdoc = false
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
+
21
+ s.add_dependency 'activesupport', '>= 3.0.0', '< 3.2'
22
+
23
+ s.description = <<DESCRIPTION
24
+ Generalized connection pooling
25
+ DESCRIPTION
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'pools'
2
+ require 'cassandra'
3
+
4
+ class Cassandra
5
+ class Pooled
6
+ include ::Pools::Pooled
7
+
8
+ def initialize(*args)
9
+ @cassandra_args = args.dup
10
+ super
11
+ end
12
+
13
+ def __connection
14
+ Cassandra.new(*@cassandra_args)
15
+ end
16
+
17
+ def __disconnect(client)
18
+ client.disconnect! if client
19
+ end
20
+
21
+ preparation_methods :login!, :disable_node_auto_discovery!
22
+
23
+ connection_methods *(Cassandra.public_instance_methods(false) - [:login!, :disable_node_auto_discovery!])
24
+ # :add, :add_column_family, :add_keyspace, :auth_request,
25
+ # :batch, :clear_column_family!, :clear_keyspace!, :cluster_name,
26
+ # :column_families, :count_columns, :count_range, :create_idx_clause,
27
+ # :create_idx_expr, :create_index, :create_index_clause, :create_index_expression,
28
+ # :default_read_consistency=, :default_write_consistency=,
29
+ # :disable_node_auto_discovery!, :disconnect!, :drop_column_family, :drop_index,
30
+ # :drop_keyspace, :each, :each_key, :exists?, :get, :get_columns,
31
+ # :get_indexed_slices, :get_range, :get_range_batch, :get_range_keys,
32
+ # :get_range_single, :insert, :inspect, :keyspace, :keyspace=, :keyspaces,
33
+ # :login!, :multi_count_columns, :multi_get, :multi_get_columns,
34
+ # :partitioner, :remove, :rename_column_family, :rename_keyspace, :ring,
35
+ # :schema, :schema_agreement?, :servers, :thrift_client_class,
36
+ # :thrift_client_options, :truncate!, :update_column_family,
37
+ # :update_keyspace, :version
38
+
39
+ end
40
+ end
data/lib/pools.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'pools/connection_pool'
2
+ require 'pools/handler'
3
+ require 'pools/middleware'
4
+ require 'pools/pooled'
5
+
6
+ require 'pools/engine' if defined?(Rails)
@@ -0,0 +1,237 @@
1
+ ##
2
+ # This file adapted from activerecord gem
3
+ #
4
+
5
+ require 'thread'
6
+ require 'monitor'
7
+ require 'set'
8
+ require 'active_support/core_ext/module/synchronization'
9
+
10
+ module Pools
11
+ # Raised when a connection could not be obtained within the connection
12
+ # acquisition timeout period.
13
+ ConnectionNotEstablished = Class.new(StandardError)
14
+ ConnectionTimeoutError = Class.new(ConnectionNotEstablished)
15
+
16
+ # Connection pool base class for managing Active Record database
17
+ # connections.
18
+ #
19
+ # == Introduction
20
+ #
21
+ # A connection pool synchronizes thread access to a limited number of
22
+ # database connections. The basic idea is that each thread checks out a
23
+ # database connection from the pool, uses that connection, and checks the
24
+ # connection back in. ConnectionPool is completely thread-safe, and will
25
+ # ensure that a connection cannot be used by two threads at the same time,
26
+ # as long as ConnectionPool's contract is correctly followed. It will also
27
+ # handle cases in which there are more threads than connections: if all
28
+ # connections have been checked out, and a thread tries to checkout a
29
+ # connection anyway, then ConnectionPool will wait until some other thread
30
+ # has checked in a connection.
31
+ #
32
+ # == Obtaining (checking out) a connection
33
+ #
34
+ # Connections can be obtained and used from a connection pool in several
35
+ # ways:
36
+ #
37
+ # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
38
+ # earlier (pre-connection-pooling). Eventually, when you're done with
39
+ # the connection(s) and wish it to be returned to the pool, you call
40
+ # ActiveRecord::Base.clear_active_connections!. This will be the
41
+ # default behavior for Active Record when used in conjunction with
42
+ # Action Pack's request handling cycle.
43
+ # 2. Manually check out a connection from the pool with
44
+ # ActiveRecord::Base.connection_pool.checkout. You are responsible for
45
+ # returning this connection to the pool when finished by calling
46
+ # ActiveRecord::Base.connection_pool.checkin(connection).
47
+ # 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
48
+ # obtains a connection, yields it as the sole argument to the block,
49
+ # and returns it to the pool after the block completes.
50
+ #
51
+ # Connections in the pool are actually AbstractAdapter objects (or objects
52
+ # compatible with AbstractAdapter's interface).
53
+ #
54
+ # == Options
55
+ #
56
+ # There are two connection-pooling-related options that you can add to
57
+ # your database connection configuration:
58
+ #
59
+ # * +pool+: number indicating size of connection pool (default 20)
60
+ # * +wait_timeout+: number of seconds to block and wait for a connection
61
+ # before giving up and raising a timeout error (default 10 seconds).
62
+ class ConnectionPool
63
+ attr_reader :options, :connections
64
+
65
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
66
+ # object which describes database connection information (e.g. adapter,
67
+ # host name, username, password, etc), as well as the maximum size for
68
+ # this ConnectionPool.
69
+ #
70
+ # The default ConnectionPool maximum size is 20.
71
+ def initialize(pooled, options)
72
+ @pooled = pooled
73
+ @options = options
74
+
75
+ # The cache of reserved connections mapped to threads
76
+ @reserved_connections = {}
77
+
78
+ # The mutex used to synchronize pool access
79
+ @connection_mutex = Monitor.new
80
+ @queue = @connection_mutex.new_cond
81
+
82
+ # default 10 second timeout unless on ruby 1.9
83
+ @timeout = options[:wait_timeout] || 10
84
+
85
+ # default max pool size to 20
86
+ @size = (options[:pool] && options[:pool].to_i) || 20
87
+
88
+ @connections = []
89
+ @checked_out = []
90
+ end
91
+
92
+ # Retrieve the connection associated with the current thread, or call
93
+ # #checkout to obtain one if necessary.
94
+ #
95
+ # #connection can be called any number of times; the connection is
96
+ # held in a hash keyed by the thread id.
97
+ def connection
98
+ @reserved_connections[current_connection_id] ||= checkout
99
+ end
100
+
101
+ # Signal that the thread is finished with the current connection.
102
+ # #release_connection releases the connection-thread association
103
+ # and returns the connection to the pool.
104
+ def release_connection(with_id = current_connection_id)
105
+ conn = @reserved_connections.delete(with_id)
106
+ checkin conn if conn
107
+ end
108
+
109
+ # If a connection already exists yield it to the block. If no connection
110
+ # exists checkout a connection, yield it to the block, and checkin the
111
+ # connection when finished.
112
+ def with_connection
113
+ connection_id = current_connection_id
114
+ fresh_connection = true unless @reserved_connections[connection_id]
115
+ yield connection
116
+ ensure
117
+ release_connection(connection_id) if fresh_connection
118
+ end
119
+
120
+ # Returns true if a connection has already been opened.
121
+ def connected?
122
+ !@connections.empty?
123
+ end
124
+
125
+ # Disconnects all connections in the pool, and clears the pool.
126
+ def disconnect!
127
+ @reserved_connections.each do |name,conn|
128
+ checkin conn
129
+ end
130
+ @reserved_connections = {}
131
+ @connections.each do |conn|
132
+ @pooled.__disconnect(conn)
133
+ end
134
+ @connections = []
135
+ end
136
+
137
+ # Verify active connections and remove and disconnect connections
138
+ # associated with stale threads.
139
+ def verify_active_connections! #:nodoc:
140
+ clear_stale_cached_connections!
141
+ @connections.each do |connection|
142
+ @pooled.__disconnect(connection)
143
+ end
144
+ end
145
+
146
+ # Return any checked-out connections back to the pool by threads that
147
+ # are no longer alive.
148
+ def clear_stale_cached_connections!
149
+ keys = @reserved_connections.keys - Thread.list.find_all { |t|
150
+ t.alive?
151
+ }.map { |thread| thread.object_id }
152
+ keys.each do |key|
153
+ checkin @reserved_connections[key]
154
+ @reserved_connections.delete(key)
155
+ end
156
+ end
157
+
158
+ # Check-out a database connection from the pool, indicating that you want
159
+ # to use it. You should call #checkin when you no longer need this.
160
+ #
161
+ # This is done by either returning an existing connection, or by creating
162
+ # a new connection. If the maximum number of connections for this pool has
163
+ # already been reached, but the pool is empty (i.e. they're all being used),
164
+ # then this method will wait until a thread has checked in a connection.
165
+ # The wait time is bounded however: if no connection can be checked out
166
+ # within the timeout specified for this pool, then a ConnectionTimeoutError
167
+ # exception will be raised.
168
+ #
169
+ # Returns: an AbstractAdapter object.
170
+ #
171
+ # Raises:
172
+ # - ConnectionTimeoutError: no connection can be obtained from the pool
173
+ # within the timeout period.
174
+ def checkout
175
+ # Checkout an available connection
176
+ @connection_mutex.synchronize do
177
+ loop do
178
+ conn = if @checked_out.size < @connections.size
179
+ checkout_existing_connection
180
+ elsif @connections.size < @size
181
+ checkout_new_connection
182
+ end
183
+ return conn if conn
184
+
185
+ @queue.wait(@timeout)
186
+
187
+ if(@checked_out.size < @connections.size)
188
+ next
189
+ else
190
+ clear_stale_cached_connections!
191
+ if @size == @checked_out.size
192
+ raise ConnectionTimeoutError, "could not obtain a pooled connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
193
+ end
194
+ end
195
+
196
+ end
197
+ end
198
+ end
199
+
200
+ # Check-in a database connection back into the pool, indicating that you
201
+ # no longer need this connection.
202
+ #
203
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
204
+ # calling +checkout+ on this pool.
205
+ def checkin(conn)
206
+ @connection_mutex.synchronize do
207
+ @checked_out.delete conn
208
+ @queue.signal
209
+ end
210
+ end
211
+
212
+ synchronize :verify_active_connections!, :connected?, :disconnect!,
213
+ :with => :@connection_mutex
214
+
215
+ private
216
+ def current_connection_id #:nodoc:
217
+ Thread.current.object_id
218
+ end
219
+
220
+ def checkout_new_connection
221
+ c = @pooled.__connection
222
+ @pooled.__prepare(c)
223
+ @connections << c
224
+ checkout_connection(c)
225
+ end
226
+
227
+ def checkout_existing_connection
228
+ c = (@connections - @checked_out).first
229
+ checkout_connection(c)
230
+ end
231
+
232
+ def checkout_connection(c)
233
+ @checked_out << c
234
+ c
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,19 @@
1
+ require 'rails'
2
+
3
+ module Pools
4
+ class Engine < Rails::Engine
5
+ engine_name :pools
6
+
7
+ # The tests bellow did not work
8
+
9
+ # Test 1
10
+ # middleware.use Pools::Middleware
11
+
12
+ # Test 2
13
+ # initializer "pools.configure_pools_middleware" do |app|
14
+ # app.middleware.use Pools::Middleware
15
+ # end
16
+
17
+ end
18
+ end
19
+
@@ -0,0 +1,62 @@
1
+ ##
2
+ # This file adapted from activerecord gem
3
+ #
4
+
5
+ module Pools
6
+ class Handler
7
+ attr_reader :pools
8
+
9
+ def initialize(pools = {})
10
+ @pools = pools
11
+ end
12
+
13
+ # Add a new connection pool to the mix
14
+ def add(pool, name = nil)
15
+ @pools[name || pool.object_id] = pool
16
+ end
17
+
18
+ # Returns any connections in use by the current thread back to the
19
+ # pool, and also returns connections to the pool cached by threads
20
+ # that are no longer alive.
21
+ def clear_active_connections!
22
+ @pools.each_value {|pool| pool.release_connection }
23
+ end
24
+
25
+ def clear_all_connections!
26
+ @pools.each_value {|pool| pool.disconnect! }
27
+ end
28
+
29
+ # Verify active connections.
30
+ def verify_active_connections! #:nodoc:
31
+ @pools.each_value {|pool| pool.verify_active_connections! }
32
+ end
33
+
34
+ # Returns true if a connection that's accessible to this class has
35
+ # already been opened.
36
+ def connected?(name)
37
+ conn = retrieve_connection_pool(name)
38
+ conn && conn.connected?
39
+ end
40
+
41
+ # Remove the connection for this class. This will close the active
42
+ # connection and the defined connection (if they exist). The result
43
+ # can be used as an argument for establish_connection, for easily
44
+ # re-establishing the connection.
45
+ def remove_connection(name)
46
+ pool = retrieve_connection_pool(name)
47
+ return nil unless pool
48
+
49
+ @pools.delete_if { |key, value| value == pool }
50
+ pool.disconnect!
51
+ end
52
+
53
+ def retrieve_connection_pool(name)
54
+ pool = @pools[name]
55
+ return pool if pool
56
+ end
57
+ end
58
+
59
+ def self.handler
60
+ @@pool_handler ||= Handler.new
61
+ end
62
+ end
@@ -0,0 +1,17 @@
1
+ ##
2
+ # This file adapted from activerecord gem
3
+ #
4
+
5
+ module Pools
6
+ class Middleware
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ @app.call(env)
13
+ ensure
14
+ Pools.handler.clear_active_connections!
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ require 'active_support/core_ext/array/extract_options'
2
+ require 'active_support/concern'
3
+
4
+ module Pools
5
+ module Pooled
6
+ extend ActiveSupport::Concern
7
+ attr_reader :connection_pool, :preparation_chain
8
+
9
+ def initialize(*args)
10
+ options = args.extract_options!
11
+ @preparation_chain = []
12
+ @connection_pool = ConnectionPool.new(self, options)
13
+ Pools.handler.add(@connection_pool, options[:pool_name])
14
+ end
15
+
16
+ def with_connection(&block)
17
+ @connection_pool.with_connection(&block)
18
+ end
19
+
20
+ def __connection
21
+ # Override in parent
22
+ end
23
+
24
+ def __disconnect(connection)
25
+ # Override in parent
26
+ end
27
+
28
+ def __prepare(connection)
29
+ @preparation_chain.each { |args| connection.send(*args) }
30
+ end
31
+
32
+ module ClassMethods
33
+ def connection_methods(*methods)
34
+ methods.each do |method|
35
+ define_method(method) do |*params, &block|
36
+ with_connection do |client|
37
+ client.send(method, *params, &block)
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def preparation_methods(*methods)
44
+ methods.each do |method|
45
+ define_method(method) do |*params|
46
+ @preparation_chain << ([method] + params)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,4 @@
1
+ class Pools
2
+ VERSION = '0.0.2'
3
+ end
4
+
@@ -0,0 +1,44 @@
1
+ require 'pools'
2
+ require 'redis'
3
+
4
+ class Redis
5
+ class Pooled
6
+ include ::Pools::Pooled
7
+
8
+ def initialize(options = {})
9
+ @redis_options = options
10
+ super
11
+ end
12
+
13
+ def __connection
14
+ Redis.connect(@redis_options)
15
+ end
16
+
17
+ def __disconnect(client)
18
+ client.quit if client
19
+ end
20
+
21
+ # Method not supported:
22
+ # Subscribe/Unsubscribe methods and the following...
23
+ # :auth, :select, :discard, :quit, :watch, :unwatch
24
+ # :exec, :multi, :disconnect
25
+
26
+ connection_methods :info, :config, :flushdb, :flushall, :save,
27
+ :bgsave, :bgrewriteaof, :get, :getset, :mget, :append, :substr,
28
+ :strlen, :hgetall, :hget, :hdel, :hkeys, :keys, :randomkey,
29
+ :echo, :ping, :lastsave, :dbsize, :exists, :llen, :lrange,
30
+ :ltrim, :lindex, :linsert, :lset, :lrem, :rpush, :rpushx,
31
+ :lpush, :lpushx, :rpop, :blpop, :brpop, :rpoplpush, :lpop,
32
+ :smembers, :sismember, :sadd, :srem, :smove, :sdiff, :sdiffstore,
33
+ :sinter, :sinterstore, :sunion, :sunionstore, :spop, :scard,
34
+ :srandmember, :zadd, :zrank, :zrevrank, :zincrby, :zcard,
35
+ :zrange, :zrangebyscore, :zcount, :zrevrange, :zremrangebyscore,
36
+ :zremrangebyrank, :zscore, :zrem, :zinterstore, :zunionstore,
37
+ :move, :setnx, :del, :rename, :renamenx, :expire, :persist,
38
+ :ttl, :expireat, :hset, :hsetnx, :hmset, :mapped_hmset, :hmget,
39
+ :mapped_hmget, :hlen, :hvals, :hincrby, :hexists, :monitor,
40
+ :debug, :sync, :[], :[]=, :set, :setex, :mset, :mapped_mset,
41
+ :msetnx, :mapped_msetnx, :mapped_mget, :sort, :incr, :incrby,
42
+ :decr, :decrby, :type, :publish, :id
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ require 'redis-store'
2
+ require 'redis/pooled'
3
+
4
+ class Redis
5
+ class PooledStore < Pooled
6
+ include Store::Ttl, Store::Interface
7
+
8
+ def initialize(options = { })
9
+ super
10
+ _extend_marshalling options
11
+ end
12
+
13
+ def self.rails3? #:nodoc:
14
+ defined?(::Rails) && ::Rails::VERSION::MAJOR == 3
15
+ end
16
+
17
+ def to_s
18
+ with_connection do |c|
19
+ "Redis::Pooled => #{c.host}:#{c.port} against DB #{c.db}"
20
+ end
21
+ end
22
+
23
+ private
24
+ def _extend_marshalling(options) # Copied from Store
25
+ @marshalling = !(options[:marshalling] === false)
26
+ extend Store::Marshalling if @marshalling
27
+ end
28
+ end
29
+
30
+ class << Store
31
+ def new(*args)
32
+ if args.size == 1 && args.first.is_a?(Hash) && args.first[:pool]
33
+ PooledStore.new(*args)
34
+ else
35
+ super(*args)
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,141 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'logger'
3
+
4
+ class TestPool
5
+ include Pools::Pooled
6
+
7
+ class Connection
8
+ attr_accessor :valid, :prepared
9
+
10
+ def test_method
11
+ '__TEST__'
12
+ end
13
+
14
+ def test_method_with_args(input)
15
+ input
16
+ end
17
+
18
+ def prepare_method(value)
19
+ self.prepared = value
20
+ end
21
+
22
+ def yielding_method(value)
23
+ yield(value)
24
+ end
25
+ end
26
+
27
+ def __connection
28
+ c = Connection.new
29
+ c.valid = true
30
+ c.prepared = false
31
+ c
32
+ end
33
+
34
+ preparation_methods :prepare_method
35
+ connection_methods :test_method, :test_method_with_args, :prepared,
36
+ :yielding_method
37
+ end
38
+
39
+ describe Pools::Pooled do
40
+ def checkout_connections
41
+ @pool = TestPool.new(:pool => 2, :wait_timeout => 0.3)
42
+ @connections = []
43
+ @timed_out = 0
44
+
45
+ 4.times do
46
+ Thread.new do
47
+ begin
48
+ @connections << @pool.connection_pool.checkout
49
+ rescue Pools::ConnectionTimeoutError
50
+ @timed_out += 1
51
+ end
52
+ end.join
53
+ end
54
+ end
55
+
56
+ it "test timeout" do
57
+ checkout_connections
58
+ @connections.length.should == 2
59
+ @timed_out.should == 2
60
+ end
61
+
62
+ def checkout_checkin_connections(pool_size, threads)
63
+ @pool = TestPool.new(:pool => pool_size, :wait_timeout => 0.5)
64
+ @connection_count = 0
65
+ @timed_out = 0
66
+ threads.times do
67
+ Thread.new do
68
+ begin
69
+ conn = @pool.connection_pool.checkout
70
+ sleep 0.1
71
+ @pool.connection_pool.checkin conn
72
+ @connection_count += 1
73
+ rescue ActiveRecord::ConnectionTimeoutError
74
+ @timed_out += 1
75
+ end
76
+ end.join
77
+ end
78
+ end
79
+
80
+ it "pass connection checkout" do
81
+ checkout_checkin_connections 1, 2
82
+ @connection_count.should == 2
83
+ @timed_out.should == 0
84
+ @pool.connection_pool.connections.size.should == 1
85
+ end
86
+
87
+ it "pass connection checkout overbooking" do
88
+ checkout_checkin_connections 2, 3
89
+ @connection_count.should == 3
90
+ @timed_out.should == 0
91
+ @pool.connection_pool.connections.size.should == 1
92
+ end
93
+
94
+ it "should check out an existing connection" do
95
+ cpool = TestPool.new(:pool => 1).connection_pool
96
+ orig_conn = cpool.checkout
97
+ cpool.checkin(orig_conn)
98
+ conn = cpool.checkout
99
+ conn.should == orig_conn
100
+ conn.should be_a(TestPool::Connection)
101
+ cpool.checkin(conn)
102
+ end
103
+
104
+ it "should not be connected on init" do
105
+ cpool = TestPool.new(:pool => 1).connection_pool
106
+ cpool.connected?.should be_false
107
+ cpool.with_connection { }
108
+ cpool.connected?.should be_true
109
+ end
110
+
111
+ it "with_connection provides a connection" do
112
+ pool = TestPool.new
113
+ pool.with_connection do |conn|
114
+ conn.should be_a(TestPool::Connection)
115
+ end
116
+ end
117
+
118
+ it "respond to client methods" do
119
+ pool = TestPool.new
120
+ pool.test_method.should == '__TEST__'
121
+ pool.test_method_with_args('hi').should == 'hi'
122
+ end
123
+
124
+ it "respond to yielding methods" do
125
+ pool = TestPool.new
126
+ pool.yielding_method(15) do |value|
127
+ value.should == 15
128
+ end
129
+ end
130
+
131
+ it "not prematurely call preparation methods" do
132
+ pool = TestPool.new
133
+ pool.prepared.should be_false
134
+ end
135
+
136
+ it "not prematurely call preparation methods" do
137
+ pool = TestPool.new
138
+ pool.prepare_method(15)
139
+ pool.prepared.should == 15
140
+ end
141
+ end
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require 'logger'
3
+ require 'redis/pooled'
4
+
5
+ describe Redis::Pooled do
6
+ let(:rpool) { Redis::Pooled.new(:pool => 1) }
7
+
8
+ it "create a client" do
9
+ rpool.with_connection do |conn|
10
+ conn.should be_a(Redis)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ $TESTING=true
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'pools'
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cassilds-connection-pool
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Rykov
9
+ - Umanni
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2011-09-22 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ requirement: &19260040 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: 3.0.0
23
+ - - <
24
+ - !ruby/object:Gem::Version
25
+ version: '3.2'
26
+ type: :runtime
27
+ prerelease: false
28
+ version_requirements: *19260040
29
+ description: ! 'Generalized connection pooling
30
+
31
+ '
32
+ email:
33
+ - mrykov@gmail
34
+ - contato@umanni.com.br
35
+ executables: []
36
+ extensions: []
37
+ extra_rdoc_files: []
38
+ files:
39
+ - .gitignore
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - cassilds-connection-pool.gemspec
44
+ - lib/cassandra/pooled.rb
45
+ - lib/pools.rb
46
+ - lib/pools/connection_pool.rb
47
+ - lib/pools/engine.rb
48
+ - lib/pools/handler.rb
49
+ - lib/pools/middleware.rb
50
+ - lib/pools/pooled.rb
51
+ - lib/pools/version.rb
52
+ - lib/redis/pooled.rb
53
+ - lib/redis/pooled_store.rb
54
+ - spec/pooled_spec.rb
55
+ - spec/redis_spec.rb
56
+ - spec/spec_helper.rb
57
+ homepage: http://github.com/umanni/cassilds-connection-pool
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.10
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Generalized connection pooling
81
+ test_files: []