Skip to content
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

Fix two sources of SQL statement leaks. #13259

Merged
merged 3 commits into from
Oct 25, 2022
Merged

Conversation

gianm
Copy link
Contributor

@gianm gianm commented Oct 25, 2022

  1. SqlTaskResource and DruidJdbcResultSet leaked statements 100% of
    the time, since they call stmt.plan(), which adds statements to
    SqlLifecycleManager, and they do not explicitly remove them.

  2. SqlResource leaked statements if yielder.close() threw an exception.
    (And also would not emit metrics, since in that case it failed to
    call stmt.close as well.)

Item (2) has been around for a while. Item (1) is a regression in 24.0, introduced in #12845. That patch moved SqlLifecycleManager registration from SqlResource to DirectStatement, but kept de-registration in SqlResource only.

1) SqlTaskResource and DruidJdbcResultSet leaked statements 100% of the
   time, since they call stmt.plan(), which adds statements to
   SqlLifecycleManager, and they do not explicitly remove them.

2) SqlResource leaked statements if yielder.close() threw an exception.
   (And also would not emit metrics, since in that case it failed to
   call stmt.close as well.)
@gianm gianm added this to the 24.0.1 milestone Oct 25, 2022
@gianm
Copy link
Contributor Author

gianm commented Oct 25, 2022

I tested this patch by running DruidAvaticaProtobufHandlerTest#testConcurrentQueries in a loop. Prior to this fix, it would fail with OOME after 9–10 iterations. After this fix, it ran for 100 iterations before I canceled it. I checked a heap dump and did not see any statement objects beyond the ones that were currently executing.

@@ -301,6 +302,7 @@ public void cancel()
public void close()
{
if (state != State.START && state != State.CLOSED) {
// super.close calls closeQuietly, which removes us from the sqlLifecycleManager.
Copy link
Contributor

@kfaraz kfaraz Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: should we rely on close/closeQuietly to do the right thing?
Since we are anyway doing an explicit remove in closeWithError, I suppose we could do the same here too. Then we can just not override closeQuietly.

Alternatively,
I think closeWithQuietly, close, closeWithError should all internally call a cleanup method. This new method should be abstract in AbstractStatement so that all impls provide a concrete cleanup method.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now that I read this again, I see that closeQuietly is called by both close and closeWithError. So we can do the second thing you suggested: only clean up the statement there. I just pushed up another commit with this change.

Copy link
Contributor

@kfaraz kfaraz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@gianm gianm merged commit 2b0d873 into apache:master Oct 25, 2022
@gianm gianm deleted the fix-jdbc-rsrc branch October 25, 2022 16:31
@kfaraz kfaraz modified the milestones: 24.0.1, 25.0 Feb 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants