More robust transient error detection.

This commit is contained in:
Michael Bayne
2026-02-20 01:27:08 -08:00
parent 4827bb48a5
commit 15d31793d9
2 changed files with 55 additions and 2 deletions
@@ -29,10 +29,29 @@ public class MySQLLiaison extends BaseLiaison
@Override // from DatabaseLiaison
public boolean isTransientException (SQLException sqe)
{
// SQL state class "08" means connection exception per the SQL standard; this covers
// lost connections, timeouts, broken pipes, etc. without needing to match message text
String sqlState = sqe.getSQLState();
if (sqlState != null && sqlState.startsWith("08")) {
return true;
}
// also check the causal chain for connection exceptions (MySQL Connector/J's
// CommunicationsException is a SQLNonTransientConnectionException which has state "08")
Throwable cause = sqe.getCause();
if (cause instanceof SQLException) {
String causeState = ((SQLException)cause).getSQLState();
if (causeState != null && causeState.startsWith("08")) {
return true;
}
}
// fall back to message matching for older drivers or unexpected exception formats
String msg = sqe.getMessage();
return (msg != null && (msg.indexOf("Lost connection") != -1 ||
msg.indexOf("link failure") != -1 ||
msg.indexOf("Broken pipe") != -1 ||
msg.indexOf("disconnected by the server") != -1 ||
msg.indexOf("The last packet successfully received") != -1));
}
@@ -29,10 +29,44 @@ public class PostgreSQLLiaison extends BaseLiaison
// from DatabaseLiaison
public boolean isTransientException (SQLException sqe)
{
// TODO: Add more error messages here as we encounter them.
if (isTransientState(sqe.getSQLState())) {
return true;
}
// also check the causal chain for connection exceptions
Throwable cause = sqe.getCause();
if (cause instanceof SQLException && isTransientState(((SQLException)cause).getSQLState())) {
return true;
}
// fall back to message matching for older drivers or unexpected exception formats
String msg = sqe.getMessage();
return (msg != null &&
msg.indexOf("An I/O error occured while sending to the backend") != -1);
(msg.contains("An I/O error") ||
msg.contains("Connection reset") ||
msg.contains("Connection refused") ||
msg.contains("Connection timed out") ||
msg.contains("Broken pipe") ||
msg.contains("unexpected EOF on client connection") ||
msg.contains("terminating connection due to administrator command") ||
msg.contains("This connection has been closed") ||
msg.contains("No more data to read from socket")));
}
private static boolean isTransientState (String sqlState) {
if (sqlState == null) return false;
// SQL state class "08" means connection exception (08000 connection_exception,
// 08001 sqlclient_unable_to_establish_sqlconnection, 08003 connection_does_not_exist,
// 08006 connection_failure, 08P01 protocol_violation)
if (sqlState.startsWith("08")) return true;
// Class "57P" covers operator intervention: 57P01 admin_shutdown,
// 57P02 crash_shutdown, 57P03 cannot_connect_now
if (sqlState.startsWith("57P")) return true;
// Class "53" covers insufficient resources: 53000 insufficient_resources,
// 53100 disk_full, 53200 out_of_memory, 53300 too_many_connections
if (sqlState.startsWith("53")) return true;
// 40001 serialization_failure, 40P01 deadlock_detected
if ("40001".equals(sqlState) || "40P01".equals(sqlState)) return true;
return false;
}
@Override