Class: ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter
- Inherits:
-
AbstractAdapter
- Object
- AbstractAdapter
- ActiveRecord::ConnectionAdapters::SeamlessDatabasePoolAdapter
- Defined in:
- lib/active_record/connection_adapters/seamless_database_pool_adapter.rb
Defined Under Namespace
Classes: AvailableConnections, DatabaseConnectionError
Instance Attribute Summary collapse
-
#master_connection ⇒ Object
readonly
Returns the value of attribute master_connection.
-
#read_connections ⇒ Object
readonly
Returns the value of attribute read_connections.
Class Method Summary collapse
-
.adapter_class(master_connection) ⇒ Object
Create an anonymous class that extends this one and proxies methods to the pool connections.
-
.visitor_for(pool) ⇒ Object
Set the arel visitor on the connections.
Instance Method Summary collapse
- #active? ⇒ Boolean
-
#adapter_name ⇒ Object
:nodoc:.
-
#all_connections ⇒ Object
Returns an array of the master connection and the read pool connections.
-
#available_read_connections ⇒ Object
Get the available weighted connections.
-
#current_read_connection ⇒ Object
Get the current read connection.
- #disconnect! ⇒ Object
-
#initialize(connection, logger, master_connection, read_connections, pool_weights, config) ⇒ SeamlessDatabasePoolAdapter
constructor
A new instance of SeamlessDatabasePoolAdapter.
- #inspect ⇒ Object
-
#pool_weight(connection) ⇒ Object
Get the pool weight of a connection.
-
#random_read_connection ⇒ Object
Get a random read connection from the pool.
- #reconnect! ⇒ Object
- #requires_reloading? ⇒ Boolean
- #reset! ⇒ Object
- #reset_available_read_connections ⇒ Object
- #reset_runtime ⇒ Object
-
#suppress_read_connection(conn, expire) ⇒ Object
Temporarily remove a connection from the read pool.
- #to_s ⇒ Object
- #transaction(options = {}) ⇒ Object
-
#use_master_connection ⇒ Object
Force using the master connection in a block.
- #using_master_connection? ⇒ Boolean
- #verify!(*ignored) ⇒ Object
- #visitor ⇒ Object
- #visitor=(visitor) ⇒ Object
Constructor Details
#initialize(connection, logger, master_connection, read_connections, pool_weights, config) ⇒ SeamlessDatabasePoolAdapter
Returns a new instance of SeamlessDatabasePoolAdapter.
154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 154 def initialize(connection, logger, master_connection, read_connections, pool_weights, config) @master_connection = master_connection @read_connections = read_connections.dup.freeze super(connection, logger, config) @weighted_read_connections = [] pool_weights.each_pair do |conn, weight| weight.times{@weighted_read_connections << conn} end @available_read_connections = [AvailableConnections.new(@weighted_read_connections)] end |
Instance Attribute Details
#master_connection ⇒ Object (readonly)
Returns the value of attribute master_connection.
80 81 82 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 80 def master_connection @master_connection end |
#read_connections ⇒ Object (readonly)
Returns the value of attribute read_connections.
80 81 82 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 80 def read_connections @read_connections end |
Class Method Details
.adapter_class(master_connection) ⇒ Object
Create an anonymous class that extends this one and proxies methods to the pool connections.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 84 def adapter_class(master_connection) adapter_class_name = master_connection.adapter_name.classify return const_get(adapter_class_name) if const_defined?(adapter_class_name, false) # Define methods to proxy to the appropriate pool read_only_methods = [:select, :select_rows, :execute, :tables, :columns] clear_cache_methods = [:insert, :update, :delete] # Get a list of all methods redefined by the underlying adapter. These will be # proxied to the master connection. master_methods = [] override_classes = (master_connection.class.ancestors - AbstractAdapter.ancestors) override_classes.each do |connection_class| master_methods.concat(connection_class.public_instance_methods(false)) master_methods.concat(connection_class.protected_instance_methods(false)) master_methods.concat(connection_class.private_instance_methods(false)) end master_methods = master_methods.collect{|m| m.to_sym}.uniq master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false) master_methods -= read_only_methods master_methods -= [:select_all, :select_one, :select_value, :select_values] master_methods -= clear_cache_methods klass = Class.new(self) master_methods.each do |method_name| klass.class_eval <<-EOS, __FILE__, __LINE__ + 1 def #{method_name}(*args, &block) use_master_connection do return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block) end end EOS end clear_cache_methods.each do |method_name| klass.class_eval <<-EOS, __FILE__, __LINE__ + 1 def #{method_name}(*args, &block) clear_query_cache if query_cache_enabled use_master_connection do return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block) end end EOS end read_only_methods.each do |method_name| klass.class_eval <<-EOS, __FILE__, __LINE__ + 1 def #{method_name}(*args, &block) connection = @use_master ? master_connection : current_read_connection proxy_connection_method(connection, :#{method_name}, :read, *args, &block) end EOS end klass.send :protected, :select const_set(adapter_class_name, klass) return klass end |
.visitor_for(pool) ⇒ Object
Set the arel visitor on the connections.
145 146 147 148 149 150 151 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 145 def visitor_for(pool) # This is ugly, but then again, so is the code in ActiveRecord for setting the arel # visitor. There is a note in the code indicating the method signatures should be updated. config = pool.spec.config.with_indifferent_access adapter = config[:master][:adapter] || config[:pool_adapter] SeamlessDatabasePool.adapter_class_for(adapter).visitor_for(pool) end |
Instance Method Details
#active? ⇒ Boolean
199 200 201 202 203 204 205 206 207 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 199 def active? if SeamlessDatabasePool.read_only_connection_type == :master @master_connection.active? else active = true do_to_connections {|conn| active &= conn.active?} active end end |
#adapter_name ⇒ Object
:nodoc:
167 168 169 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 167 def adapter_name #:nodoc: 'Seamless_Database_Pool' end |
#all_connections ⇒ Object
Returns an array of the master connection and the read pool connections
172 173 174 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 172 def all_connections [@master_connection] + @read_connections end |
#available_read_connections ⇒ Object
Get the available weighted connections. When a connection is dead and cannot be reconnected, it will be temporarily removed from the read pool so we don’t keep trying to reconnect to a database that isn’t listening.
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 302 def available_read_connections available = @available_read_connections.last if available.expired? begin @logger.info("Adding dead database connection back to the pool") if @logger available.reconnect! rescue => e # Couldn't reconnect so try again in a little bit if @logger @logger.warn("Failed to reconnect to database when adding connection back to the pool") @logger.warn(e) end available.expires = 30.seconds.from_now return available.connections end # If reconnect is successful, the connection will have been re-added to @available_read_connections list, # so let's pop this old version of the connection @available_read_connections.pop # Now we'll try again after either expiring our bad connection or re-adding our good one return available_read_connections else return available.connections end end |
#current_read_connection ⇒ Object
Get the current read connection
247 248 249 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 247 def current_read_connection return SeamlessDatabasePool.read_only_connection(self) end |
#disconnect! ⇒ Object
213 214 215 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 213 def disconnect! do_to_connections {|conn| conn.disconnect!} end |
#inspect ⇒ Object
270 271 272 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 270 def inspect to_s end |
#pool_weight(connection) ⇒ Object
Get the pool weight of a connection
177 178 179 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 177 def pool_weight(connection) return @weighted_read_connections.select{|conn| conn == connection}.size end |
#random_read_connection ⇒ Object
Get a random read connection from the pool. If the connection is not active, it will attempt to reconnect to the database. If that fails, it will be removed from the pool for one minute.
237 238 239 240 241 242 243 244 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 237 def random_read_connection weighted_read_connections = available_read_connections if @use_master || weighted_read_connections.empty? return master_connection else weighted_read_connections[rand(weighted_read_connections.length)] end end |
#reconnect! ⇒ Object
209 210 211 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 209 def reconnect! do_to_connections {|conn| conn.reconnect!} end |
#requires_reloading? ⇒ Boolean
181 182 183 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 181 def requires_reloading? false end |
#reset! ⇒ Object
217 218 219 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 217 def reset! do_to_connections {|conn| conn.reset!} end |
#reset_available_read_connections ⇒ Object
329 330 331 332 333 334 335 336 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 329 def reset_available_read_connections @available_read_connections.slice!(1, @available_read_connections.length) @available_read_connections.first.connections.each do |connection| unless connection.active? connection.reconnect! rescue nil end end end |
#reset_runtime ⇒ Object
229 230 231 232 233 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 229 def reset_runtime total = 0.0 do_to_connections {|conn| total += conn.reset_runtime} total end |
#suppress_read_connection(conn, expire) ⇒ Object
Temporarily remove a connection from the read pool.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 339 def suppress_read_connection(conn, expire) available = available_read_connections connections = available.reject{|c| c == conn} # This wasn't a read connection so don't suppress it return if connections.length == available.length if connections.empty? @logger.warn("All read connections are marked dead; trying them all again.") if @logger # No connections available so we might as well try them all again reset_available_read_connections else @logger.warn("Removing #{conn.inspect} from the connection pool for #{expire} seconds") if @logger # Available connections will now not include the suppressed connection for a while @available_read_connections.push(AvailableConnections.new(connections, conn, expire.seconds.from_now)) end end |
#to_s ⇒ Object
266 267 268 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 266 def to_s "#<#{self.class.name}:0x#{object_id.to_s(16)} #{all_connections.size} connections>" end |
#transaction(options = {}) ⇒ Object
185 186 187 188 189 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 185 def transaction( = {}) use_master_connection do super end end |
#use_master_connection ⇒ Object
Force using the master connection in a block.
256 257 258 259 260 261 262 263 264 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 256 def use_master_connection save_val = @use_master begin @use_master = true yield if block_given? ensure @use_master = save_val end end |
#using_master_connection? ⇒ Boolean
251 252 253 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 251 def using_master_connection? !!@use_master end |
#verify!(*ignored) ⇒ Object
221 222 223 224 225 226 227 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 221 def verify!(*ignored) if SeamlessDatabasePool.read_only_connection_type == :master @master_connection.verify!(*ignored) else do_to_connections {|conn| conn.verify!(*ignored)} end end |
#visitor ⇒ Object
195 196 197 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 195 def visitor master_connection.visitor end |
#visitor=(visitor) ⇒ Object
191 192 193 |
# File 'lib/active_record/connection_adapters/seamless_database_pool_adapter.rb', line 191 def visitor=(visitor) all_connections.each{|conn| conn.visitor = visitor} end |