diff --git a/pom.xml b/pom.xml index 1289f02e..d17cc279 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ 0.17 3.3.1 3.6.0 - 3.5.1 + 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/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 810c8e32..27341d1e 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -500,7 +500,6 @@ public class Controller extends Thread { @Override public void run() { Thread.currentThread().setName("Shutdown hook"); - Controller.getInstance().shutdown(); } }); @@ -580,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/repository/BlockArchiver.java b/src/main/java/org/qortal/controller/repository/BlockArchiver.java index 12793f89..0de6e38e 100644 --- a/src/main/java/org/qortal/controller/repository/BlockArchiver.java +++ b/src/main/java/org/qortal/controller/repository/BlockArchiver.java @@ -21,7 +21,7 @@ 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"); @@ -52,9 +52,29 @@ public class BlockArchiver implements Runnable { Thread.sleep(Settings.getInstance().getArchiveInterval()); +<<<<<<< HEAD BlockData chainTip = Controller.getInstance().getChainTip(); if (chainTip == null || NTP.getTime() == null) { continue; +======= + // 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); // 2 hour around 100 blocks + 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; +>>>>>>> alphax/master } // Don't even attempt if we're mid-sync as our repository requests will be delayed for ages diff --git a/src/main/java/org/qortal/network/Network.java b/src/main/java/org/qortal/network/Network.java index d80892b3..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) diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java index 67daa905..4abad781 100644 --- a/src/main/java/org/qortal/settings/Settings.java +++ b/src/main/java/org/qortal/settings/Settings.java @@ -197,21 +197,21 @@ public class Settings { /** Target number of outbound connections to peers we should make. */ private int minOutboundPeers = 32; /** Maximum number of peer connections we allow. */ - private int maxPeers = 60; + private int maxPeers = 64; /** Number of slots to reserve for short-lived QDN data transfers */ private int maxDataPeers = 5; /** Maximum number of threads for network engine. */ - private int maxNetworkThreadPoolSize = 620; + private int maxNetworkThreadPoolSize = 512; /** Maximum number of threads for network proof-of-work compute, used during handshaking. */ - private int networkPoWComputePoolSize = 2; + private int networkPoWComputePoolSize = 4; /** Maximum number of retry attempts if a peer fails to respond with the requested data */ - private int maxRetries = 2; + private int maxRetries = 3; /** The number of seconds of no activity before recovery mode begins */ public long recoveryModeTimeout = 9999999999999L; /** Minimum peer version number required in order to sync with them */ - private String minPeerVersion = "4.5.2"; + private String minPeerVersion = "4.6.0"; /** Whether to allow connections with peers below minPeerVersion * If true, we won't sync with them but they can still sync with us, and will show in the peers list * If false, sync will be blocked both ways, and they will not appear in the peers list */