Details
-
Bug
-
Status: Closed (View Workflow)
-
Major
-
Resolution: Fixed
-
2.4.0
-
None
Description
Happens when running in sequential HA mode with Client Prepared Statement in a transaction.
Create prepared statement and bind parameter to non-serializable object:
try(PreparedStatement statement = connection.prepareStatement("SELECT * FROM test WHERE column = ?")) |
{
|
statement.setObject(1, new Object()); |
statement.execute();
|
}
|
Executing such query and writing parameters into the OutputStream (i.e. when calling "ComQuery.sendSubCmd"), results in "java.io.NotSerializableException: java.lang.Object".
Stack trace:
java.io.NotSerializableException: java.lang.Object
|
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
|
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
|
at org.mariadb.jdbc.internal.com.send.parameters.SerializableParameter.writeObjectToBytes(SerializableParameter.java:91)
|
at org.mariadb.jdbc.internal.com.send.parameters.SerializableParameter.writeTo(SerializableParameter.java:80)
|
at org.mariadb.jdbc.internal.com.send.ComQuery.sendSubCmd(ComQuery.java:85)
|
at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.executeQuery(AbstractQueryProtocol.java:285)
|
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
|
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
|
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
|
at java.lang.reflect.Method.invoke(Method.java:498)
|
at org.mariadb.jdbc.internal.failover.AbstractMastersListener.invoke(AbstractMastersListener.java:398)
|
at org.mariadb.jdbc.internal.failover.FailoverProxy.executeInvocation(FailoverProxy.java:278)
|
at org.mariadb.jdbc.internal.failover.FailoverProxy.invoke(FailoverProxy.java:270)
|
at com.sun.proxy.$Proxy8.executeQuery(Unknown Source)
|
at org.mariadb.jdbc.ClientSidePreparedStatement.executeInternal(ClientSidePreparedStatement.java:221)
|
at org.mariadb.jdbc.ClientSidePreparedStatement.execute(ClientSidePreparedStatement.java:157)
|
at org.mariadb.jdbc.ClientSidePreparedStatement.executeUpdate(ClientSidePreparedStatement.java:192)
|
What is happening:
- "AbstractQueryProtocol.handleIoException" method will mark the protocol as not connected, via: "connected = false;"
- FailoverProxy.executeInvocation catches the exception and enters the block:
if (hasToHandleFailover(queryException)) {
return handleFailOver(queryException, method, args, protocol);
}
- MastersFailoverLustener.primaryFail will reconnect with a new protocol, but because the protocol returns false on "currentProtocol.isConnected()", existing protocol is not closed.
@Override
public HandleErrorResult primaryFail(Method method, Object[] args, boolean killCmd) {
boolean alreadyClosed = !currentProtocol.isConnected();
boolean inTransaction = currentProtocol != null && currentProtocol.inTransaction();
if (currentProtocol.isConnected()) {
currentProtocol.close();
}
try {
reconnectFailedConnection(new SearchFilter(true, false));
- During reconnection in "reconnectFailedConnection" method, "MastersFailoverListener.foundActiveMaster" method is called, which override current protocol's reference, without closing existing one, as "currentProtocol.isClosed()" returns true
if (currentProtocol != null && !currentProtocol.isClosed()) {
currentProtocol.close();
}
currentProtocol = protocol;
Results in hanging open connection to database every time you run into above scenario. Calling "close" on the Connection, does not close the old protocol.
I would expect that "AbstractQueryProtocol.handleIoException" method to not assume that IOExceptions is due to connection being broken and actually close the protocol and not just flag it as "closed".