Skip to content

Commit

Permalink
Make connection_validator extension handle case where Database#valid_…
Browse files Browse the repository at this point in the history
…connection? raises an exception

This shouldn't happen, but if it did, it resulted in a
corruption of the connection pool, with the connection allocated
to the current thread and never being checked back in.

Fix previous CHANGELOG entry while here.
  • Loading branch information
jeremyevans committed Jan 31, 2025
1 parent a961fcd commit b8e4e2a
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 11 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
=== master

* Support :avoid_connection_compare_by_identity Database option to work around bugs in other libraries or the ruby implementation (jeremyevans)
* Make connection_validator extension handle case where Database#valid_connection? raises an exception (jeremyevans)

* Support :compare_connections_by_identity Database option to work around bugs in other libraries or the ruby implementation (jeremyevans)

* Make mysql2 adapter handle invalid statement handles when closing prepared statements (jeremyevans)

Expand Down
25 changes: 15 additions & 10 deletions lib/sequel/extensions/connection_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,23 @@ def acquire(*a)
1.times do
if (conn = super) &&
(timer = sync{@connection_timestamps.delete(conn)}) &&
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout &&
!db.valid_connection?(conn)
Sequel.elapsed_seconds_since(timer) > @connection_validation_timeout

case pool_type
when :sharded_threaded, :sharded_timed_queue
sync{@allocated[a.last].delete(Sequel.current)}
else
sync{@allocated.delete(Sequel.current)}
end
begin
valid = db.valid_connection?(conn)
ensure
unless valid
case pool_type
when :sharded_threaded, :sharded_timed_queue
sync{@allocated[a.last].delete(Sequel.current)}
else
sync{@allocated.delete(Sequel.current)}
end

disconnect_connection(conn)
redo
disconnect_connection(conn)
redo
end
end
end
end

Expand Down
11 changes: 11 additions & 0 deletions spec/extensions/connection_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def disconnect_connection(conn)
end
def valid_connection?(conn)
super
raise if conn.valid == :exception
conn.valid
end
def connect(server)
Expand Down Expand Up @@ -54,6 +55,16 @@ def connect(server)
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
c2 = @db.synchronize{|c| c}
@db.sqls.must_equal ['SELECT NULL', 'disconnect']
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
Expand Down

0 comments on commit b8e4e2a

Please sign in to comment.