sessionm-cassandra_object 2.2.20 → 2.2.22

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,8 +4,6 @@ module CassandraObject
4
4
  extend ActiveSupport::Autoload
5
5
 
6
6
  autoload :Base
7
- autoload :AsyncConnection
8
- autoload :Connection
9
7
  autoload :Attributes
10
8
  autoload :Dirty
11
9
  autoload :Consistency
@@ -25,6 +23,8 @@ module CassandraObject
25
23
  autoload :Type
26
24
  autoload :Schema
27
25
 
26
+ autoload :ConnectionAdapters, 'cassandra_object/connection/connection_pool'
27
+
28
28
  module Tasks
29
29
  extend ActiveSupport::Autoload
30
30
  autoload :Keyspace
@@ -49,3 +49,4 @@ module CassandraObject
49
49
  end
50
50
 
51
51
  require 'cassandra_object/railtie'
52
+
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
 
3
+ require 'cassandra_object/connection/connection_specification'
3
4
  require 'cassandra_object/log_subscriber'
4
5
  require 'cassandra_object/types'
5
6
  require 'cassandra_object/errors'
@@ -31,8 +32,9 @@ module CassandraObject
31
32
  extend ActiveSupport::DescendantsTracker
32
33
 
33
34
  #TODO: make the connection type configurable
34
- include AsyncConnection
35
+ #include AsyncConnection
35
36
  #include Connection
37
+ #include ConnectionPool
36
38
  include Consistency
37
39
  include Identity
38
40
  include Attributes
@@ -0,0 +1,306 @@
1
+ require 'thread'
2
+ require 'monitor'
3
+ require 'set'
4
+ require 'active_support/core_ext/module/synchronization'
5
+ require 'cassandra_object/errors'
6
+
7
+ module CassandraObject
8
+ # Raised when a connection could not be obtained within the connection
9
+ # acquisition timeout period.
10
+ class ConnectionTimeoutError < ConnectionNotEstablished
11
+ end
12
+
13
+ module ConnectionAdapters
14
+ class ConnectionPool
15
+ attr_reader :spec, :connections
16
+
17
+ # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
18
+ # object which describes database connection information (e.g. adapter,
19
+ # host name, username, password, etc), as well as the maximum size for
20
+ # this ConnectionPool.
21
+ #
22
+ # The default ConnectionPool maximum size is 5.
23
+ def initialize(spec)
24
+ @spec = spec
25
+
26
+ # The cache of reserved connections mapped to threads
27
+ @reserved_connections = {}
28
+
29
+ # The mutex used to synchronize pool access
30
+ @connection_mutex = Monitor.new
31
+ @queue = @connection_mutex.new_cond
32
+
33
+ @thrift = spec[:thrift]
34
+
35
+ # default max pool size to 5
36
+ @size = (spec[:pool] && spec[:pool].to_i) || 5
37
+
38
+ @connections = []
39
+ @checked_out = []
40
+ end
41
+
42
+ # Retrieve the connection associated with the current thread, or call
43
+ # #checkout to obtain one if necessary.
44
+ #
45
+ # #connection can be called any number of times; the connection is
46
+ # held in a hash keyed by the thread id.
47
+ def connection
48
+ Rails.logger.info "current_connection_id:#{current_connection_id} conn:#{@reserved_connections[current_connection_id]}"
49
+ @reserved_connections[current_connection_id] ||= checkout
50
+ end
51
+
52
+ # Signal that the thread is finished with the current connection.
53
+ # #release_connection releases the connection-thread association
54
+ # and returns the connection to the pool.
55
+ def release_connection(with_id = current_connection_id)
56
+ conn = @reserved_connections.delete(with_id)
57
+ checkin conn if conn
58
+ end
59
+
60
+ # If a connection already exists yield it to the block. If no connection
61
+ # exists checkout a connection, yield it to the block, and checkin the
62
+ # connection when finished.
63
+ def with_connection
64
+ connection_id = current_connection_id
65
+ fresh_connection = true unless @reserved_connections[connection_id]
66
+ yield connection
67
+ ensure
68
+ release_connection(connection_id) if fresh_connection
69
+ end
70
+
71
+ # Returns true if a connection has already been opened.
72
+ def connected?
73
+ !@connections.empty?
74
+ end
75
+
76
+ # Disconnects all connections in the pool, and clears the pool.
77
+ def disconnect!
78
+ @reserved_connections.each do |name,conn|
79
+ checkin conn
80
+ end
81
+ @reserved_connections = {}
82
+ @connections.each do |conn|
83
+ conn.disconnect!
84
+ end
85
+ @connections = []
86
+ end
87
+
88
+ # Clears the cache which maps classes
89
+ def clear_reloadable_connections!
90
+ @reserved_connections.each do |name, conn|
91
+ checkin conn
92
+ end
93
+ @reserved_connections = {}
94
+ @connections.each do |conn|
95
+ conn.disconnect! if conn.requires_reloading?
96
+ end
97
+ @connections.delete_if do |conn|
98
+ conn.requires_reloading?
99
+ end
100
+ end
101
+
102
+ # Verify active connections and remove and disconnect connections
103
+ # associated with stale threads.
104
+ def verify_active_connections! #:nodoc:
105
+ clear_stale_cached_connections!
106
+ @connections.each do |connection|
107
+ connection.verify!
108
+ end
109
+ end
110
+
111
+ # Return any checked-out connections back to the pool by threads that
112
+ # are no longer alive.
113
+ def clear_stale_cached_connections!
114
+ keys = @reserved_connections.keys - Thread.list.find_all { |t|
115
+ t.alive?
116
+ }.map { |thread| thread.object_id }
117
+ keys.each do |key|
118
+ checkin @reserved_connections[key]
119
+ @reserved_connections.delete(key)
120
+ end
121
+ end
122
+
123
+ # Check-out a database connection from the pool, indicating that you want
124
+ # to use it. You should call #checkin when you no longer need this.
125
+ #
126
+ # This is done by either returning an existing connection, or by creating
127
+ # a new connection. If the maximum number of connections for this pool has
128
+ # already been reached, but the pool is empty (i.e. they're all being used),
129
+ # then this method will wait until a thread has checked in a connection.
130
+ # The wait time is bounded however: if no connection can be checked out
131
+ # within the timeout specified for this pool, then a ConnectionTimeoutError
132
+ # exception will be raised.
133
+ #
134
+ # Returns: an AbstractAdapter object.
135
+ #
136
+ # Raises:
137
+ # - ConnectionTimeoutError: no connection can be obtained from the pool
138
+ # within the timeout period.
139
+ def checkout
140
+ Rails.logger.info "checkout()"
141
+ # Checkout an available connection
142
+ @connection_mutex.synchronize do
143
+ loop do
144
+ conn = if @checked_out.size < @connections.size
145
+ checkout_existing_connection
146
+ elsif @connections.size < @size
147
+ checkout_new_connection
148
+ end
149
+ Rails.logger.info "checkout returning:#{conn}" if conn
150
+ return conn if conn
151
+
152
+ @queue.wait(@timeout)
153
+
154
+ if(@checked_out.size < @connections.size)
155
+ next
156
+ else
157
+ clear_stale_cached_connections!
158
+ if @size == @checked_out.size
159
+ raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
160
+ end
161
+ end
162
+
163
+ end
164
+ end
165
+ end
166
+
167
+ # Check-in a database connection back into the pool, indicating that you
168
+ # no longer need this connection.
169
+ #
170
+ # +conn+: an AbstractAdapter object, which was obtained by earlier by
171
+ # calling +checkout+ on this pool.
172
+ def checkin(conn)
173
+ Rails.logger.info "checkin:#{conn}"
174
+ @connection_mutex.synchronize do
175
+ # conn.send(:_run_checkin_callbacks) do
176
+ @checked_out.delete conn
177
+ @queue.signal
178
+ # end
179
+ end
180
+ end
181
+
182
+ synchronize :clear_reloadable_connections!, :verify_active_connections!,
183
+ :connected?, :disconnect!, :with => :@connection_mutex
184
+
185
+ private
186
+ def new_connection
187
+ Rails.logger.info "spec:#{spec.inspect}"
188
+ Cassandra.new(spec[:keyspace], spec[:servers], spec[:thrift])
189
+
190
+ #this_spec = spec.dup
191
+ #require 'thrift_client/event_machine'
192
+ #this_spec[:thrift].merge!(:transport => Thrift::EventMachineTransport,
193
+ # :transport_wrapper => nil)
194
+ #Cassandra.new(this_spec[:keyspace], this_spec[:servers], this_spec[:thrift])
195
+ end
196
+
197
+ def current_connection_id #:nodoc:
198
+ Thread.current.object_id
199
+ end
200
+
201
+ def checkout_new_connection
202
+ c = new_connection
203
+ @connections << c
204
+ checkout_and_verify(c)
205
+ end
206
+
207
+ def checkout_existing_connection
208
+ c = (@connections - @checked_out).first
209
+ checkout_and_verify(c)
210
+ end
211
+
212
+ def checkout_and_verify(c)
213
+ # c.run_callbacks :checkout do
214
+ # c.verify!
215
+ @checked_out << c
216
+ # end
217
+ c
218
+ end
219
+ end
220
+
221
+ class ConnectionHandler
222
+ attr_reader :connection_pools
223
+
224
+ def initialize(pools = {})
225
+ @connection_pools = pools
226
+ end
227
+
228
+ def establish_connection(name, spec)
229
+ @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
230
+ end
231
+
232
+ # Returns any connections in use by the current thread back to the pool,
233
+ # and also returns connections to the pool cached by threads that are no
234
+ # longer alive.
235
+ def clear_active_connections!
236
+ @connection_pools.each_value {|pool| pool.release_connection }
237
+ end
238
+
239
+ # Clears the cache which maps classes
240
+ def clear_reloadable_connections!
241
+ @connection_pools.each_value {|pool| pool.clear_reloadable_connections! }
242
+ end
243
+
244
+ def clear_all_connections!
245
+ @connection_pools.each_value {|pool| pool.disconnect! }
246
+ end
247
+
248
+ # Verify active connections.
249
+ def verify_active_connections! #:nodoc:
250
+ @connection_pools.each_value {|pool| pool.verify_active_connections! }
251
+ end
252
+
253
+ # Locate the connection of the nearest super class. This can be an
254
+ # active or defined connection: if it is the latter, it will be
255
+ # opened and set as the active connection for the class it was defined
256
+ # for (not necessarily the current class).
257
+ def retrieve_connection(klass) #:nodoc:
258
+ pool = retrieve_connection_pool(klass)
259
+ (pool && pool.connection) or raise ConnectionNotEstablished
260
+ end
261
+
262
+ # Returns true if a connection that's accessible to this class has
263
+ # already been opened.
264
+ def connected?(klass)
265
+ conn = retrieve_connection_pool(klass)
266
+ conn && conn.connected?
267
+ end
268
+
269
+ # Remove the connection for this class. This will close the active
270
+ # connection and the defined connection (if they exist). The result
271
+ # can be used as an argument for establish_connection, for easily
272
+ # re-establishing the connection.
273
+ def remove_connection(klass)
274
+ pool = @connection_pools[klass.name]
275
+ return nil unless pool
276
+
277
+ @connection_pools.delete_if { |key, value| value == pool }
278
+ pool.disconnect!
279
+ pool.spec.config
280
+ end
281
+
282
+ def retrieve_connection_pool(klass)
283
+ pool = @connection_pools[klass.name]
284
+ return pool if pool
285
+ return nil if CassandraObject::Base == klass
286
+ retrieve_connection_pool klass.superclass
287
+ end
288
+ end
289
+
290
+ class ConnectionManagement
291
+ def initialize(app)
292
+ @app = app
293
+ end
294
+
295
+ def call(env)
296
+ @app.call(env)
297
+ ensure
298
+ # Don't return connection (and perform implicit rollback) if
299
+ # this request is a part of integration test
300
+ unless env.key?("rack.test")
301
+ CassandraObject::Base.clear_active_connections!
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,56 @@
1
+ module CassandraObject
2
+ class Base
3
+ class ConnectionSpecification #:nodoc:
4
+ attr_reader :config, :adapter_method
5
+ def initialize (config, adapter_method)
6
+ @config, @adapter_method = config, adapter_method
7
+ end
8
+ end
9
+
10
+ ##
11
+ # :singleton-method:
12
+ # The connection handler
13
+ class_attribute :connection_handler, :instance_writer => false
14
+ self.connection_handler = ConnectionAdapters::ConnectionHandler.new
15
+
16
+ # Returns the connection currently associated with the class. This can
17
+ # also be used to "borrow" the connection to do database work that isn't
18
+ # easily done without going straight to SQL.
19
+ def connection
20
+ self.class.connection
21
+ end
22
+
23
+ def self.establish_connection(spec = nil)
24
+ self.connection_handler.establish_connection(self.name, spec)
25
+ end
26
+
27
+ class << self
28
+ # Returns the connection currently associated with the class. This can
29
+ # also be used to "borrow" the connection to do database work unrelated
30
+ # to any of the specific Active Records.
31
+ def connection
32
+ retrieve_connection
33
+ end
34
+
35
+ def connection_pool
36
+ connection_handler.retrieve_connection_pool(self)
37
+ end
38
+
39
+ def retrieve_connection
40
+ connection_handler.retrieve_connection(self)
41
+ end
42
+
43
+ # Returns true if Active Record is connected.
44
+ def connected?
45
+ connection_handler.connected?(self)
46
+ end
47
+
48
+ def remove_connection(klass = self)
49
+ connection_handler.remove_connection(klass)
50
+ end
51
+
52
+ delegate :clear_active_connections!, :clear_reloadable_connections!,
53
+ :clear_all_connections!,:verify_active_connections!, :to => :connection_handler
54
+ end
55
+ end
56
+ end
@@ -7,4 +7,7 @@ module CassandraObject
7
7
 
8
8
  class RecordNotFound < CasssandraObjectError
9
9
  end
10
- end
10
+
11
+ class ConnectionNotEstablished < CasssandraObjectError
12
+ end
13
+ end
@@ -1,5 +1,8 @@
1
1
  module CassandraObject
2
2
  class Railtie < Rails::Railtie
3
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
4
+ "CassandraObject::ConnectionAdapters::ConnectionManagement"
5
+
3
6
  rake_tasks do
4
7
  load 'cassandra_object/tasks/ks.rake'
5
8
  end
@@ -8,4 +11,4 @@ module CassandraObject
8
11
  require 'cassandra_object/generators/migration_generator'
9
12
  end
10
13
  end
11
- end
14
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'sessionm-cassandra_object'
5
- s.version = '2.2.20'
5
+ s.version = '2.2.22'
6
6
  s.description = 'Cassandra ActiveModel'
7
7
  s.summary = 'Cassandra ActiveModel'
8
8
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 2
7
7
  - 2
8
- - 20
9
- version: 2.2.20
8
+ - 22
9
+ version: 2.2.22
10
10
  platform: ruby
11
11
  authors:
12
12
  - Michael Koziarski
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-08-16 00:00:00 -04:00
19
+ date: 2011-08-17 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -80,13 +80,14 @@ files:
80
80
  - lib/cassandra_object/associations.rb
81
81
  - lib/cassandra_object/associations/one_to_many.rb
82
82
  - lib/cassandra_object/associations/one_to_one.rb
83
- - lib/cassandra_object/async_connection.rb
84
83
  - lib/cassandra_object/attributes.rb
85
84
  - lib/cassandra_object/base.rb
86
85
  - lib/cassandra_object/batches.rb
87
86
  - lib/cassandra_object/callbacks.rb
88
87
  - lib/cassandra_object/collection.rb
89
- - lib/cassandra_object/connection.rb
88
+ - lib/cassandra_object/connection/connection.rb
89
+ - lib/cassandra_object/connection/connection_pool.rb
90
+ - lib/cassandra_object/connection/connection_specification.rb
90
91
  - lib/cassandra_object/consistency.rb
91
92
  - lib/cassandra_object/cursor.rb
92
93
  - lib/cassandra_object/dirty.rb
@@ -1,48 +0,0 @@
1
- module CassandraObject
2
- module AsyncConnection
3
- extend ActiveSupport::Concern
4
-
5
- included do
6
- class_attribute :connection_spec
7
-
8
- class_eval do
9
- @@fiber_connections = {}
10
- def self.connection()
11
- @@fiber_connections[Fiber.current.object_id] ||=
12
- begin
13
- spec = connection_spec.dup
14
-
15
- if EM.reactor_running?
16
- require 'thrift_client/event_machine'
17
- spec[:thrift].merge!(:transport => Thrift::EventMachineTransport,
18
- :transport_wrapper => nil)
19
- end
20
-
21
- Cassandra.new(spec[:keyspace], spec[:servers], spec[:thrift])
22
- end
23
- end
24
- def self.connection?() !!connection end
25
-
26
- def connection
27
- defined?(@connection) ? @connection : singleton_class.connection
28
- end
29
-
30
- def connection?
31
- !!connection
32
- end
33
- end
34
- end
35
-
36
- module ClassMethods
37
- DEFAULT_OPTIONS = {
38
- servers: "127.0.0.1:9160",
39
- thrift: {}
40
- }
41
- def establish_connection(spec)
42
- spec.reverse_merge!(DEFAULT_OPTIONS)
43
- spec[:thrift].symbolize_keys!
44
- self.connection_spec = spec
45
- end
46
- end
47
- end
48
- end