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 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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
require_relative "spec_helper"
connection_validator_specs = Module.new do
extend Minitest::Spec::DSL
before do
@db = db
@m = Module.new do
def disconnect_connection(conn)
@sqls << 'disconnect'
end
def valid_connection?(conn)
super
raise if conn.valid == :exception
conn.valid
end
def connect(server)
conn = super
conn.extend(Module.new do
attr_accessor :valid
end)
conn.valid = true
conn
end
end
@db.extend @m
@db.extension(:connection_validator)
end
it "should still allow new connections" do
@db.synchronize{|c| c}.must_be_kind_of(Sequel::Mock::Connection)
end
it "should only validate if connection idle longer than timeout" do
c1 = @db.synchronize{|c| c}
@db.sqls.must_equal []
@db.synchronize{|c| c}.must_be_same_as(c1)
@db.sqls.must_equal []
@db.pool.connection_validation_timeout = -1
@db.synchronize{|c| c}.must_be_same_as(c1)
@db.sqls.must_equal ['SELECT NULL']
@db.pool.connection_validation_timeout = 1
@db.synchronize{|c| c}.must_be_same_as(c1)
@db.sqls.must_equal []
@db.synchronize{|c| c}.must_be_same_as(c1)
@db.sqls.must_equal []
end
it "should disconnect connection if not valid" do
c1 = @db.synchronize{|c| c}
@db.sqls.must_equal []
c1.valid = false
@db.pool.connection_validation_timeout = -1
c2 = @db.synchronize{|c| c}
@db.sqls.must_equal ['SELECT NULL', 'disconnect']
c2.wont_be_same_as(c1)
end
it "should assume that exceptions raised during valid_connection mean the connection is not valid" do
c1 = @db.synchronize{|c| c}
@db.sqls.must_equal []
c1.valid = :exception
@db.pool.connection_validation_timeout = -1
proc{@db.synchronize{}}.must_raise RuntimeError
@db.sqls.must_equal ['SELECT NULL', 'disconnect']
c2 = @db.synchronize{|c| c}
c2.wont_be_same_as(c1)
end
it "should handle Database#disconnect calls while the connection is checked out" do
@db.synchronize{|c| @db.disconnect}
end
it "should handle disconnected connections" do
proc{@db.synchronize{|c| raise Sequel::DatabaseDisconnectError}}.must_raise Sequel::DatabaseDisconnectError
@db.sqls.must_equal ['disconnect']
end
it "should disconnect multiple connections repeatedly if they are not valid" do
q, q1 = Queue.new, Queue.new
c1 = nil
c2 = nil
@db.pool.connection_validation_timeout = -1
@db.synchronize do |c|
Thread.new do
@db.synchronize do |cc|
c2 = cc
end
q1.pop
q.push nil
end
q1.push nil
q.pop
c1 = c
end
c1.valid = false
c2.valid = false
c3 = @db.synchronize{|c| c}
@db.sqls.must_equal ['SELECT NULL', 'disconnect', 'SELECT NULL', 'disconnect']
c3.wont_be_same_as(c1)
c3.wont_be_same_as(c2)
end
it "should not leak connection references during disconnect" do
@db.synchronize{}
@db.pool.instance_variable_get(:@connection_timestamps).size.must_equal 1
@db.disconnect
@db.pool.instance_variable_get(:@connection_timestamps).size.must_equal 0
end
it "should not leak connection references" do
c1 = @db.synchronize do |c|
@db.pool.instance_variable_get(:@connection_timestamps).must_equal({})
c
end
@db.pool.instance_variable_get(:@connection_timestamps).must_include(c1)
c1.valid = false
@db.pool.connection_validation_timeout = -1
c2 = @db.synchronize do |c|
@db.pool.instance_variable_get(:@connection_timestamps).must_equal({})
c
end
c2.wont_be_same_as(c1)
@db.pool.instance_variable_get(:@connection_timestamps).wont_include(c1)
@db.pool.instance_variable_get(:@connection_timestamps).must_include(c2)
end
it "should handle case where determining validity requires a connection" do
def @db.valid_connection?(c) synchronize{}; true end
@db.pool.connection_validation_timeout = -1
c1 = @db.synchronize{|c| c}
@db.synchronize{|c| c}.must_be_same_as(c1)
end
end
threaded_connection_validator_specs = Module.new do
extend Minitest::Spec::DSL
it "should handle :connection_handling => :disconnect setting" do
@db = Sequel.mock(@db.opts.merge(:connection_handling => :disconnect))
@db.extend @m
@db.extension(:connection_validator)
@db.synchronize{}
@db.sqls.must_equal ['disconnect']
end
end
describe "Sequel::ConnectionValidator with single threaded pool" do
it "should raise an error if trying to load the connection_validator extension into a single connection pool" do
db = Sequel.mock(:test=>false, :pool_class=>:single)
proc{db.extension(:connection_validator)}.must_raise Sequel::Error
end
end
describe "Sequel::ConnectionValidator with sharded single threaded pool" do
it "should raise an error if trying to load the connection_validator extension into a single connection pool" do
db = Sequel.mock(:test=>false, :pool_class=>:sharded_single)
proc{db.extension(:connection_validator)}.must_raise Sequel::Error
end
end
describe "Sequel::ConnectionValidator with threaded pool" do
def db
Sequel.mock(:test=>false, :pool_class=>:threaded)
end
include connection_validator_specs
include threaded_connection_validator_specs
end
describe "Sequel::ConnectionValidator with sharded threaded pool" do
def db
Sequel.mock(:test=>false, :servers=>{}, :pool_class=>:sharded_threaded)
end
include connection_validator_specs
include threaded_connection_validator_specs
end
describe "Sequel::ConnectionValidator with timed_queue pool" do
def db
Sequel.mock(:test=>false, :pool_class=>:timed_queue)
end
include connection_validator_specs
end if RUBY_VERSION >= '3.2'
describe "Sequel::ConnectionValidator with sharded_timed_queue pool" do
def db
Sequel.mock(:test=>false, :pool_class=>:sharded_timed_queue)
end
include connection_validator_specs
end if RUBY_VERSION >= '3.2'
|