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

HikariPool::createTimeoutException - SQLTransientConnectionException copies SQLState from cause, but not ErrorCode #2222

Open
the-vj opened this issue Jul 29, 2024 · 0 comments

Comments

@the-vj
Copy link
Contributor

the-vj commented Jul 29, 2024

The createTimeoutException() method of HikariPool class wraps SQLException like so:

if (originalException instanceof SQLException) {
sqlState = ((SQLException) originalException).getSQLState();
}
final var connectionException = new SQLTransientConnectionException(
poolName + " - Connection is not available, request timed out after " + elapsedMillis(startTime) + "ms " +
"(total=" + getTotalConnections() + ", active=" + getActiveConnections() + ", idle=" + getIdleConnections() + ", waiting=" + getThreadsAwaitingConnection() + ")",
sqlState, originalException);

It seems that the intent of the wrapping code is to preserve the main attributes of the cause - at least SQLState does get copied... however, the error code is not copied and therefore it will be set to 0 in the new SQLTransientConnectionException.

This creates a peculiar situation when dealing with specific error handling routines: let's have an example of business logic for "detect invalid password used by a connection and recover from it by fetching new credentials". The focus here is on "detection", not the details of "recovery".

  1. PostgreSQL: The situation can be determined from SQLState - which is present in the wrapped exception.

  2. Oracle: The situation can be determined from ErrorCode - which is not present in the wrapped exception.

See example of a stack trace:

Caused by: java.sql.SQLTransientConnectionException: Connection is not available, request timed out after 10000ms (total=0, active=0, idle=0, waiting=0)
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:686)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:179)
....
Caused by: java.sql.SQLException: ORA-01017: invalid credential or not authorized; logon denied
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:709)
at oracle.jdbc.driver.T4CTTIoer11.processError(T4CTTIoer11.java:604)

Business logic can react to ErrorCode (1017 is Oracle's indication of connection having invalid credentials). But our code cannot be simply testing for SQLException::getErrorCode() - SQLTransientConnectionException is also SQLException.

And now we can see that the correct error code is only in the cause (while the correct SQLState is in both) => depending on whether or not the original SQLException got wrapped by Hikari (this depends on various scenarios), client needs to perform analysis of the entire exception chain to consistently figure out this is an invalid password situation...

While the above is of course possible, now every client needs to deal with additional dimensions of the problem:

  • understand behavior of different DB vendors (sure, we cannot do much about that)
  • process different depth of the exception chain (all exceptions are instances of SQLException) and find the original information
  • follow Hikari's implementation details (which may change in different releases) to stay on top of things

A solution to this would be elementary:

add one line to extract also the error code here:

sqlState = ((SQLException) originalException).getSQLState();

and use a constructor where also the error code is passed here:

It is possible that this could solve also several other open issues...

What do you think?

@the-vj the-vj changed the title HikariPool::createTimeoutException - SQLTransientConnectionException does not copy ErrorCode of cause HikariPool::createTimeoutException - SQLTransientConnectionException copies SQLState from cause, but not ErrorCode Jul 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant