javah2dropwizardtomcat-jdbc

How to prevent validationQueryTimeout affects other SQL statements in H2


I'm using Dropwizard 1.3.12 backed with a H2 1.4.199 database. In the database config I have

database:
  ...
  validationQuery: "/* MyService Health Check */ SELECT 1"
  validationQueryTimeout: 3s
  ...

The problem I am running into is that the timeout of 3s also propagates to the real DB queries done in the Dropwizard application. Some DB queries are getting interrupted by this timeout. I rather have them wait a bit longer.

From my understanding validationQueryTimeout should only control the timeout of the validationQuery. The real DB requests done inside the application should not be affected by this. I have tried removing validationQueryTimeout and that seems to do the trick and remove the query timeout. Doing this I see as a last resort workaround since I think it makes sense to have a timeout on the query that validates that the connection is up and running when fetching a connection from the connection pool.

I tried using Postgresql and there the validationQueryTimeout doesn't seem to affect other DB queries.

I have done some debugging and think I found the reason, but I lack a good workaround.

When doing the validation tomcat-jdbc sets query timeout on the the validation statement. https://github.com/apache/tomcat/blob/9.0.16/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java#L536-L544

            stmt = connection.createStatement();

            int validationQueryTimeout = poolProperties.getValidationQueryTimeout();
            if (validationQueryTimeout > 0) {
                stmt.setQueryTimeout(validationQueryTimeout);
            }

            stmt.execute(query);
            stmt.close();

The big problem here is that H2 keeps the timeout on a connection level instead of a statement level. https://github.com/h2database/h2database/blob/version-1.4.199/h2/src/main/org/h2/jdbc/JdbcStatement.java#L695-L717

    /**
     * Sets the current query timeout in seconds.
     * Changing the value will affect all statements of this connection.
     * This method does not commit a transaction,
     * and rolling back a transaction does not affect this setting.
     *
     * @param seconds the timeout in seconds - 0 means no timeout, values
     *        smaller 0 will throw an exception
     * @throws SQLException if this object is closed
     */
    @Override
    public void setQueryTimeout(int seconds) throws SQLException {
        try {
            debugCodeCall("setQueryTimeout", seconds);
            checkClosed();
            if (seconds < 0) {
                throw DbException.getInvalidValueException("seconds", seconds);
            }
            conn.setQueryTimeout(seconds);
        } catch (Exception e) {
            throw logAndConvert(e);
        }
    }

Is there something I can do? Or is a fix needed in either h2 or tomcat-jdbc? What I can think of is to get the current query timeout value in tomcat-jdbc before setting it for the validation query and then setting it back to that value after running the validation query.


Solution

  • You're right, it is a known limitation of H2 (see here and here)

    I guess you could set the timeout to 0 to avoid the faulty behavior of H2. Then implement a JdbcInterceptor that checks for the timeout after the statement is executed. However, it will not abort the statement when it breaches the timeout will only report afterward the exception :-(