HikariCP
HikariCP copied to clipboard
HikariPool::createTimeoutException - SQLTransientConnectionException copies SQLState from cause, but not ErrorCode
The createTimeoutException() method of HikariPool class wraps SQLException like so:
https://github.com/brettwooldridge/HikariCP/blob/0a6ccdb334b2ecde25ae090034669d534736a0de/src/main/java/com/zaxxer/hikari/pool/HikariPool.java#L681-L687
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".
-
PostgreSQL: The situation can be determined from SQLState - which is present in the wrapped exception.
-
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: https://github.com/brettwooldridge/HikariCP/blob/0a6ccdb334b2ecde25ae090034669d534736a0de/src/main/java/com/zaxxer/hikari/pool/HikariPool.java#L682
and use a constructor where also the error code is passed here: https://github.com/brettwooldridge/HikariCP/blob/0a6ccdb334b2ecde25ae090034669d534736a0de/src/main/java/com/zaxxer/hikari/pool/HikariPool.java#L687
It is possible that this could solve also several other open issues...
What do you think?