-
Notifications
You must be signed in to change notification settings - Fork 555
Close several unclosed clients in tests #848
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Close several unclosed clients in tests #848
Conversation
That same test I mentioned in the PR description is failing, but this time with Threads_connected increasing by 1 after opening a connection and closing it. Seems like this test is flaky on master.
It looks like the mysql server might not have synchronously closed the connection when receiving a the QUIT message. Otherwise, there must be something else connecting to the database in the middle of the test. Either way, the flaky test failure isn't caused by this PR. |
I can reproduce the flaky test failure locally using the following changes to make it happen more reliably diff --git a/spec/mysql2/client_spec.rb b/spec/mysql2/client_spec.rb
index dab4036..3390b60 100644
--- a/spec/mysql2/client_spec.rb
+++ b/spec/mysql2/client_spec.rb
@@ -171,12 +171,13 @@ RSpec.describe Mysql2::Client do
end
it "should terminate connections when calling close" do
- expect {
- Mysql2::Client.new(DatabaseCredentials['root']).close
- }.to_not change {
- @client.query("SHOW STATUS LIKE 'Aborted_%'").to_a +
+ 10_000.times do |i|
+ expect {
+ Mysql2::Client.new(DatabaseCredentials['root']).close
+ }.to_not change {
@client.query("SHOW STATUS LIKE 'Threads_connected'").to_a
- }
+ }
+ end
end
it "should not leave dangling connections after garbage collection" do |
if (!skip_check)
result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ?
1 : 0); so it doesn't wait for the response to the quit command, which is what makes this race condition feasible. I'll open another PR to address this, since this PR is dealing with a different reason why that test can fail. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. This feels like a lot of litter in the specs themselves, though. Perhaps there's an abstraction we could introduce to both establish a connection and add it to a list of connections in use, then let an after { @clients.each &:close }
clean up.
before { @clients = [] }
after { @clients.each &:close }
def connect(*args)
Mysql2::Client.new(*args).tap { |c| @clients << c }
end
or a block form to close right away:
def connect(*args)
client = Mysql2::Client.new(*args)
if block_given?
begin
yield client
ensure
client.close
end
else
@clients << client
end
client
end
spec/mysql2/client_spec.rb
Outdated
end | ||
|
||
it "should not raise an exception without default group" do | ||
@client.close |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps clearer to have these tests simply not set @client
. Let them be responsible for creating and closing this separate client instance.
spec/mysql2/statement_spec.rb
Outdated
|
||
RSpec.describe Mysql2::Statement do | ||
before :each do | ||
@client.close |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels odd to close the client before instantiating it. Should this be in an after { … }
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spec/spec_helper.rb already created a client. This one creates another one with a change in its configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, just feels odd to start a test by cleaning up after setup.
spec/mysql2/error_spec.rb
Outdated
end | ||
|
||
after(:each) { client.close } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Note that after { … }
is equivalent to after(:each) { … }
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't know that. All the other test files use after(:each)
, so should I keep it as after(:each)
for consistency?
Introduced an abstraction as you suggested for creating clients, which avoid the problems with spec files wanting to override |
Well this is awesome! |
The test at line 265 is much worse now - could you take another look to be sure that you're exercising that a parent processes's connection is not garbage collected and closed by a child process that inherits the connection?
|
e9d9f8e
to
cdd371b
Compare
cdd371b
to
932c270
Compare
Problem
I noticed some flaky test failures that seem like they were related to unclosed connections, like this test failure in https://travis-ci.org/brianmario/mysql2/jobs/235381348
Notice that the test failed because an extra connection was closed. Also, there were 6 Threads_connected at the start of the test, when there should only be one for
@client
.Solution
I went through the tests looking for unclosed clients and added a
close
call for the ones I found.