diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index c64e9e88..8c821b38 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -806,7 +806,10 @@ public class Controller extends Thread { repository.saveChanges(); } catch (DataException e) { - LOGGER.error("Repository issue while deleting expired unconfirmed transactions", e); + if (RepositoryManager.isDeadlockRelated(e)) + LOGGER.info("Couldn't delete some expired, unconfirmed transactions this round"); + else + LOGGER.error("Repository issue while deleting expired unconfirmed transactions", e); } } diff --git a/src/main/java/org/qortal/repository/RepositoryFactory.java b/src/main/java/org/qortal/repository/RepositoryFactory.java index e5b29d1b..bb34d1c9 100644 --- a/src/main/java/org/qortal/repository/RepositoryFactory.java +++ b/src/main/java/org/qortal/repository/RepositoryFactory.java @@ -1,5 +1,7 @@ package org.qortal.repository; +import java.sql.SQLException; + public interface RepositoryFactory { public boolean wasPristineAtOpen(); @@ -12,4 +14,7 @@ public interface RepositoryFactory { public void close() throws DataException; + // Not ideal place for this but implementating class will know the answer without having to open a new DB session + public boolean isDeadlockException(SQLException e); + } diff --git a/src/main/java/org/qortal/repository/RepositoryManager.java b/src/main/java/org/qortal/repository/RepositoryManager.java index 55e4f304..df578888 100644 --- a/src/main/java/org/qortal/repository/RepositoryManager.java +++ b/src/main/java/org/qortal/repository/RepositoryManager.java @@ -1,5 +1,7 @@ package org.qortal.repository; +import java.sql.SQLException; + public abstract class RepositoryManager { private static RepositoryFactory repositoryFactory = null; @@ -69,4 +71,10 @@ public abstract class RepositoryManager { repositoryFactory = oldRepositoryFactory.reopen(); } + public static boolean isDeadlockRelated(Throwable e) { + Throwable cause = e.getCause(); + + return SQLException.class.isInstance(cause) && repositoryFactory.isDeadlockException((SQLException) cause); + } + } diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java index 6d01516e..904bd9fb 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java @@ -59,6 +59,9 @@ public class HSQLDBRepository implements Repository { private static final Object CHECKPOINT_LOCK = new Object(); + // "serialization failure" + private static final Integer DEADLOCK_ERROR_CODE = Integer.valueOf(-4861); + protected Connection connection; protected final Deque savepoints = new ArrayDeque<>(3); protected boolean debugState = false; @@ -708,7 +711,16 @@ public class HSQLDBRepository implements Repository { long beforeQuery = this.slowQueryThreshold == null ? 0 : System.currentTimeMillis(); - int[] updateCounts = preparedStatement.executeBatch(); + int[] updateCounts = null; + try { + updateCounts = preparedStatement.executeBatch(); + } catch (SQLException e) { + if (isDeadlockException(e)) + // We want more info on what other DB sessions are doing to cause this + examineException(e); + + throw e; + } if (this.slowQueryThreshold != null) { long queryTime = System.currentTimeMillis() - beforeQuery; @@ -1000,4 +1012,8 @@ public class HSQLDBRepository implements Repository { return Crypto.toAddress(publicKey); } + /*package*/ static boolean isDeadlockException(SQLException e) { + return DEADLOCK_ERROR_CODE.equals(e.getErrorCode()); + } + } \ No newline at end of file diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java index c2be7074..2f18720c 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java @@ -148,4 +148,9 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory { } } + @Override + public boolean isDeadlockException(SQLException e) { + return HSQLDBRepository.isDeadlockException(e); + } + }