1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
# frozen-string-literal: true
#
# The connection_expiration extension modifies a database's
# connection pool to validate that connections checked out
# from the pool are not expired, before yielding them for
# use. If it detects an expired connection, it removes it
# from the pool and tries the next available connection,
# creating a new connection if no available connection is
# unexpired. Example of use:
#
# DB.extension(:connection_expiration)
#
# The default connection timeout is 14400 seconds (4 hours).
# To override it:
#
# DB.pool.connection_expiration_timeout = 3600 # 1 hour
#
# Note that this extension only affects the default threaded
# and the sharded threaded connection pool. The single
# threaded and sharded single threaded connection pools are
# not affected. As the only reason to use the single threaded
# pools is for speed, and this extension makes the connection
# pool slower, there's not much point in modifying this
# extension to work with the single threaded pools. The
# threaded pools work fine even in single threaded code, so if
# you are currently using a single threaded pool and want to
# use this extension, switch to using a threaded pool.
#
# Related module: Sequel::ConnectionExpiration
#
module Sequel
module ConnectionExpiration
class Retry < Error; end
Sequel::Deprecation.deprecate_constant(self, :Retry)
# The number of seconds that need to pass since
# connection creation before expiring a connection.
# Defaults to 14400 seconds (4 hours).
attr_accessor :connection_expiration_timeout
# The maximum number of seconds that will be added as a random delay to the expiration timeout
# Defaults to 0 seconds (no random delay).
attr_accessor :connection_expiration_random_delay
# Initialize the data structures used by this extension.
def self.extended(pool)
pool.instance_exec do
sync do
@connection_expiration_timestamps ||= {}
@connection_expiration_timeout ||= 14400
@connection_expiration_random_delay ||= 0
end
end
end
private
# Clean up expiration timestamps during disconnect.
def disconnect_connection(conn)
sync{@connection_expiration_timestamps.delete(conn)}
super
end
# Record the time the connection was created.
def make_new(*)
conn = super
@connection_expiration_timestamps[conn] = [Sequel.start_timer, @connection_expiration_timeout + (rand * @connection_expiration_random_delay)].freeze
conn
end
# When acquiring a connection, check if the connection is expired.
# If it is expired, disconnect the connection, and retry with a new
# connection.
def acquire(*a)
conn = nil
1.times do
if (conn = super) &&
(cet = sync{@connection_expiration_timestamps[conn]}) &&
Sequel.elapsed_seconds_since(cet[0]) > cet[1]
if pool_type == :sharded_threaded
sync{allocated(a.last).delete(Sequel.current)}
else
sync{@allocated.delete(Sequel.current)}
end
disconnect_connection(conn)
redo
end
end
conn
end
end
Database.register_extension(:connection_expiration){|db| db.pool.extend(ConnectionExpiration)}
end
|