diff --git a/src/main/java/org/qortal/RepositoryMaintenance.java b/src/main/java/org/qortal/RepositoryMaintenance.java new file mode 100644 index 00000000..c3ae0616 --- /dev/null +++ b/src/main/java/org/qortal/RepositoryMaintenance.java @@ -0,0 +1,75 @@ +package org.qortal; + +import java.security.Security; + +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.controller.Controller; +import org.qortal.repository.DataException; +import org.qortal.repository.Repository; +import org.qortal.repository.RepositoryFactory; +import org.qortal.repository.RepositoryManager; +import org.qortal.repository.hsqldb.HSQLDBRepositoryFactory; +import org.qortal.settings.Settings; + +public class RepositoryMaintenance { + + static { + // This must go before any calls to LogManager/Logger + System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); + } + + private static final Logger LOGGER = LogManager.getLogger(RepositoryMaintenance.class); + + public static void main(String[] args) { + LOGGER.info("Repository maintenance starting up..."); + + Security.insertProviderAt(new BouncyCastleProvider(), 0); + Security.insertProviderAt(new BouncyCastleJsseProvider(), 1); + + // Load/check settings, which potentially sets up blockchain config, etc. + try { + if (args.length > 0) + Settings.fileInstance(args[0]); + else + Settings.getInstance(); + } catch (Throwable t) { + LOGGER.error("Settings file error: " + t.getMessage()); + System.exit(2); + } + + LOGGER.info("Opening repository"); + try { + RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl()); + RepositoryManager.setRepositoryFactory(repositoryFactory); + } catch (DataException e) { + // If exception has no cause then repository is in use by some other process. + if (e.getCause() == null) { + LOGGER.info("Repository in use by another process?"); + } else { + LOGGER.error("Unable to start repository", e); + } + + System.exit(1); + } + + LOGGER.info("Starting repository periodic maintenance. This can take a while..."); + try (final Repository repository = RepositoryManager.getRepository()) { + repository.performPeriodicMaintenance(); + + LOGGER.info("Repository periodic maintenance completed"); + } catch (DataException e) { + LOGGER.error("Repository periodic maintenance failed", e); + } + + try { + LOGGER.info("Shutting down repository"); + RepositoryManager.closeRepositoryFactory(); + } catch (DataException e) { + LOGGER.error("Error occurred while shutting down repository", e); + } + } + +} diff --git a/src/main/java/org/qortal/repository/Repository.java b/src/main/java/org/qortal/repository/Repository.java index aecf4ef0..71936373 100644 --- a/src/main/java/org/qortal/repository/Repository.java +++ b/src/main/java/org/qortal/repository/Repository.java @@ -45,4 +45,6 @@ public interface Repository extends AutoCloseable { public void backup(boolean quick) throws DataException; + public void performPeriodicMaintenance() throws DataException; + } diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java index ad0b6ec9..5f527540 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java @@ -341,6 +341,16 @@ public class HSQLDBRepository implements Repository { } } + @Override + public void performPeriodicMaintenance() throws DataException { + // Defrag DB - takes a while! + try (Statement stmt = this.connection.createStatement()) { + stmt.execute("CHECKPOINT DEFRAG"); + } catch (SQLException e) { + throw new DataException("Unable to defrag repository"); + } + } + /** Returns DB pathname from passed connection URL. If memory DB, returns "mem". */ private static String getDbPathname(String connectionUrl) { Pattern pattern = Pattern.compile("hsqldb:(mem|file):(.*?)(;|$)");