-
-
Notifications
You must be signed in to change notification settings - Fork 336
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
Handle: unconditionally close Connection #2451
Conversation
This might need a bit more thinking,. It is one of these "damned if I do, damned if I don't" situation because of the plethora of drivers. The check exists because there are a number of drivers that throw a "ConnectionAlreadyClosedException" if calling Connection#close on a closed connection (yes, it's against the spec, but but ...). Making the close unconditional might pop up more errors in more popular paths. The isAlive check was ported over in the 3.35.0 rewrite from the previous code. I can take a closer look at the change over the weekend when I am no longer in an airport departure lounge, please hold off merging till then. The bug report worries me. It implies that there are connection pools that report a connection "dead" (IAW closed) while still holding onto the actual database driver connection. This would be a pool bug. |
No urgency to merge this. Throwing a I think it is understandable that if the pool leases a connection, it expects it to be returned, even if the database server asynchronously-closes the connection. Maybe there is a pool bug but I am not as sure as you are. |
If drivers violating the spec is a problem, we can catch and ignore the thrown exception. Checking the connection |
Hi, Regarding:
vs
I think that the latter is more accurate for my bug report. In my case, the connection was never returned to the pool. The pool can have "health" checks while creating, borrowing or returning object to the pool but in this case this would not work since connection was closed after it was acquired from the pool and before |
95c6013
to
10bac09
Compare
65f39ad
to
9c91ade
Compare
Yeah, but by doing so, we now penalize all the code that behaves correctly (reporting the connection as dead when it is dead). Catching the exception is expensive. This seems to be a case where we should just have a config switch. If the underlying pool/thing requires an unconditional close, you can set a flag and we force that. |
Thank you for the explanation but that is then a pool bug. We call "checkConnectionLive()" which needs to return Is there a specific pool library that shows that behavior? (please don't say "c3p0"). |
@@ -362,7 +363,16 @@ public Handle open() { | |||
} | |||
LOG.trace("Jdbi [{}] obtain handle [{}] in {}ms", this, h, MILLISECONDS.convert(stop - start, NANOSECONDS)); | |||
return h; | |||
} catch (SQLException e) { | |||
} catch (Exception e) { |
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.
this makes me super-uncomfortable. There is a single bug report about a problem with connection closing and this is a change at one of the most fundamental pieces of Jdbi, affecting the most fundamental path in the whole code base which is probably executed a billion times a day all over the java ecosystem without any problems.
Why touch this and if touching it, there needs to be a bunch of tests that prove that this low level code (this replaces one line of straightforward catch and rethrow with a bunch of manual resource manipulations) actually is an improvement. I don't believe it is.
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 spend a lot of time and wrote a lot of tests in the 3.35.0 cycle to prove that the new code not only behaves better but does not leak resources anymore. I don't think introducing this manual piece-by-piece resource releasing and bypassing the mechanism that is used everywhere else in post-3.35.0 code is the right thing to do.
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.
Hi @hgschmie
I totally understand your concerns.
There is a single bug report about a problem with connection closing
This is true, it is a single bug report and it was originally about connection closing. However yesterday I have updated the bug report because it turns out that the problem that I found is more generic and case when db connection was closed can be one of the "implementation" of this issue.
The pool has basically two sets of methods,
one for acquiring the object and second one for returning/destroying the object when it is no longer needed. Both methods are called by pool client which in this case is JDBI. It is the pool client who knows when to return the object back to the pool, so its up to the caller (JDBI i this case) to make sure that object is always return back to the pool right? At lest to make sure that proper API is called. Forgive me but I can not say that this is a pool bug, but maybe I'm missing something.
The org.jdbi.v3.core.Jdbi::open()
gets object from the pool. If there will be an exception thrown anywhere in org.jdbi.v3.core.Jdbi::open()
method, after the connection was acquired from pool, the connection object will not be returned back to the pool.
I would say that org.jdbi.v3.core.Jdbi::open()
should have exception handling logic which will guaranty that connection object will be properly returned to the pool in case of exception. Currently it is not the case.
The specific use cases for connection close:
-
The connection is closed from the client side, as described in this bug report.
In this case it turns out that exception will be thrown by this line in Handle's constructor.
this.transactionHandler = transactionHandler.specialize(this); so even before registering any Cleanable objects
In this case, since exception was thrown from Handle constructor, there is no Handle instance to close/clean at the end. -
The second use case is where the connection is closed from server side, for example Data Base restart.
In our system we are using Jdbi's PostgresPlugin. In org.jdbi.v3.core.Jdbi::open() method, after Handle object is created there is this call:
for (JdbiPlugin p : plugins) {
h = p.customizeHandle(h);
}
This will throw an exception for PostgresPlugin.
In result the open() method will not return any Handle reference so no Handle will be closed/clean up hence connection object will not be returned to the pool. The stack trace for this case -
JdbiTrace.txt
To reproduce this, please pull my repository (I added PostgresPlugin) and simply restart Postgresql container on a break point just after where connection object is acquired from the pool.
However this problem will happen in case of any exception thrown in Jdbi::open() method. The connection issue is only one example (that I could find/reproduce on a real system).
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 added test cases to show this behavior in a unit test that we can commit to the repo.
9c91ade
to
473b57a
Compare
Hi @hgschmie
Not sure if I can agree with that this is pool bug. Originally bug report was about connection closing, but after further investigation I believe that there is an issue with exception handling in
|
I think this is the other way around: the current code penalizes all code that behaves correctly, because calling Throwing an exception on duplicate close is forbidden by spec, so it will not happen, so it has no cost. Unless there's a specific case of a driver that incorrectly does this.
If we have a specific database where this is needed, we can add it. I don't think we should pre-emptively add configuration in contradiction to the JDBC spec without a specific reason. That just adds complexity. I agree with Kristoff that we have a potential leak in |
473b57a
to
11bdacf
Compare
The changes you pushed over mine undoes the cleanup of resources. So I restored that. I wrote two tests: one to show the leak when customization fails, and one to show the leak when isClosed throws. Both tests fail in master today, and pass with this fix. |
11bdacf
to
231e6b8
Compare
Right now, we check if a connection is closed before closing it. Also, if a plugin fails to customize a Handle, the connection can leak However, this isn't good enough: even if the server closed the connection, we might have borrowed this from a pool, which needs to know we've returned it. Connection.close is specified to no-op if called more than once, so it's safe to just do it regardless. Fixes #2446
231e6b8
to
c1fb9fd
Compare
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
closed in favor of #2458 |
Right now, we check if a connection is closed before closing it.
However, this isn't good enough: even if the server closed the connection, we might have borrowed this from a pool, which needs to know we've returned it.
Connection.close is specified to no-op if called more than once, so it's safe to just do it regardless.
Fixes #2446