sessionm-cassandra_object 2.2.20 → 2.2.22

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