diff --git a/pom.xml b/pom.xml
index 33c30f79..d17cc279 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,12 +22,12 @@
1.2.2
0.12.3
4.9.10
- 1.66.0
- 33.3.0-jre
+ 1.68.1
+ 33.3.1-jre
2.2
1.2.1
2.5.1
- 75.1
+ 76.1
4.12
4.0.1
2.3.9
@@ -49,7 +49,7 @@
0.17
3.3.1
3.6.0
- 3.5.0
+ 3.5.2
3.25.3
1.5.3
1.17
diff --git a/src/main/java/org/qortal/ApplyRestart.java b/src/main/java/org/qortal/ApplyRestart.java
index 70d07df5..a2d4588d 100644
--- a/src/main/java/org/qortal/ApplyRestart.java
+++ b/src/main/java/org/qortal/ApplyRestart.java
@@ -1,14 +1,17 @@
package org.qortal;
+import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.qortal.api.ApiKey;
import org.qortal.api.ApiRequest;
+import org.qortal.controller.Controller;
import org.qortal.controller.RestartNode;
import org.qortal.settings.Settings;
+import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
@@ -16,6 +19,8 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.Security;
import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import static org.qortal.controller.RestartNode.AGENTLIB_JVM_HOLDER_ARG;
@@ -57,15 +62,32 @@ public class ApplyRestart {
if (!shutdownNode())
return;
- // Restart node
- restartNode(args);
+ try {
+ // Give some time for shutdown
+ TimeUnit.SECONDS.sleep(30);
- LOGGER.info("Restarting...");
+ // Remove blockchain lock if exist
+ ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock();
+ if (blockchainLock.isLocked())
+ blockchainLock.unlock();
+
+ // Remove blockchain lock file if exist
+ TimeUnit.SECONDS.sleep(60);
+ deleteLock();
+
+ // Restart node
+ TimeUnit.SECONDS.sleep(30);
+ restartNode(args);
+
+ LOGGER.info("Restarting...");
+ } catch (InterruptedException e) {
+ LOGGER.error("Unable to restart", e);
+ }
}
private static boolean shutdownNode() {
String baseUri = "http://localhost:" + Settings.getInstance().getApiPort() + "/";
- LOGGER.info(() -> String.format("Shutting down node using API via %s", baseUri));
+ LOGGER.debug(() -> String.format("Shutting down node using API via %s", baseUri));
// The /admin/stop endpoint requires an API key, which may or may not be already generated
boolean apiKeyNewlyGenerated = false;
@@ -134,7 +156,22 @@ public class ApplyRestart {
apiKey.delete();
} catch (IOException e) {
- LOGGER.info("Error loading or deleting API key: {}", e.getMessage());
+ LOGGER.error("Error loading or deleting API key: {}", e.getMessage());
+ }
+ }
+
+ private static void deleteLock() {
+ // Get the repository path from settings
+ String repositoryPath = Settings.getInstance().getRepositoryPath();
+ LOGGER.debug(String.format("Repository path is: %s", repositoryPath));
+
+ try {
+ Path root = Paths.get(repositoryPath);
+ File lockFile = new File(root.resolve("blockchain.lck").toUri());
+ LOGGER.debug("Lockfile is: {}", lockFile);
+ FileUtils.forceDelete(FileUtils.getFile(lockFile));
+ } catch (IOException e) {
+ LOGGER.error("Error deleting blockchain lock file: {}", e.getMessage());
}
}
@@ -150,9 +187,10 @@ public class ApplyRestart {
List javaCmd;
if (Files.exists(exeLauncher)) {
- javaCmd = Arrays.asList(exeLauncher.toString());
+ javaCmd = List.of(exeLauncher.toString());
} else {
javaCmd = new ArrayList<>();
+
// Java runtime binary itself
javaCmd.add(javaBinary.toString());
diff --git a/src/main/java/org/qortal/controller/BlockMinter.java b/src/main/java/org/qortal/controller/BlockMinter.java
index 91dd12bb..7ea2242c 100644
--- a/src/main/java/org/qortal/controller/BlockMinter.java
+++ b/src/main/java/org/qortal/controller/BlockMinter.java
@@ -64,6 +64,7 @@ public class BlockMinter extends Thread {
@Override
public void run() {
Thread.currentThread().setName("BlockMinter");
+ Thread.currentThread().setPriority(MAX_PRIORITY);
if (Settings.getInstance().isTopOnly() || Settings.getInstance().isLite()) {
// Top only and lite nodes do not sign blocks
diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java
index 3f753c7f..27341d1e 100644
--- a/src/main/java/org/qortal/controller/Controller.java
+++ b/src/main/java/org/qortal/controller/Controller.java
@@ -13,6 +13,7 @@ import org.qortal.block.Block;
import org.qortal.block.BlockChain;
import org.qortal.block.BlockChain.BlockTimingByHeight;
import org.qortal.controller.arbitrary.*;
+import org.qortal.controller.hsqldb.HSQLDBDataCacheManager;
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
import org.qortal.controller.repository.PruneManager;
import org.qortal.controller.tradebot.TradeBot;
@@ -35,6 +36,7 @@ import org.qortal.network.Peer;
import org.qortal.network.PeerAddress;
import org.qortal.network.message.*;
import org.qortal.repository.*;
+import org.qortal.repository.hsqldb.HSQLDBRepository;
import org.qortal.repository.hsqldb.HSQLDBRepositoryFactory;
import org.qortal.settings.Settings;
import org.qortal.transaction.Transaction;
@@ -99,7 +101,7 @@ public class Controller extends Thread {
private final long buildTimestamp; // seconds
private final String[] savedArgs;
- private ExecutorService callbackExecutor = Executors.newFixedThreadPool(3);
+ private ExecutorService callbackExecutor = Executors.newFixedThreadPool(4);
private volatile boolean notifyGroupMembershipChange = false;
/** Latest blocks on our chain. Note: tail/last is the latest block. */
@@ -406,8 +408,17 @@ public class Controller extends Thread {
RepositoryManager.setRequestedCheckpoint(Boolean.TRUE);
try (final Repository repository = RepositoryManager.getRepository()) {
- RepositoryManager.rebuildTransactionSequences(repository);
+ // RepositoryManager.rebuildTransactionSequences(repository);
ArbitraryDataCacheManager.getInstance().buildArbitraryResourcesCache(repository, false);
+
+ if( Settings.getInstance().isDbCacheEnabled() ) {
+ LOGGER.info("Db Cache Starting ...");
+ HSQLDBDataCacheManager hsqldbDataCacheManager = new HSQLDBDataCacheManager((HSQLDBRepository) repositoryFactory.getRepository());
+ hsqldbDataCacheManager.start();
+ }
+ else {
+ LOGGER.info("Db Cache Disabled");
+ }
}
} catch (DataException e) {
// If exception has no cause or message then repository is in use by some other process.
@@ -489,7 +500,6 @@ public class Controller extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Shutdown hook");
-
Controller.getInstance().shutdown();
}
});
@@ -569,10 +579,31 @@ public class Controller extends Thread {
// If GUI is enabled, we're no longer starting up but actually running now
Gui.getInstance().notifyRunning();
- // Check every 10 minutes to see if the block minter is running
- Timer timer = new Timer();
+ // Check every 10 minutes if we have enough connected peers
+ Timer checkConnectedPeers = new Timer();
- timer.schedule(new TimerTask() {
+ checkConnectedPeers.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ // Get the connected peers
+ int myConnectedPeers = Network.getInstance().getImmutableHandshakedPeers().size();
+ LOGGER.debug("Node have {} connected peers", myConnectedPeers);
+ if (myConnectedPeers == 0) {
+ // Restart node if we have 0 peers
+ LOGGER.info("Node have no connected peers, restarting node");
+ try {
+ RestartNode.attemptToRestart();
+ } catch (Exception e) {
+ LOGGER.error("Unable to restart the node", e);
+ }
+ }
+ }
+ }, 10*60*1000, 10*60*1000);
+
+ // Check every 10 minutes to see if the block minter is running
+ Timer checkBlockMinter = new Timer();
+
+ checkBlockMinter.schedule(new TimerTask() {
@Override
public void run() {
if (blockMinter.isAlive()) {
diff --git a/src/main/java/org/qortal/controller/OnlineAccountsManager.java b/src/main/java/org/qortal/controller/OnlineAccountsManager.java
index d37b2aef..97af8627 100644
--- a/src/main/java/org/qortal/controller/OnlineAccountsManager.java
+++ b/src/main/java/org/qortal/controller/OnlineAccountsManager.java
@@ -80,7 +80,7 @@ public class OnlineAccountsManager {
// one for the transition period.
private static long[] POW_VERIFY_WORK_BUFFER = new long[getPoWBufferSize() / 8];
- private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4, new NamedThreadFactory("OnlineAccounts"));
+ private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(4, new NamedThreadFactory("OnlineAccounts", Thread.NORM_PRIORITY));
private volatile boolean isStopping = false;
private final Set onlineAccountsImportQueue = ConcurrentHashMap.newKeySet();
diff --git a/src/main/java/org/qortal/controller/PirateChainWalletController.java b/src/main/java/org/qortal/controller/PirateChainWalletController.java
index e009d531..8f0c63b7 100644
--- a/src/main/java/org/qortal/controller/PirateChainWalletController.java
+++ b/src/main/java/org/qortal/controller/PirateChainWalletController.java
@@ -65,6 +65,7 @@ public class PirateChainWalletController extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Pirate Chain Wallet Controller");
+ Thread.currentThread().setPriority(MIN_PRIORITY);
try {
while (running && !Controller.isStopping()) {
diff --git a/src/main/java/org/qortal/controller/Synchronizer.java b/src/main/java/org/qortal/controller/Synchronizer.java
index 306784f5..400e7965 100644
--- a/src/main/java/org/qortal/controller/Synchronizer.java
+++ b/src/main/java/org/qortal/controller/Synchronizer.java
@@ -118,8 +118,12 @@ public class Synchronizer extends Thread {
}
public static Synchronizer getInstance() {
- if (instance == null)
+ if (instance == null) {
instance = new Synchronizer();
+ instance.setPriority(Settings.getInstance().getSynchronizerThreadPriority());
+
+ LOGGER.info("thread priority = " + instance.getPriority());
+ }
return instance;
}
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataBuilderThread.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataBuilderThread.java
index 11f613ae..7f70ac05 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataBuilderThread.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataBuilderThread.java
@@ -14,6 +14,7 @@ import java.io.IOException;
import java.util.Comparator;
import java.util.Map;
+import static java.lang.Thread.NORM_PRIORITY;
import static org.qortal.data.arbitrary.ArbitraryResourceStatus.Status.NOT_PUBLISHED;
@@ -28,6 +29,7 @@ public class ArbitraryDataBuilderThread implements Runnable {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Builder Thread");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
ArbitraryDataBuildManager buildManager = ArbitraryDataBuildManager.getInstance();
while (!Controller.isStopping()) {
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCacheManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCacheManager.java
index 36d53761..d6b9303f 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCacheManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCacheManager.java
@@ -41,6 +41,7 @@ public class ArbitraryDataCacheManager extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Cache Manager");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
try {
while (!Controller.isStopping()) {
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
index 7b434acb..aa29a7b8 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
@@ -71,6 +71,7 @@ public class ArbitraryDataCleanupManager extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Cleanup Manager");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
// Paginate queries when fetching arbitrary transactions
final int limit = 100;
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileRequestThread.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileRequestThread.java
index 12a03aee..8f734457 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileRequestThread.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataFileRequestThread.java
@@ -17,6 +17,8 @@ import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
+import static java.lang.Thread.NORM_PRIORITY;
+
public class ArbitraryDataFileRequestThread implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(ArbitraryDataFileRequestThread.class);
@@ -28,6 +30,7 @@ public class ArbitraryDataFileRequestThread implements Runnable {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data File Request Thread");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
try {
while (!Controller.isStopping()) {
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java
index 37801f50..6d64e20a 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataManager.java
@@ -91,6 +91,7 @@ public class ArbitraryDataManager extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Manager");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
// Create data directory in case it doesn't exist yet
this.createDataDirectory();
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java
index 809db7af..c2a720fa 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataRenderManager.java
@@ -36,6 +36,7 @@ public class ArbitraryDataRenderManager extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Render Manager");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
try {
while (!isStopping) {
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
index 91cb9965..62feb8bd 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
@@ -72,6 +72,8 @@ public class ArbitraryDataStorageManager extends Thread {
@Override
public void run() {
Thread.currentThread().setName("Arbitrary Data Storage Manager");
+ Thread.currentThread().setPriority(NORM_PRIORITY);
+
try {
while (!isStopping) {
Thread.sleep(1000);
diff --git a/src/main/java/org/qortal/controller/hsqldb/HSQLDBDataCacheManager.java b/src/main/java/org/qortal/controller/hsqldb/HSQLDBDataCacheManager.java
new file mode 100644
index 00000000..33950a9c
--- /dev/null
+++ b/src/main/java/org/qortal/controller/hsqldb/HSQLDBDataCacheManager.java
@@ -0,0 +1,29 @@
+package org.qortal.controller.hsqldb;
+
+import org.qortal.data.arbitrary.ArbitraryResourceCache;
+import org.qortal.repository.RepositoryManager;
+import org.qortal.repository.hsqldb.HSQLDBCacheUtils;
+import org.qortal.repository.hsqldb.HSQLDBRepository;
+import org.qortal.settings.Settings;
+
+public class HSQLDBDataCacheManager extends Thread{
+
+ private ArbitraryResourceCache cache = ArbitraryResourceCache.getInstance();
+ private HSQLDBRepository respository;
+
+ public HSQLDBDataCacheManager(HSQLDBRepository respository) {
+ this.respository = respository;
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("HSQLDB Data Cache Manager");
+
+ this.cache
+ = HSQLDBCacheUtils.startCaching(
+ Settings.getInstance().getDbCacheThreadPriority(),
+ Settings.getInstance().getDbCacheFrequency(),
+ this.respository
+ );
+ }
+}
diff --git a/src/main/java/org/qortal/controller/repository/AtStatesPruner.java b/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
index f06efdb8..467333c8 100644
--- a/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
+++ b/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
@@ -11,6 +11,8 @@ import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
+import static java.lang.Thread.MIN_PRIORITY;
+
public class AtStatesPruner implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(AtStatesPruner.class);
@@ -46,72 +48,81 @@ public class AtStatesPruner implements Runnable {
repository.saveChanges();
while (!Controller.isStopping()) {
- repository.discardChanges();
+ try {
+ repository.discardChanges();
- Thread.sleep(Settings.getInstance().getAtStatesPruneInterval());
+ Thread.sleep(Settings.getInstance().getAtStatesPruneInterval());
- BlockData chainTip = Controller.getInstance().getChainTip();
- if (chainTip == null || NTP.getTime() == null)
- continue;
+ BlockData chainTip = Controller.getInstance().getChainTip();
+ if (chainTip == null || NTP.getTime() == null)
+ continue;
- // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
- if (Synchronizer.getInstance().isSynchronizing())
- continue;
+ // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
+ if (Synchronizer.getInstance().isSynchronizing())
+ continue;
- // Prune AT states for all blocks up until our latest minus pruneBlockLimit
- final int ourLatestHeight = chainTip.getHeight();
- int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
+ // Prune AT states for all blocks up until our latest minus pruneBlockLimit
+ final int ourLatestHeight = chainTip.getHeight();
+ int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
- // In archive mode we are only allowed to trim blocks that have already been archived
- if (archiveMode) {
- upperPrunableHeight = repository.getBlockArchiveRepository().getBlockArchiveHeight() - 1;
+ // In archive mode we are only allowed to trim blocks that have already been archived
+ if (archiveMode) {
+ upperPrunableHeight = repository.getBlockArchiveRepository().getBlockArchiveHeight() - 1;
- // TODO: validate that the actual archived data exists before pruning it?
- }
+ // TODO: validate that the actual archived data exists before pruning it?
+ }
- int upperBatchHeight = pruneStartHeight + Settings.getInstance().getAtStatesPruneBatchSize();
- int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
+ int upperBatchHeight = pruneStartHeight + Settings.getInstance().getAtStatesPruneBatchSize();
+ int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
- if (pruneStartHeight >= upperPruneHeight)
- continue;
+ if (pruneStartHeight >= upperPruneHeight)
+ continue;
- LOGGER.debug(String.format("Pruning AT states between blocks %d and %d...", pruneStartHeight, upperPruneHeight));
+ LOGGER.info(String.format("Pruning AT states between blocks %d and %d...", pruneStartHeight, upperPruneHeight));
- int numAtStatesPruned = repository.getATRepository().pruneAtStates(pruneStartHeight, upperPruneHeight);
- repository.saveChanges();
- int numAtStateDataRowsTrimmed = repository.getATRepository().trimAtStates(
- pruneStartHeight, upperPruneHeight, Settings.getInstance().getAtStatesTrimLimit());
- repository.saveChanges();
-
- if (numAtStatesPruned > 0 || numAtStateDataRowsTrimmed > 0) {
- final int finalPruneStartHeight = pruneStartHeight;
- LOGGER.debug(() -> String.format("Pruned %d AT state%s between blocks %d and %d",
- numAtStatesPruned, (numAtStatesPruned != 1 ? "s" : ""),
- finalPruneStartHeight, upperPruneHeight));
- } else {
- // Can we move onto next batch?
- if (upperPrunableHeight > upperBatchHeight) {
- pruneStartHeight = upperBatchHeight;
- repository.getATRepository().setAtPruneHeight(pruneStartHeight);
- maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
- repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
- repository.saveChanges();
+ int numAtStatesPruned = repository.getATRepository().pruneAtStates(pruneStartHeight, upperPruneHeight);
+ repository.saveChanges();
+ int numAtStateDataRowsTrimmed = repository.getATRepository().trimAtStates(
+ pruneStartHeight, upperPruneHeight, Settings.getInstance().getAtStatesTrimLimit());
+ repository.saveChanges();
+ if (numAtStatesPruned > 0 || numAtStateDataRowsTrimmed > 0) {
final int finalPruneStartHeight = pruneStartHeight;
- LOGGER.debug(() -> String.format("Bumping AT state base prune height to %d", finalPruneStartHeight));
+ LOGGER.info(() -> String.format("Pruned %d AT state%s between blocks %d and %d",
+ numAtStatesPruned, (numAtStatesPruned != 1 ? "s" : ""),
+ finalPruneStartHeight, upperPruneHeight));
+ } else {
+ // Can we move onto next batch?
+ if (upperPrunableHeight > upperBatchHeight) {
+ pruneStartHeight = upperBatchHeight;
+ repository.getATRepository().setAtPruneHeight(pruneStartHeight);
+ maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
+ repository.saveChanges();
+
+ final int finalPruneStartHeight = pruneStartHeight;
+ LOGGER.info(() -> String.format("Bumping AT state base prune height to %d", finalPruneStartHeight));
+ }
+ else {
+ // We've pruned up to the upper prunable height
+ // Back off for a while to save CPU for syncing
+ repository.discardChanges();
+ Thread.sleep(5*60*1000L);
+ }
+ }
+ } catch (InterruptedException e) {
+ if(Controller.isStopping()) {
+ LOGGER.info("AT States Pruning Shutting Down");
}
else {
- // We've pruned up to the upper prunable height
- // Back off for a while to save CPU for syncing
- repository.discardChanges();
- Thread.sleep(5*60*1000L);
+ LOGGER.warn("AT States Pruning interrupted. Trying again. Report this error immediately to the developers.", e);
}
+ } catch (Exception e) {
+ LOGGER.warn("AT States Pruning stopped working. Trying again. Report this error immediately to the developers.", e);
}
}
- } catch (DataException e) {
- LOGGER.warn(String.format("Repository issue trying to prune AT states: %s", e.getMessage()));
- } catch (InterruptedException e) {
- // Time to exit
+ } catch (Exception e) {
+ LOGGER.error("AT States Pruning is not working! Not trying again. Restart ASAP. Report this error immediately to the developers.", e);
}
}
diff --git a/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java b/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
index 125628f1..26c2931d 100644
--- a/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
+++ b/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
@@ -11,6 +11,8 @@ import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
+import static java.lang.Thread.MIN_PRIORITY;
+
public class AtStatesTrimmer implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(AtStatesTrimmer.class);
@@ -33,57 +35,66 @@ public class AtStatesTrimmer implements Runnable {
repository.saveChanges();
while (!Controller.isStopping()) {
- repository.discardChanges();
+ try {
+ repository.discardChanges();
- Thread.sleep(Settings.getInstance().getAtStatesTrimInterval());
+ Thread.sleep(Settings.getInstance().getAtStatesTrimInterval());
- BlockData chainTip = Controller.getInstance().getChainTip();
- if (chainTip == null || NTP.getTime() == null)
- continue;
+ BlockData chainTip = Controller.getInstance().getChainTip();
+ if (chainTip == null || NTP.getTime() == null)
+ continue;
- // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
- if (Synchronizer.getInstance().isSynchronizing())
- continue;
+ // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
+ if (Synchronizer.getInstance().isSynchronizing())
+ continue;
- long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime();
- // We want to keep AT states near the tip of our copy of blockchain so we can process/orphan nearby blocks
- long chainTrimmableTimestamp = chainTip.getTimestamp() - Settings.getInstance().getAtStatesMaxLifetime();
+ long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime();
+ // We want to keep AT states near the tip of our copy of blockchain so we can process/orphan nearby blocks
+ long chainTrimmableTimestamp = chainTip.getTimestamp() - Settings.getInstance().getAtStatesMaxLifetime();
- long upperTrimmableTimestamp = Math.min(currentTrimmableTimestamp, chainTrimmableTimestamp);
- int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
+ long upperTrimmableTimestamp = Math.min(currentTrimmableTimestamp, chainTrimmableTimestamp);
+ int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
- int upperBatchHeight = trimStartHeight + Settings.getInstance().getAtStatesTrimBatchSize();
- int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
+ int upperBatchHeight = trimStartHeight + Settings.getInstance().getAtStatesTrimBatchSize();
+ int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
- if (trimStartHeight >= upperTrimHeight)
- continue;
+ if (trimStartHeight >= upperTrimHeight)
+ continue;
- int numAtStatesTrimmed = repository.getATRepository().trimAtStates(trimStartHeight, upperTrimHeight, Settings.getInstance().getAtStatesTrimLimit());
- 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" : ""),
- finalTrimStartHeight, upperTrimHeight));
- } else {
- // Can we move onto next batch?
- if (upperTrimmableHeight > upperBatchHeight) {
- trimStartHeight = upperBatchHeight;
- repository.getATRepository().setAtTrimHeight(trimStartHeight);
- maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
- repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
- repository.saveChanges();
+ int numAtStatesTrimmed = repository.getATRepository().trimAtStates(trimStartHeight, upperTrimHeight, Settings.getInstance().getAtStatesTrimLimit());
+ repository.saveChanges();
+ if (numAtStatesTrimmed > 0) {
final int finalTrimStartHeight = trimStartHeight;
- LOGGER.debug(() -> String.format("Bumping AT state base trim height to %d", finalTrimStartHeight));
+ LOGGER.info(() -> String.format("Trimmed %d AT state%s between blocks %d and %d",
+ numAtStatesTrimmed, (numAtStatesTrimmed != 1 ? "s" : ""),
+ finalTrimStartHeight, upperTrimHeight));
+ } else {
+ // Can we move onto next batch?
+ if (upperTrimmableHeight > upperBatchHeight) {
+ trimStartHeight = upperBatchHeight;
+ repository.getATRepository().setAtTrimHeight(trimStartHeight);
+ maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
+ repository.saveChanges();
+
+ final int finalTrimStartHeight = trimStartHeight;
+ LOGGER.info(() -> String.format("Bumping AT state base trim height to %d", finalTrimStartHeight));
+ }
}
+ } catch (InterruptedException e) {
+ if(Controller.isStopping()) {
+ LOGGER.info("AT States Trimming Shutting Down");
+ }
+ else {
+ LOGGER.warn("AT States Trimming interrupted. Trying again. Report this error immediately to the developers.", e);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("AT States Trimming stopped working. Trying again. Report this error immediately to the developers.", e);
}
}
- } catch (DataException e) {
- LOGGER.warn(String.format("Repository issue trying to trim AT states: %s", e.getMessage()));
- } catch (InterruptedException e) {
- // Time to exit
+ } catch (Exception e) {
+ LOGGER.error("AT States Trimming is not working! Not trying again. Restart ASAP. Report this error immediately to the developers.", e);
}
}
diff --git a/src/main/java/org/qortal/controller/repository/BlockArchiver.java b/src/main/java/org/qortal/controller/repository/BlockArchiver.java
index a643d9b9..b3c01f35 100644
--- a/src/main/java/org/qortal/controller/repository/BlockArchiver.java
+++ b/src/main/java/org/qortal/controller/repository/BlockArchiver.java
@@ -15,11 +15,13 @@ import org.qortal.utils.NTP;
import java.io.IOException;
+import static java.lang.Thread.NORM_PRIORITY;
+
public class BlockArchiver implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(BlockArchiver.class);
- private static final long INITIAL_SLEEP_PERIOD = 5 * 60 * 1000L + 1234L; // ms
+ private static final long INITIAL_SLEEP_PERIOD = 15 * 60 * 1000L; // ms
public void run() {
Thread.currentThread().setName("Block archiver");
@@ -45,71 +47,78 @@ public class BlockArchiver implements Runnable {
LOGGER.info("Starting block archiver from height {}...", startHeight);
while (!Controller.isStopping()) {
- repository.discardChanges();
-
- Thread.sleep(Settings.getInstance().getArchiveInterval());
-
- BlockData chainTip = Controller.getInstance().getChainTip();
- if (chainTip == null || NTP.getTime() == null) {
- continue;
- }
-
- // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
- if (Synchronizer.getInstance().isSynchronizing()) {
- continue;
- }
-
- // Don't attempt to archive if we're not synced yet
- final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
- if (minLatestBlockTimestamp == null || chainTip.getTimestamp() < minLatestBlockTimestamp) {
- continue;
- }
-
-
- // Build cache of blocks
try {
- final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository);
- BlockArchiveWriter writer = new BlockArchiveWriter(startHeight, maximumArchiveHeight, repository);
- BlockArchiveWriter.BlockArchiveWriteResult result = writer.write();
- switch (result) {
- case OK:
- // Increment block archive height
- startHeight += writer.getWrittenCount();
- repository.getBlockArchiveRepository().setBlockArchiveHeight(startHeight);
- repository.saveChanges();
- break;
+ repository.discardChanges();
- case STOPPING:
- return;
+ Thread.sleep(Settings.getInstance().getArchiveInterval());
- // We've reached the limit of the blocks we can archive
- // Sleep for a while to allow more to become available
- case NOT_ENOUGH_BLOCKS:
- // We didn't reach our file size target, so that must mean that we don't have enough blocks
- // yet or something went wrong. Sleep for a while and then try again.
- repository.discardChanges();
- Thread.sleep(60 * 60 * 1000L); // 1 hour
- break;
-
- case BLOCK_NOT_FOUND:
- // We tried to archive a block that didn't exist. This is a major failure and likely means
- // that a bootstrap or re-sync is needed. Try again every minute until then.
- LOGGER.info("Error: block not found when building archive. If this error persists, " +
- "a bootstrap or re-sync may be needed.");
- repository.discardChanges();
- Thread.sleep( 60 * 1000L); // 1 minute
- break;
+ BlockData chainTip = Controller.getInstance().getChainTip();
+ if (chainTip == null || NTP.getTime() == null) {
+ continue;
}
- } catch (IOException | TransformationException e) {
- LOGGER.info("Caught exception when creating block cache", e);
- }
+ // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
+ if (Synchronizer.getInstance().isSynchronizing()) {
+ continue;
+ }
+ // Don't attempt to archive if we're not synced yet
+ final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
+ if (minLatestBlockTimestamp == null || chainTip.getTimestamp() < minLatestBlockTimestamp) {
+ continue;
+ }
+
+ // Build cache of blocks
+ try {
+ final int maximumArchiveHeight = BlockArchiveWriter.getMaxArchiveHeight(repository);
+ BlockArchiveWriter writer = new BlockArchiveWriter(startHeight, maximumArchiveHeight, repository);
+ BlockArchiveWriter.BlockArchiveWriteResult result = writer.write();
+ switch (result) {
+ case OK:
+ // Increment block archive height
+ startHeight += writer.getWrittenCount();
+ repository.getBlockArchiveRepository().setBlockArchiveHeight(startHeight);
+ repository.saveChanges();
+ break;
+
+ case STOPPING:
+ return;
+
+ // We've reached the limit of the blocks we can archive
+ // Sleep for a while to allow more to become available
+ case NOT_ENOUGH_BLOCKS:
+ // We didn't reach our file size target, so that must mean that we don't have enough blocks
+ // yet or something went wrong. Sleep for a while and then try again.
+ repository.discardChanges();
+ Thread.sleep(2 * 60 * 60 * 1000L); // 1 hour
+ break;
+
+ case BLOCK_NOT_FOUND:
+ // We tried to archive a block that didn't exist. This is a major failure and likely means
+ // that a bootstrap or re-sync is needed. Try again every minute until then.
+ LOGGER.info("Error: block not found when building archive. If this error persists, " +
+ "a bootstrap or re-sync may be needed.");
+ repository.discardChanges();
+ Thread.sleep(60 * 1000L); // 1 minute
+ break;
+ }
+
+ } catch (IOException | TransformationException e) {
+ LOGGER.info("Caught exception when creating block cache", e);
+ }
+ } catch (InterruptedException e) {
+ if(Controller.isStopping()) {
+ LOGGER.info("Block Archiving Shutting Down");
+ }
+ else {
+ LOGGER.warn("Block Archiving interrupted. Trying again. Report this error immediately to the developers.", e);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Block Archiving stopped working. Trying again. Report this error immediately to the developers.", e);
+ }
}
- } catch (DataException e) {
- LOGGER.info("Caught exception when creating block cache", e);
- } catch (InterruptedException e) {
- // Do nothing
+ } catch (Exception e) {
+ LOGGER.error("Block Archiving is not working! Not trying again. Restart ASAP. Report this error immediately to the developers.", e);
}
}
diff --git a/src/main/java/org/qortal/controller/repository/BlockPruner.java b/src/main/java/org/qortal/controller/repository/BlockPruner.java
index 23e3a45a..624457eb 100644
--- a/src/main/java/org/qortal/controller/repository/BlockPruner.java
+++ b/src/main/java/org/qortal/controller/repository/BlockPruner.java
@@ -11,6 +11,8 @@ import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
+import static java.lang.Thread.NORM_PRIORITY;
+
public class BlockPruner implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(BlockPruner.class);
@@ -48,72 +50,81 @@ public class BlockPruner implements Runnable {
}
while (!Controller.isStopping()) {
- repository.discardChanges();
+ try {
+ repository.discardChanges();
- Thread.sleep(Settings.getInstance().getBlockPruneInterval());
+ Thread.sleep(Settings.getInstance().getBlockPruneInterval());
- BlockData chainTip = Controller.getInstance().getChainTip();
- if (chainTip == null || NTP.getTime() == null)
- continue;
+ BlockData chainTip = Controller.getInstance().getChainTip();
+ if (chainTip == null || NTP.getTime() == null)
+ continue;
- // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
- if (Synchronizer.getInstance().isSynchronizing()) {
- continue;
- }
+ // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
+ if (Synchronizer.getInstance().isSynchronizing()) {
+ continue;
+ }
- // Don't attempt to prune if we're not synced yet
- final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
- if (minLatestBlockTimestamp == null || chainTip.getTimestamp() < minLatestBlockTimestamp) {
- continue;
- }
+ // Don't attempt to prune if we're not synced yet
+ final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
+ if (minLatestBlockTimestamp == null || chainTip.getTimestamp() < minLatestBlockTimestamp) {
+ continue;
+ }
- // Prune all blocks up until our latest minus pruneBlockLimit
- final int ourLatestHeight = chainTip.getHeight();
- int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
+ // Prune all blocks up until our latest minus pruneBlockLimit
+ final int ourLatestHeight = chainTip.getHeight();
+ int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
- // In archive mode we are only allowed to trim blocks that have already been archived
- if (archiveMode) {
- upperPrunableHeight = repository.getBlockArchiveRepository().getBlockArchiveHeight() - 1;
- }
+ // In archive mode we are only allowed to trim blocks that have already been archived
+ if (archiveMode) {
+ upperPrunableHeight = repository.getBlockArchiveRepository().getBlockArchiveHeight() - 1;
+ }
- int upperBatchHeight = pruneStartHeight + Settings.getInstance().getBlockPruneBatchSize();
- int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
+ int upperBatchHeight = pruneStartHeight + Settings.getInstance().getBlockPruneBatchSize();
+ int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
- if (pruneStartHeight >= upperPruneHeight) {
- continue;
- }
+ if (pruneStartHeight >= upperPruneHeight) {
+ continue;
+ }
- LOGGER.debug(String.format("Pruning blocks between %d and %d...", pruneStartHeight, upperPruneHeight));
+ LOGGER.info(String.format("Pruning blocks between %d and %d...", pruneStartHeight, upperPruneHeight));
- int numBlocksPruned = repository.getBlockRepository().pruneBlocks(pruneStartHeight, upperPruneHeight);
- repository.saveChanges();
-
- if (numBlocksPruned > 0) {
- LOGGER.debug(String.format("Pruned %d block%s between %d and %d",
- numBlocksPruned, (numBlocksPruned != 1 ? "s" : ""),
- pruneStartHeight, upperPruneHeight));
- } else {
- final int nextPruneHeight = upperPruneHeight + 1;
- repository.getBlockRepository().setBlockPruneHeight(nextPruneHeight);
+ int numBlocksPruned = repository.getBlockRepository().pruneBlocks(pruneStartHeight, upperPruneHeight);
repository.saveChanges();
- LOGGER.debug(String.format("Bumping block base prune height to %d", pruneStartHeight));
- // Can we move onto next batch?
- if (upperPrunableHeight > nextPruneHeight) {
- pruneStartHeight = nextPruneHeight;
+ if (numBlocksPruned > 0) {
+ LOGGER.info(String.format("Pruned %d block%s between %d and %d",
+ numBlocksPruned, (numBlocksPruned != 1 ? "s" : ""),
+ pruneStartHeight, upperPruneHeight));
+ } else {
+ final int nextPruneHeight = upperPruneHeight + 1;
+ repository.getBlockRepository().setBlockPruneHeight(nextPruneHeight);
+ repository.saveChanges();
+ LOGGER.info(String.format("Bumping block base prune height to %d", pruneStartHeight));
+
+ // Can we move onto next batch?
+ if (upperPrunableHeight > nextPruneHeight) {
+ pruneStartHeight = nextPruneHeight;
+ }
+ else {
+ // We've pruned up to the upper prunable height
+ // Back off for a while to save CPU for syncing
+ repository.discardChanges();
+ Thread.sleep(10*60*1000L);
+ }
+ }
+ } catch (InterruptedException e) {
+ if(Controller.isStopping()) {
+ LOGGER.info("Block Pruning Shutting Down");
}
else {
- // We've pruned up to the upper prunable height
- // Back off for a while to save CPU for syncing
- repository.discardChanges();
- Thread.sleep(10*60*1000L);
+ LOGGER.warn("Block Pruning interrupted. Trying again. Report this error immediately to the developers.", e);
}
+ } catch (Exception e) {
+ LOGGER.warn("Block Pruning stopped working. Trying again. Report this error immediately to the developers.", e);
}
}
- } catch (DataException e) {
- LOGGER.warn(String.format("Repository issue trying to prune blocks: %s", e.getMessage()));
- } catch (InterruptedException e) {
- // Time to exit
+ } catch (Exception e) {
+ LOGGER.error("Block Pruning is not working! Not trying again. Restart ASAP. Report this error immediately to the developers.", e);
}
}
diff --git a/src/main/java/org/qortal/controller/repository/OnlineAccountsSignaturesTrimmer.java b/src/main/java/org/qortal/controller/repository/OnlineAccountsSignaturesTrimmer.java
index d74df4b5..f6ee3e05 100644
--- a/src/main/java/org/qortal/controller/repository/OnlineAccountsSignaturesTrimmer.java
+++ b/src/main/java/org/qortal/controller/repository/OnlineAccountsSignaturesTrimmer.java
@@ -12,6 +12,8 @@ import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
+import static java.lang.Thread.NORM_PRIORITY;
+
public class OnlineAccountsSignaturesTrimmer implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(OnlineAccountsSignaturesTrimmer.class);
@@ -33,53 +35,62 @@ public class OnlineAccountsSignaturesTrimmer implements Runnable {
int trimStartHeight = repository.getBlockRepository().getOnlineAccountsSignaturesTrimHeight();
while (!Controller.isStopping()) {
- repository.discardChanges();
+ try {
+ repository.discardChanges();
- Thread.sleep(Settings.getInstance().getOnlineSignaturesTrimInterval());
+ Thread.sleep(Settings.getInstance().getOnlineSignaturesTrimInterval());
- BlockData chainTip = Controller.getInstance().getChainTip();
- if (chainTip == null || NTP.getTime() == null)
- continue;
+ BlockData chainTip = Controller.getInstance().getChainTip();
+ if (chainTip == null || NTP.getTime() == null)
+ continue;
- // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
- if (Synchronizer.getInstance().isSynchronizing())
- continue;
+ // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
+ if (Synchronizer.getInstance().isSynchronizing())
+ continue;
- // Trim blockchain by removing 'old' online accounts signatures
- long upperTrimmableTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime();
- int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
+ // Trim blockchain by removing 'old' online accounts signatures
+ long upperTrimmableTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime();
+ int upperTrimmableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperTrimmableTimestamp);
- int upperBatchHeight = trimStartHeight + Settings.getInstance().getOnlineSignaturesTrimBatchSize();
- int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
+ int upperBatchHeight = trimStartHeight + Settings.getInstance().getOnlineSignaturesTrimBatchSize();
+ int upperTrimHeight = Math.min(upperBatchHeight, upperTrimmableHeight);
- if (trimStartHeight >= upperTrimHeight)
- continue;
+ if (trimStartHeight >= upperTrimHeight)
+ continue;
- int numSigsTrimmed = repository.getBlockRepository().trimOldOnlineAccountsSignatures(trimStartHeight, upperTrimHeight);
- 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" : ""),
- finalTrimStartHeight, upperTrimHeight));
- } else {
- // Can we move onto next batch?
- if (upperTrimmableHeight > upperBatchHeight) {
- trimStartHeight = upperBatchHeight;
-
- repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(trimStartHeight);
- repository.saveChanges();
+ int numSigsTrimmed = repository.getBlockRepository().trimOldOnlineAccountsSignatures(trimStartHeight, upperTrimHeight);
+ repository.saveChanges();
+ if (numSigsTrimmed > 0) {
final int finalTrimStartHeight = trimStartHeight;
- LOGGER.debug(() -> String.format("Bumping online accounts signatures base trim height to %d", finalTrimStartHeight));
+ LOGGER.info(() -> String.format("Trimmed %d online accounts signature%s between blocks %d and %d",
+ numSigsTrimmed, (numSigsTrimmed != 1 ? "s" : ""),
+ finalTrimStartHeight, upperTrimHeight));
+ } else {
+ // Can we move onto next batch?
+ if (upperTrimmableHeight > upperBatchHeight) {
+ trimStartHeight = upperBatchHeight;
+
+ repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(trimStartHeight);
+ repository.saveChanges();
+
+ final int finalTrimStartHeight = trimStartHeight;
+ LOGGER.info(() -> String.format("Bumping online accounts signatures base trim height to %d", finalTrimStartHeight));
+ }
}
+ } catch (InterruptedException e) {
+ if(Controller.isStopping()) {
+ LOGGER.info("Online Accounts Signatures Trimming Shutting Down");
+ }
+ else {
+ LOGGER.warn("Online Accounts Signatures Trimming interrupted. Trying again. Report this error immediately to the developers.", e);
+ }
+ } catch (Exception e) {
+ LOGGER.warn("Online Accounts Signatures Trimming stopped working. Trying again. Report this error immediately to the developers.", e);
}
}
- } catch (DataException e) {
- LOGGER.warn(String.format("Repository issue trying to trim online accounts signatures: %s", e.getMessage()));
- } catch (InterruptedException e) {
- // Time to exit
+ } catch (Exception e) {
+ LOGGER.error("Online Accounts Signatures Trimming is not working! Not trying again. Restart ASAP. Report this error immediately to the developers.", e);
}
}
diff --git a/src/main/java/org/qortal/controller/repository/PruneManager.java b/src/main/java/org/qortal/controller/repository/PruneManager.java
index d48f85f7..8865668b 100644
--- a/src/main/java/org/qortal/controller/repository/PruneManager.java
+++ b/src/main/java/org/qortal/controller/repository/PruneManager.java
@@ -40,7 +40,7 @@ public class PruneManager {
}
public void start() {
- this.executorService = Executors.newCachedThreadPool(new DaemonThreadFactory());
+ this.executorService = Executors.newCachedThreadPool(new DaemonThreadFactory(Settings.getInstance().getPruningThreadPriority()));
if (Settings.getInstance().isTopOnly()) {
// Top-only-sync
diff --git a/src/main/java/org/qortal/data/account/AddressLevelPairing.java b/src/main/java/org/qortal/data/account/AddressLevelPairing.java
index f6156c0b..3b6fcf3f 100644
--- a/src/main/java/org/qortal/data/account/AddressLevelPairing.java
+++ b/src/main/java/org/qortal/data/account/AddressLevelPairing.java
@@ -33,9 +33,10 @@ public class AddressLevelPairing {
public int getLevel() {
return level;
}
+
@Override
public String toString() {
- return "SponsorshipReport{" +
+ return "AddressLevelPairing{" +
"address='" + address + '\'' +
", level=" + level +
'}';
diff --git a/src/main/java/org/qortal/data/arbitrary/ArbitraryResourceCache.java b/src/main/java/org/qortal/data/arbitrary/ArbitraryResourceCache.java
new file mode 100644
index 00000000..f838cdd7
--- /dev/null
+++ b/src/main/java/org/qortal/data/arbitrary/ArbitraryResourceCache.java
@@ -0,0 +1,26 @@
+package org.qortal.data.arbitrary;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ArbitraryResourceCache {
+ private ConcurrentHashMap> dataByService = new ConcurrentHashMap<>();
+ private ConcurrentHashMap levelByName = new ConcurrentHashMap<>();
+
+ private ArbitraryResourceCache() {}
+
+ private static ArbitraryResourceCache SINGLETON = new ArbitraryResourceCache();
+
+ public static ArbitraryResourceCache getInstance(){
+ return SINGLETON;
+ }
+
+ public ConcurrentHashMap getLevelByName() {
+ return levelByName;
+ }
+
+ public ConcurrentHashMap> getDataByService() {
+ return this.dataByService;
+ }
+}
diff --git a/src/main/java/org/qortal/network/Handshake.java b/src/main/java/org/qortal/network/Handshake.java
index 782ca2b7..081e79e6 100644
--- a/src/main/java/org/qortal/network/Handshake.java
+++ b/src/main/java/org/qortal/network/Handshake.java
@@ -269,7 +269,7 @@ public enum Handshake {
private static final int POW_DIFFICULTY_POST_131 = 2; // leading zero bits
- private static final ExecutorService responseExecutor = Executors.newFixedThreadPool(Settings.getInstance().getNetworkPoWComputePoolSize(), new DaemonThreadFactory("Network-PoW"));
+ private static final ExecutorService responseExecutor = Executors.newFixedThreadPool(Settings.getInstance().getNetworkPoWComputePoolSize(), new DaemonThreadFactory("Network-PoW", Settings.getInstance().getHandshakeThreadPriority()));
private static final byte[] ZERO_CHALLENGE = new byte[ChallengeMessage.CHALLENGE_LENGTH];
diff --git a/src/main/java/org/qortal/network/Network.java b/src/main/java/org/qortal/network/Network.java
index 0e5885ad..f500b2e8 100644
--- a/src/main/java/org/qortal/network/Network.java
+++ b/src/main/java/org/qortal/network/Network.java
@@ -53,7 +53,7 @@ public class Network {
/**
* How long between informational broadcasts to all connected peers, in milliseconds.
*/
- private static final long BROADCAST_INTERVAL = 60 * 1000L; // ms
+ private static final long BROADCAST_INTERVAL = 30 * 1000L; // ms
/**
* Maximum time since last successful connection for peer info to be propagated, in milliseconds.
*/
@@ -83,12 +83,12 @@ public class Network {
"node6.qortalnodes.live", "node7.qortalnodes.live", "node8.qortalnodes.live"
};
- private static final long NETWORK_EPC_KEEPALIVE = 10L; // seconds
+ private static final long NETWORK_EPC_KEEPALIVE = 5L; // seconds
public static final int MAX_SIGNATURES_PER_REPLY = 500;
public static final int MAX_BLOCK_SUMMARIES_PER_REPLY = 500;
- private static final long DISCONNECTION_CHECK_INTERVAL = 10 * 1000L; // milliseconds
+ private static final long DISCONNECTION_CHECK_INTERVAL = 20 * 1000L; // milliseconds
private static final int BROADCAST_CHAIN_TIP_DEPTH = 7; // Just enough to fill a SINGLE TCP packet (~1440 bytes)
@@ -164,11 +164,11 @@ public class Network {
maxPeers = Settings.getInstance().getMaxPeers();
// We'll use a cached thread pool but with more aggressive timeout.
- ExecutorService networkExecutor = new ThreadPoolExecutor(1,
+ ExecutorService networkExecutor = new ThreadPoolExecutor(2,
Settings.getInstance().getMaxNetworkThreadPoolSize(),
NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS,
new SynchronousQueue(),
- new NamedThreadFactory("Network-EPC"));
+ new NamedThreadFactory("Network-EPC", Settings.getInstance().getNetworkThreadPriority()));
networkEPC = new NetworkProcessor(networkExecutor);
}
diff --git a/src/main/java/org/qortal/repository/BlockArchiveWriter.java b/src/main/java/org/qortal/repository/BlockArchiveWriter.java
index e47aabbd..901dab89 100644
--- a/src/main/java/org/qortal/repository/BlockArchiveWriter.java
+++ b/src/main/java/org/qortal/repository/BlockArchiveWriter.java
@@ -153,13 +153,16 @@ public class BlockArchiveWriter {
int i = 0;
while (headerBytes.size() + bytes.size() < this.fileSizeTarget) {
+ // pause, since this can be a long process and other processes need to execute
+ Thread.sleep(Settings.getInstance().getArchivingPause());
+
if (Controller.isStopping()) {
return BlockArchiveWriteResult.STOPPING;
}
- if (Synchronizer.getInstance().isSynchronizing()) {
- Thread.sleep(1000L);
+
+ // wait until the Synchronizer stops
+ if( Synchronizer.getInstance().isSynchronizing() )
continue;
- }
int currentHeight = startHeight + i;
if (currentHeight > endHeight) {
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java
index 6b945aa7..9cec85b2 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBAccountRepository.java
@@ -1215,7 +1215,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
sponseeSql.append(")");
// Create a new array to hold both
- String[] combinedArray = new String[realRewardShareRecipients.length + 1];
+ Object[] combinedArray = new Object[realRewardShareRecipients.length + 1];
// Add the single string to the first position
combinedArray[0] = account;
@@ -1439,7 +1439,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
sql.append(String.join(", ", Collections.nCopies(addressCount, "?")));
sql.append(") ");
sql.append("AND a.account = tx.recipient AND a.public_key != ats.creator AND asset_id = 0 ");
- String[] sponsees = addresses.toArray(new String[addressCount]);
+ Object[] sponsees = addresses.toArray(new Object[addressCount]);
ResultSet buySellResultSet = this.repository.checkedExecute(sql.toString(), sponsees);
return buySellResultSet;
@@ -1456,7 +1456,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
sql.append(String.join(", ", Collections.nCopies(addressCount, "?")));
sql.append(") ");
sql.append("AND a.account != tx.recipient AND asset_id = 0 ");
- String[] sponsees = addresses.toArray(new String[addressCount]);
+ Object[] sponsees = addresses.toArray(new Object[addressCount]);
return this.repository.checkedExecute(sql.toString(), sponsees);
}
@@ -1490,7 +1490,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
txTypeTotalsSql.append(") and type in (10, 12, 40) ");
txTypeTotalsSql.append("group by type order by type");
- String[] sponsees = sponseeAddresses.toArray(new String[sponseeCount]);
+ Object[] sponsees = sponseeAddresses.toArray(new Object[sponseeCount]);
ResultSet txTypeResultSet = this.repository.checkedExecute(txTypeTotalsSql.toString(), sponsees);
return txTypeResultSet;
}
@@ -1502,7 +1502,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
avgBalanceSql.append(String.join(", ", Collections.nCopies(sponseeCount, "?")));
avgBalanceSql.append(") and ASSET_ID = 0");
- String[] sponsees = sponseeAddresses.toArray(new String[sponseeCount]);
+ Object[] sponsees = sponseeAddresses.toArray(new Object[sponseeCount]);
return this.repository.checkedExecute(avgBalanceSql.toString(), sponsees);
}
@@ -1538,7 +1538,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
namesSql.append(String.join(", ", Collections.nCopies(sponseeCount, "?")));
namesSql.append(")");
- String[] sponsees = sponseeAddresses.toArray(new String[sponseeCount]);
+ Object[] sponsees = sponseeAddresses.toArray(new Object[sponseeCount]);
ResultSet namesResultSet = this.repository.checkedExecute(namesSql.toString(), sponsees);
return namesResultSet;
}
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java
index e30d61bf..049e98aa 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBArbitraryRepository.java
@@ -7,6 +7,8 @@ import org.qortal.arbitrary.ArbitraryDataFile;
import org.qortal.arbitrary.metadata.ArbitraryDataTransactionMetadata;
import org.qortal.arbitrary.misc.Category;
import org.qortal.arbitrary.misc.Service;
+import org.qortal.controller.arbitrary.ArbitraryDataManager;
+import org.qortal.data.arbitrary.ArbitraryResourceCache;
import org.qortal.data.arbitrary.ArbitraryResourceData;
import org.qortal.data.arbitrary.ArbitraryResourceMetadata;
import org.qortal.data.arbitrary.ArbitraryResourceStatus;
@@ -18,6 +20,7 @@ import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.ArbitraryRepository;
import org.qortal.repository.DataException;
+import org.qortal.settings.Settings;
import org.qortal.transaction.ArbitraryTransaction;
import org.qortal.transaction.Transaction.ApprovalStatus;
import org.qortal.utils.Base58;
@@ -28,6 +31,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
public class HSQLDBArbitraryRepository implements ArbitraryRepository {
@@ -723,6 +727,50 @@ public class HSQLDBArbitraryRepository implements ArbitraryRepository {
public List searchArbitraryResources(Service service, String query, String identifier, List names, String title, String description, boolean prefixOnly,
List exactMatchNames, boolean defaultResource, SearchMode mode, Integer minLevel, Boolean followedOnly, Boolean excludeBlocked,
Boolean includeMetadata, Boolean includeStatus, Long before, Long after, Integer limit, Integer offset, Boolean reverse) throws DataException {
+
+ if(Settings.getInstance().isDbCacheEnabled()) {
+
+ List list
+ = HSQLDBCacheUtils.callCache(
+ ArbitraryResourceCache.getInstance(),
+ service, query, identifier, names, title, description, prefixOnly, exactMatchNames,
+ defaultResource, mode, minLevel, followedOnly, excludeBlocked, includeMetadata, includeStatus,
+ before, after, limit, offset, reverse);
+
+ if( !list.isEmpty() ) {
+ List results
+ = HSQLDBCacheUtils.filterList(
+ list,
+ ArbitraryResourceCache.getInstance().getLevelByName(),
+ Optional.ofNullable(mode),
+ Optional.ofNullable(service),
+ Optional.ofNullable(query),
+ Optional.ofNullable(identifier),
+ Optional.ofNullable(names),
+ Optional.ofNullable(title),
+ Optional.ofNullable(description),
+ prefixOnly,
+ Optional.ofNullable(exactMatchNames),
+ defaultResource,
+ Optional.ofNullable(minLevel),
+ Optional.ofNullable(() -> ListUtils.followedNames()),
+ Optional.ofNullable(ListUtils::blockedNames),
+ Optional.ofNullable(includeMetadata),
+ Optional.ofNullable(includeStatus),
+ Optional.ofNullable(before),
+ Optional.ofNullable(after),
+ Optional.ofNullable(limit),
+ Optional.ofNullable(offset),
+ Optional.ofNullable(reverse)
+ );
+
+ return results;
+ }
+ else {
+ LOGGER.info("Db Enabled Cache has zero candidates.");
+ }
+ }
+
StringBuilder sql = new StringBuilder(512);
List