This ports two active_record patches over that deal with database thread pooling
parent
b3dec4d183
commit
a6787106a5
|
@ -8,6 +8,10 @@ require 'fileutils'
|
|||
# Provide access to ActiveRecord models shared w/ commercial versions
|
||||
require "metasploit_data_models"
|
||||
|
||||
# Patches issues with ActiveRecord
|
||||
require "msf/core/patches/active_record"
|
||||
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
module Msf
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
class ConnectionPool
|
||||
|
||||
|
||||
# XXX: This fixes the logic around whether a connection allocated is "fresh"
|
||||
# AR incorrectly assumed that if any connection was established, it should
|
||||
# no longer free the allocated connection.
|
||||
|
||||
# Check to see if there is an active thread connection
|
||||
def active_thread_connection?(with_id = current_connection_id)
|
||||
@reserved_connections.has_key?(with_id)
|
||||
end
|
||||
|
||||
# If a connection already exists yield it to the block. If no connection
|
||||
# exists checkout a connection, yield it to the block, and checkin the
|
||||
# connection when finished.
|
||||
def with_connection
|
||||
connection_id = current_connection_id
|
||||
fresh_connection = true unless active_thread_connection?(connection_id)
|
||||
yield connection
|
||||
ensure
|
||||
release_connection(connection_id) if fresh_connection
|
||||
end
|
||||
|
||||
|
||||
# XXX: This allows the wait_timeout parameter in the database specification
|
||||
# to use wall time vs a single @queue.wait() call to determine when
|
||||
# it should look for an available connection. This is important with
|
||||
# heavy threading and can be used to buffer spikes when a large number
|
||||
# of threads need their own connection.
|
||||
|
||||
# Check-out a database connection from the pool, indicating that you want
|
||||
# to use it. You should call #checkin when you no longer need this.
|
||||
#
|
||||
# This is done by either returning an existing connection, or by creating
|
||||
# a new connection. If the maximum number of connections for this pool has
|
||||
# already been reached, but the pool is empty (i.e. they're all being used),
|
||||
# then this method will wait until a thread has checked in a connection.
|
||||
# The wait time is bounded however: if no connection can be checked out
|
||||
# within the timeout specified for this pool, then a ConnectionTimeoutError
|
||||
# exception will be raised.
|
||||
#
|
||||
# Returns: an AbstractAdapter object.
|
||||
#
|
||||
# Raises:
|
||||
# - ConnectionTimeoutError: no connection can be obtained from the pool
|
||||
# within the timeout period.
|
||||
def checkout
|
||||
# Checkout an available connection
|
||||
checkout_time = Time.now.to_i
|
||||
loop do
|
||||
synchronize do
|
||||
conn = @connections.find { |c| c.lease }
|
||||
|
||||
unless conn
|
||||
if @connections.size < @size
|
||||
conn = checkout_new_connection
|
||||
conn.lease
|
||||
end
|
||||
end
|
||||
|
||||
if conn
|
||||
checkout_and_verify conn
|
||||
return conn
|
||||
end
|
||||
|
||||
# Wait up to five seconds at a time for a yield
|
||||
@queue.wait(5)
|
||||
|
||||
if(active_connections.size < @connections.size)
|
||||
next
|
||||
else
|
||||
clear_stale_cached_connections!
|
||||
if @size == active_connections.size and (Time.now.to_i - @timeout) > checkout_time
|
||||
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it or the wait_timeout parameter"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue