Fix repository race condition from using wrong synchronization object

Previous fixes for "transaction rollback: serialization failure" when updating trim heights
in commits 16397852 and 58ed7205 had the right idea but were broken due to being synchronized
on different objects.

this.repository.trimHeightsLock would be a new Object() for each repository connection/session
and so not actually synchronize concurrent updates.

Implicit saveChanges()/COMMIT is still needed.

Fix is to use a repository-wide object for synchronization - in this case the repositoryFactory
object as held by RepositoryManager.

Added test to cover.

Also reduced DB trim height read to one call at start of thread for both trimming threads.
This commit is contained in:
catbref
2020-11-17 14:53:39 +00:00
parent 62ae49b639
commit a12045c19e
5 changed files with 154 additions and 11 deletions

View File

@@ -18,6 +18,8 @@ public class AtStatesTrimmer implements Runnable {
Thread.currentThread().setName("AT States trimmer");
try (final Repository repository = RepositoryManager.getRepository()) {
int trimStartHeight = repository.getATRepository().getAtTrimHeight();
repository.getATRepository().prepareForAtStateTrimming();
repository.saveChanges();
@@ -41,8 +43,6 @@ public class AtStatesTrimmer implements Runnable {
long upperTrimmableTimestamp = Math.min(currentTrimmableTimestamp, chainTrimmableTimestamp);
int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
int trimStartHeight = repository.getATRepository().getAtTrimHeight();
int upperBatchHeight = trimStartHeight + Settings.getInstance().getAtStatesTrimBatchSize();
int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
@@ -53,17 +53,20 @@ public class AtStatesTrimmer implements Runnable {
repository.saveChanges();
if (numAtStatesTrimmed > 0) {
final int finalTrimStartHeight = trimStartHeight;
LOGGER.debug(() -> String.format("Trimmed %d AT state%s between blocks %d and %d",
numAtStatesTrimmed, (numAtStatesTrimmed != 1 ? "s" : ""),
trimStartHeight, upperTrimHeight));
finalTrimStartHeight, upperTrimHeight));
} else {
// Can we move onto next batch?
if (upperTrimmableHeight > upperBatchHeight) {
repository.getATRepository().setAtTrimHeight(upperBatchHeight);
trimStartHeight = upperBatchHeight;
repository.getATRepository().setAtTrimHeight(trimStartHeight);
repository.getATRepository().prepareForAtStateTrimming();
repository.saveChanges();
LOGGER.debug(() -> String.format("Bumping AT state trim height to %d", upperBatchHeight));
final int finalTrimStartHeight = trimStartHeight;
LOGGER.debug(() -> String.format("Bumping AT state base trim height to %d", finalTrimStartHeight));
}
}
}

View File

@@ -23,6 +23,8 @@ public class OnlineAccountsSignaturesTrimmer implements Runnable {
// Don't even start trimming until initial rush has ended
Thread.sleep(INITIAL_SLEEP_PERIOD);
int trimStartHeight = repository.getBlockRepository().getOnlineAccountsSignaturesTrimHeight();
while (!Controller.isStopping()) {
repository.discardChanges();
@@ -40,8 +42,6 @@ public class OnlineAccountsSignaturesTrimmer implements Runnable {
long upperTrimmableTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime();
int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
int trimStartHeight = repository.getBlockRepository().getOnlineAccountsSignaturesTrimHeight();
int upperBatchHeight = trimStartHeight + Settings.getInstance().getOnlineSignaturesTrimBatchSize();
int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
@@ -52,16 +52,20 @@ public class OnlineAccountsSignaturesTrimmer implements Runnable {
repository.saveChanges();
if (numSigsTrimmed > 0) {
final int finalTrimStartHeight = trimStartHeight;
LOGGER.debug(() -> String.format("Trimmed %d online accounts signature%s between blocks %d and %d",
numSigsTrimmed, (numSigsTrimmed != 1 ? "s" : ""),
trimStartHeight, upperTrimHeight));
finalTrimStartHeight, upperTrimHeight));
} else {
// Can we move onto next batch?
if (upperTrimmableHeight > upperBatchHeight) {
repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(upperBatchHeight);
trimStartHeight = upperBatchHeight;
repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(trimStartHeight);
repository.saveChanges();
LOGGER.debug(() -> String.format("Bumping online accounts signatures trim height to %d", upperBatchHeight));
final int finalTrimStartHeight = trimStartHeight;
LOGGER.debug(() -> String.format("Bumping online accounts signatures base trim height to %d", finalTrimStartHeight));
}
}
}

View File

@@ -4,6 +4,10 @@ public abstract class RepositoryManager {
private static RepositoryFactory repositoryFactory = null;
public static RepositoryFactory getRepositoryFactory() {
return repositoryFactory;
}
public static void setRepositoryFactory(RepositoryFactory newRepositoryFactory) {
repositoryFactory = newRepositoryFactory;
}

View File

@@ -60,7 +60,8 @@ public class HSQLDBRepository implements Repository {
protected List<String> sqlStatements;
protected long sessionId;
protected final Map<String, PreparedStatement> preparedStatementCache = new HashMap<>();
protected final Object trimHeightsLock = new Object();
// We want the same object corresponding to the actual DB
protected final Object trimHeightsLock = RepositoryManager.getRepositoryFactory();
private final ATRepository atRepository = new HSQLDBATRepository(this);
private final AccountRepository accountRepository = new HSQLDBAccountRepository(this);