-
Notifications
You must be signed in to change notification settings - Fork 819
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: isValid() should not wait longer than network timeout #2022
Conversation
Another grey area in the JDBC spec. isValid is in seconds There are also some interesting notes on setNetworkTimeout It also states that the networkTimeout overrides all other timeouts. |
setNetworkTimeout(null, timeout * 1000); | ||
// change network timeout only if the new value is more restrictive than the old one | ||
// (zero means no timeout) | ||
if (newNetworkTimeout != 0 && (oldNetworkTimeout == 0 || oldNetworkTimeout > newNetworkTimeout)) { |
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.
Instead of using the word restrictive can we change the comment to "the new value is less than the current"
Also it's easier for me to read new < old
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.
"the new value is less than the current" is not exactly accurate because of the zero-timeout case, so I tried to come up with a more "creative" wording :) OK, I'll change that
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.
just put a note that 0 is really no timeout.
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.
changed
462776f
to
e44872f
Compare
@Powerrr sorry I should have mentioned this earlier. Can you change this PR to be against https://github.com/pgjdbc/pgjdbc/tree/release/42.2. I will then cherry pick it into master. Also any ideas on how to test this ? |
e44872f
to
b776d38
Compare
I added a test, based on a similar test I found. And another thing: I had to disable ssl in my test, because apparently closing an ssl socket (after the timeout happens) invokes one more read from the socket, which adds an extra delay (since the socket is dead) and the test fails. This happens only on somewhat recent versions of java, maybe 11.0.something and newer and is probably worth looking into in a separate issue. |
b776d38
to
7df403d
Compare
I need to think about whether to push this into release 42.2.x as this will be a change. @vlsi @sehrope thoughts ? Personally I think we are fixing a regression, however this will have "interesting" side effects. I'm also curious if we are adhering to the spec regarding killing all existing queries when the network times out. |
I think it makes sense and follows the spec for the network timeout to supersede any parameter in the The JDBC docs say as much. Check out the last line:
I'm okay with this fix going into 42.2.x as a fix. In the real world I don't see how this would break anything. All user SQL is going to be slower than the empty command used by I took a peek at the PR and there's a potential integer overflow if a nonsensical huge timeout is specified. Though the existing code had the same issue. It's the JDBC's fault for having Could fix both the overflow and this actual timeout issue with: diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
index 7250cc1f..718ab525 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
@@ -1412,7 +1412,11 @@ public class PgConnection implements BaseConnection {
try {
int savedNetworkTimeOut = getNetworkTimeout();
try {
- setNetworkTimeout(null, timeout * 1000);
+ // change network timeout only if the new value is less than the current
+ // (zero means infinite timeout)
+ if (timeout != 0 && (savedNetworkTimeOut == 0 || timeout * 1000L < savedNetworkTimeOut)) {
+ setNetworkTimeout(null, (int) Math.min(timeout * 1000L, Integer.MAX_VALUE));
+ }
if (replicationConnection) {
Statement statement = createStatement();
statement.execute("IDENTIFY_SYSTEM"); This resets the socket timeout on every call but the old code did too. Should also add |
7df403d
to
c896443
Compare
I fixed the integer overflow and rebased the code on top of release/42.2. There is one small problem: two of my tests won't pass on 42.2, because #1943 wasn't backported there, so the driver tries to cancel the validation query, which takes additional time. |
c896443
to
e61fb51
Compare
I think the fix itself is good. The new test is a bit shaky though. I rewrote the test to use a new "StrangeProxyServer" that selectively stops forwarding older client traffic. This new test works even with the old cancellation code as the traffic for the cancellation request is allowed to go through. Branch rebased on release/42.2 with the new test is here: https://github.com/sehrope/pgjdbc/commits/fix-is-valid-timeout Here's the dif vs 42.2 with the helpers: https://github.com/pgjdbc/pgjdbc/compare/release/42.2..sehrope:fix-is-valid-timeout If that looks good and passes CI then we'll get that merged in and can cherry-pick it for master as well. |
Some time ago PR #1557 was merged into master and it introduced a kind of regression:
setNetworkTimeout()
with some small value (say, 500 ms) and then calledisValid()
with a larger timeout (say, 2 seconds) on an unresponsive connection,isValid()
would respect the smaller timeout and would wait for only 500 ms;isValid()
would override the network timeout and wait for 2 seconds.This is a problem for me, because at the company where I work we often use timeouts smaller than 1 second. We use HikariCP and it allows validation timeouts as small as 250 ms, which is implemented by smth. like
conn.setNetworkTimeout(250); return conn.isValid(1);
(see https://github.com/brettwooldridge/HikariCP/blob/HikariCP-3.4.5/src/main/java/com/zaxxer/hikari/pool/PoolBase.java#L156-L162).And if we update to the latest PgJDBC version we'll be limited to validation timeouts that are multiples of 1 second.
As a solution I suggest to change
isValid
method so that it overrides the network timeout only in those cases where the new timeout is smaller than the one already set. So, for example, if you callsetNetworkTimeout(500)
and then callisValid(2)
, it would wait for at most 500 ms. And if you callsetNetworkTimeout(3000)
and then callisValid(1)
, it would wait for at most 1 second.I wrote some code to show the proposed solution. I haven't added any tests, but I think I could try to (if this PR is otherwise considered good).
All Submissions:
Changes to Existing Features: