Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[CONJ-419] failover execution when master is demote without restart
  • Loading branch information
rusher committed Feb 9, 2017
1 parent 7e4650d commit 2aa57ba
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 28 deletions.
68 changes: 58 additions & 10 deletions src/main/java/org/mariadb/jdbc/internal/failover/FailoverProxy.java
Expand Up @@ -188,25 +188,60 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl

default:
}

return executeInvocation(method, args, false);

}


private Object executeInvocation(Method method, Object[] args, boolean isSecondExecution) throws Throwable{

try {
return listener.invoke(method, args);
} catch (InvocationTargetException e) {
if (e.getTargetException() != null) {
if (e.getTargetException() instanceof QueryException) {

QueryException queryException = (QueryException) e.getTargetException();
addExceptionHost(queryException, listener.getCurrentProtocol());
Protocol protocol = listener.getCurrentProtocol();

addHostInformationToException(queryException, protocol);

if (hasToHandleFailover(queryException)) {
return handleFailOver(queryException, method, args, listener.getCurrentProtocol());
return handleFailOver(queryException, method, args, protocol);
}

//error is "The MySQL server is running with the %s option so it cannot execute this statement"
//checking that server was master has not been demote to slave without resetting connections
if (queryException.getErrorCode() == 1290
&& listener.getCurrentProtocol() != null
&& listener.getCurrentProtocol().isMasterConnection()
&& !listener.getCurrentProtocol().checkIfMaster()) {
//connection state has changed !
&& !isSecondExecution
&& protocol != null
&& protocol.isMasterConnection()
&& !protocol.checkIfMaster()) {

boolean inTransaction = protocol.inTransaction();
boolean isReconnected;

//connection state has changed, master connection is now read-only
//reconnect to master, to re-execute command if wasn't in a transaction since
//we are sure has not been executed.

//reconnection
lock.lock();
try {
protocol.close();
isReconnected = listener.primaryFail(null, null).isReconnected;
} finally {
lock.unlock();
}

//relaunch command
if (isReconnected && !inTransaction) {
return executeInvocation(method, args, true);
}

//throw exception if not reconnected, or was in a transaction
return handleFailOver(queryException, method, args, listener.getCurrentProtocol());

}
}
throw e.getTargetException();
Expand All @@ -215,7 +250,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
}
}


/**
* After a connection exception, launch failover.
*
Expand Down Expand Up @@ -275,7 +309,21 @@ public Listener getListener() {
return listener;
}

private static void addExceptionHost(QueryException exception, Protocol protocol) {
exception.setMessage(exception.getMessage() + "\non " + protocol.getHostAddress().toString() + ",master=" + protocol.isMasterConnection() );
/**
* Add Host information ("on HostAddress...") to exception.
*
* example :
* <p>
* java.sql.SQLException: (conn:603) Cannot execute statement in a READ ONLY transaction.<br/>
* Query is : INSERT INTO TableX VALUES (21)<br/>
* on HostAddress{host='mydb.example.com', port=3306},master=true</p>
*
* @param exception current exception
* @param protocol protocol to have hostname
*/
private static void addHostInformationToException(QueryException exception, Protocol protocol) {
if (protocol != null) {
exception.setMessage(exception.getMessage() + "\non " + protocol.getHostAddress().toString() + ",master=" + protocol.isMasterConnection() );
}
}
}
Expand Up @@ -224,30 +224,26 @@ public void preExecute() throws QueryException {
*/
public void checkWaitingConnection() throws QueryException {
if (isSecondaryHostFail()) {
Protocol waitingProtocol = waitNewSecondaryProtocol.getAndSet(null);
if (waitingProtocol != null) {
proxy.lock.lock();
try {
if (pingSecondaryProtocol(waitingProtocol)) {
lockAndSwitchSecondary(waitingProtocol);
}
} finally {
proxy.lock.unlock();
proxy.lock.lock();
try {
Protocol waitingProtocol = waitNewSecondaryProtocol.getAndSet(null);
if (waitingProtocol != null && pingSecondaryProtocol(waitingProtocol)) {
lockAndSwitchSecondary(waitingProtocol);
}
} finally {
proxy.lock.unlock();
}
}

if (isMasterHostFail()) {
Protocol waitingProtocol = waitNewMasterProtocol.getAndSet(null);
if (waitingProtocol != null) {
proxy.lock.lock();
try {
if (pingMasterProtocol(waitingProtocol)) {
lockAndSwitchMaster(waitingProtocol);
}
} finally {
proxy.lock.unlock();
proxy.lock.lock();
try {
Protocol waitingProtocol = waitNewMasterProtocol.getAndSet(null);
if (waitingProtocol != null && pingMasterProtocol(waitingProtocol)) {
lockAndSwitchMaster(waitingProtocol);
}
} finally {
proxy.lock.unlock();
}
}
}
Expand Down Expand Up @@ -348,6 +344,8 @@ public void foundActiveMaster(Protocol newMasterProtocol) {
/**
* Use the parameter newMasterProtocol as new current master connection.
*
* <i>Lock must be set</i>
*
* @param newMasterProtocol new master connection
* @throws ReconnectDuringTransactionException if there was an active transaction.
*/
Expand Down

0 comments on commit 2aa57ba

Please sign in to comment.