mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-15 05:41:51 +08:00
Retry on Deadlock to Transaction
Recommended behavior for MySQL deadlock is to retry the transaction. Added code that performs 3 attempts with recursion if deadlocks occur. Affects issues: - Fixed #1212
This commit is contained in:
parent
8749d12128
commit
2970b6590e
@ -36,6 +36,8 @@ public abstract class Transaction {
|
|||||||
|
|
||||||
// SQLite version on 1.8.8 does not support savepoints, see createSavePoint() method
|
// SQLite version on 1.8.8 does not support savepoints, see createSavePoint() method
|
||||||
private static final AtomicBoolean SUPPORTS_SAVE_POINTS = new AtomicBoolean(true);
|
private static final AtomicBoolean SUPPORTS_SAVE_POINTS = new AtomicBoolean(true);
|
||||||
|
// Limit for Deadlock attempts.
|
||||||
|
private static final int ATTEMPT_LIMIT = 3;
|
||||||
|
|
||||||
private SQLDB db;
|
private SQLDB db;
|
||||||
protected DBType dbType;
|
protected DBType dbType;
|
||||||
@ -44,9 +46,11 @@ public abstract class Transaction {
|
|||||||
private Savepoint savepoint;
|
private Savepoint savepoint;
|
||||||
|
|
||||||
protected boolean success;
|
protected boolean success;
|
||||||
|
protected int attempts;
|
||||||
|
|
||||||
protected Transaction() {
|
protected Transaction() {
|
||||||
success = false;
|
success = false;
|
||||||
|
attempts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void executeTransaction(SQLDB db) {
|
public void executeTransaction(SQLDB db) {
|
||||||
@ -61,31 +65,56 @@ public abstract class Transaction {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attempts++; // Keeps track how many attempts have been made to avoid infinite recursion.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initializeTransaction(db);
|
initializeTransaction(db);
|
||||||
performOperations();
|
performOperations();
|
||||||
if (connection != null) connection.commit();
|
if (connection != null) connection.commit();
|
||||||
success = true;
|
success = true;
|
||||||
} catch (Exception statementFail) {
|
} catch (SQLException statementFail) {
|
||||||
manageFailure(statementFail); // Throws a DBOpException.
|
manageFailure(statementFail); // Throws a DBOpException.
|
||||||
} finally {
|
} finally {
|
||||||
db.returnToPool(connection);
|
db.returnToPool(connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void manageFailure(Exception statementFail) {
|
private void manageFailure(SQLException statementFail) {
|
||||||
String failMsg = getClass().getSimpleName() + " failed: " + statementFail.getMessage();
|
String failMsg = getClass().getSimpleName() + " failed: " + statementFail.getMessage();
|
||||||
if (!SUPPORTS_SAVE_POINTS.get()) {
|
String rollbackStatusMsg = rollbackTransaction();
|
||||||
throw new DBOpException(failMsg + ", additionally rollbacks are not supported on this server version.", statementFail);
|
|
||||||
|
// Retry if deadlock occurs.
|
||||||
|
int errorCode = statementFail.getErrorCode();
|
||||||
|
boolean mySQLDeadlock = dbType == DBType.MYSQL && errorCode == 1213;
|
||||||
|
boolean h2Deadlock = dbType == DBType.H2 && errorCode == 40001;
|
||||||
|
boolean deadlocked = mySQLDeadlock || h2Deadlock;
|
||||||
|
if (deadlocked && attempts < ATTEMPT_LIMIT) {
|
||||||
|
executeTransaction(db); // Recurse to attempt again.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
try {
|
if (attempts >= ATTEMPT_LIMIT) {
|
||||||
if (Verify.notNull(connection, savepoint)) {
|
failMsg += " (Attempted " + attempts + " times)";
|
||||||
connection.rollback(savepoint);
|
}
|
||||||
|
|
||||||
|
throw new DBOpException(failMsg + rollbackStatusMsg, statementFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String rollbackTransaction() {
|
||||||
|
String rollbackStatusMsg = ", Transaction was rolled back.";
|
||||||
|
boolean hasNoSavepoints = !SUPPORTS_SAVE_POINTS.get();
|
||||||
|
if (hasNoSavepoints) {
|
||||||
|
rollbackStatusMsg = ", additionally rollbacks are not supported on this server version.";
|
||||||
|
} else {
|
||||||
|
// Rollbacks are supported.
|
||||||
|
try {
|
||||||
|
if (Verify.notNull(connection, savepoint)) {
|
||||||
|
connection.rollback(savepoint);
|
||||||
|
}
|
||||||
|
} catch (SQLException rollbackFail) {
|
||||||
|
rollbackStatusMsg = ", additionally Transaction rollback failed: " + rollbackFail.getMessage();
|
||||||
}
|
}
|
||||||
} catch (SQLException rollbackFail) {
|
|
||||||
throw new DBOpException(failMsg + ", additionally Transaction rollback failed: " + rollbackFail.getMessage(), statementFail);
|
|
||||||
}
|
}
|
||||||
throw new DBOpException(failMsg + ", Transaction was rolled back.", statementFail);
|
return rollbackStatusMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user