diff --git a/src/main/java/org/qortal/RepositoryMaintenance.java b/src/main/java/org/qortal/RepositoryMaintenance.java
index c3ae0616..c0d5626d 100644
--- a/src/main/java/org/qortal/RepositoryMaintenance.java
+++ b/src/main/java/org/qortal/RepositoryMaintenance.java
@@ -6,7 +6,6 @@ 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;
@@ -42,7 +41,7 @@ public class RepositoryMaintenance {
LOGGER.info("Opening repository");
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
// If exception has no cause then repository is in use by some other process.
diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java
index a7d39d3c..0f27c2bc 100644
--- a/src/main/java/org/qortal/controller/Controller.java
+++ b/src/main/java/org/qortal/controller/Controller.java
@@ -1,7 +1,6 @@
package org.qortal.controller;
import java.awt.TrayIcon.MessageType;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
@@ -108,7 +107,7 @@ public class Controller extends Thread {
private static final long MISBEHAVIOUR_COOLOFF = 10 * 60 * 1000L; // ms
private static final int MAX_BLOCKCHAIN_TIP_AGE = 5; // blocks
private static final Object shutdownLock = new Object();
- private static final String repositoryUrlTemplate = "jdbc:hsqldb:file:%s" + File.separator + "blockchain;create=true;hsqldb.full_log_replay=true";
+
private static final long ARBITRARY_REQUEST_TIMEOUT = 5 * 1000L; // ms
private static final long NTP_PRE_SYNC_CHECK_PERIOD = 5 * 1000L; // ms
private static final long NTP_POST_SYNC_CHECK_PERIOD = 5 * 60 * 1000L; // ms
@@ -224,10 +223,6 @@ public class Controller extends Thread {
// Getters / setters
- public static String getRepositoryUrl() {
- return String.format(repositoryUrlTemplate, Settings.getInstance().getRepositoryPath());
- }
-
public long getBuildTimestamp() {
return this.buildTimestamp;
}
@@ -315,7 +310,7 @@ public class Controller extends Thread {
LOGGER.info("Starting repository");
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
// If exception has no cause then repository is in use by some other process.
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java
index 760d1ebc..f02ae3e4 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepository.java
@@ -76,9 +76,8 @@ public class HSQLDBRepository implements Repository {
// Constructors
- // NB: no visibility modifier so only callable from within same package
- /* package */ HSQLDBRepository(Connection connection) throws DataException {
- this.connection = connection;
+ /*package*/ HSQLDBRepository(Connection blockchainConnection, Connection nodeLocalConnection) throws DataException {
+ this.connection = blockchainConnection;
this.slowQueryThreshold = Settings.getInstance().getSlowQueryThreshold();
if (this.slowQueryThreshold != null)
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java
index 8561b698..bdeffa11 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBRepositoryFactory.java
@@ -1,5 +1,6 @@
package org.qortal.repository.hsqldb;
+import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -20,25 +21,44 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
private static final Logger LOGGER = LogManager.getLogger(HSQLDBRepositoryFactory.class);
private static final int POOL_SIZE = 100;
+ private static final String blockchainFileUrlTemplate = "jdbc:hsqldb:file:%s" + File.separator + "blockchain;create=true;hsqldb.full_log_replay=true";
+ private static final String blockchainMemoryUrl = "jdbc:hsqldb:mem:blockchain";
+
+ private static final String nodeLocalFileUrlTemplate = "jdbc:hsqldb:file:%s" + File.separator + "node-local;create=true;hsqldb.full_log_replay=true";
+ private static final String nodeLocalMemoryUrl = "jdbc:hsqldb:mem:node-local";
+
/** Log getConnection() calls that take longer than this. (ms) */
private static final long SLOW_CONNECTION_THRESHOLD = 1000L;
- private String connectionUrl;
- private HSQLDBPool connectionPool;
+ private final String repositoryPath;
+
+ private final String blockchainConnectionUrl;
+ private final String nodeLocalConnectionUrl;
+
+ private final HSQLDBPool blockchainConnectionPool;
+ private final HSQLDBPool nodeLocalConnectionPool;
/**
- * Constructs new RepositoryFactory using passed connectionUrl.
+ * Constructs new RepositoryFactory using passed repository path, or null for in-memory.
*
- * @param connectionUrl
+ * @param repositoryPath
* @throws DataException without throwable if repository in use by another process.
* @throws DataException with throwable if repository cannot be opened for some other reason.
*/
- public HSQLDBRepositoryFactory(String connectionUrl) throws DataException {
+ public HSQLDBRepositoryFactory(String repositoryPath) throws DataException {
+ this.repositoryPath = repositoryPath;
+
// one-time initialization goes in here
- this.connectionUrl = connectionUrl;
+ if (repositoryPath != null) {
+ this.blockchainConnectionUrl = String.format(blockchainFileUrlTemplate, repositoryPath);
+ this.nodeLocalConnectionUrl = String.format(nodeLocalFileUrlTemplate, repositoryPath);
+ } else {
+ this.blockchainConnectionUrl = blockchainMemoryUrl;
+ this.nodeLocalConnectionUrl = nodeLocalMemoryUrl;
+ }
// Check no-one else is accessing database
- try (Connection connection = DriverManager.getConnection(this.connectionUrl)) {
+ try (Connection connection = DriverManager.getConnection(this.blockchainConnectionUrl)) {
// We only need to check we can obtain connection. It will be auto-closed.
} catch (SQLException e) {
Throwable cause = e.getCause();
@@ -53,19 +73,27 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
throw new DataException("Unable to read repository: " + e.getMessage(), e);
// Attempt recovery?
- HSQLDBRepository.attemptRecovery(connectionUrl);
+ HSQLDBRepository.attemptRecovery(repositoryPath);
}
- this.connectionPool = new HSQLDBPool(POOL_SIZE);
- this.connectionPool.setUrl(this.connectionUrl);
+ this.blockchainConnectionPool = new HSQLDBPool(POOL_SIZE);
+ this.blockchainConnectionPool.setUrl(this.blockchainConnectionUrl);
- Properties properties = new Properties();
- properties.setProperty("close_result", "true"); // Auto-close old ResultSet if Statement creates new ResultSet
- this.connectionPool.setProperties(properties);
+ Properties blockchainProperties = new Properties();
+ blockchainProperties.setProperty("close_result", "true"); // Auto-close old ResultSet if Statement creates new ResultSet
+ this.blockchainConnectionPool.setProperties(blockchainProperties);
+
+ this.nodeLocalConnectionPool = new HSQLDBPool(POOL_SIZE);
+ this.nodeLocalConnectionPool.setUrl(this.nodeLocalConnectionUrl);
+
+ Properties nodeLocalProperties = new Properties();
+ nodeLocalProperties.setProperty("close_result", "true"); // Auto-close old ResultSet if Statement creates new ResultSet
+ this.nodeLocalConnectionPool.setProperties(nodeLocalProperties);
// Perform DB updates?
- try (final Connection connection = this.connectionPool.getConnection()) {
- HSQLDBDatabaseUpdates.updateDatabase(connection);
+ try (final Connection blockchainConnection = this.blockchainConnectionPool.getConnection();
+ final Connection nodeLocalConnection = this.nodeLocalConnectionPool.getConnection()) {
+ HSQLDBDatabaseUpdates.updateDatabase(blockchainConnection);
} catch (SQLException e) {
throw new DataException("Repository initialization error", e);
}
@@ -73,13 +101,13 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
@Override
public RepositoryFactory reopen() throws DataException {
- return new HSQLDBRepositoryFactory(this.connectionUrl);
+ return new HSQLDBRepositoryFactory(this.repositoryPath);
}
@Override
public Repository getRepository() throws DataException {
try {
- return new HSQLDBRepository(this.getConnection());
+ return new HSQLDBRepository(this.getBlockchainConnection(), this.getNodeLocalConnection());
} catch (SQLException e) {
throw new DataException("Repository instantiation error", e);
}
@@ -88,27 +116,54 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
@Override
public Repository tryRepository() throws DataException {
try {
- return new HSQLDBRepository(this.tryConnection());
+ Connection blockchainConnection = this.tryBlockchainConnection();
+ if (blockchainConnection == null)
+ return null;
+
+ Connection nodeLocalConnection = this.tryNodeLocalConnection();
+ if (nodeLocalConnection == null)
+ return null;
+
+ return new HSQLDBRepository(blockchainConnection, nodeLocalConnection);
} catch (SQLException e) {
throw new DataException("Repository instantiation error", e);
}
}
- private Connection getConnection() throws SQLException {
+ private Connection getBlockchainConnection() throws SQLException {
+ return getConnection(this.blockchainConnectionPool, "blockchain");
+ }
+
+ private Connection tryBlockchainConnection() throws SQLException {
+ return tryConnection(this.blockchainConnectionPool);
+ }
+
+ private Connection getNodeLocalConnection() throws SQLException {
+ return getConnection(this.nodeLocalConnectionPool, "node-local");
+ }
+
+ private Connection tryNodeLocalConnection() throws SQLException {
+ return tryConnection(this.nodeLocalConnectionPool);
+ }
+
+ private Connection getConnection(HSQLDBPool pool, String poolName) throws SQLException {
final long before = System.currentTimeMillis();
- Connection connection = this.connectionPool.getConnection();
+ Connection connection = pool.getConnection();
final long delay = System.currentTimeMillis() - before;
if (delay > SLOW_CONNECTION_THRESHOLD)
// This could be an indication of excessive repository use, or insufficient pool size
- LOGGER.warn(() -> String.format("Fetching repository connection from pool took %dms (threshold: %dms)", delay, SLOW_CONNECTION_THRESHOLD));
+ LOGGER.warn(() -> String.format("Fetching repository connection from %s pool took %dms (threshold: %dms)",
+ poolName,
+ delay,
+ SLOW_CONNECTION_THRESHOLD));
setupConnection(connection);
return connection;
}
- private Connection tryConnection() throws SQLException {
- Connection connection = this.connectionPool.tryConnection();
+ private Connection tryConnection(HSQLDBPool pool) throws SQLException {
+ Connection connection = pool.tryConnection();
if (connection == null)
return null;
@@ -126,10 +181,16 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
public void close() throws DataException {
try {
// Close all existing connections immediately
- this.connectionPool.close(0);
+ this.blockchainConnectionPool.close(0);
+ this.nodeLocalConnectionPool.close(0);
// Now that all connections are closed, create a dedicated connection to shut down repository
- try (Connection connection = DriverManager.getConnection(this.connectionUrl);
+ try (Connection connection = DriverManager.getConnection(this.blockchainConnectionUrl);
+ Statement stmt = connection.createStatement()) {
+ stmt.execute("SHUTDOWN");
+ }
+
+ try (Connection connection = DriverManager.getConnection(this.nodeLocalConnectionUrl);
Statement stmt = connection.createStatement()) {
stmt.execute("SHUTDOWN");
}
diff --git a/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java b/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java
index 1781f719..d62eb104 100644
--- a/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java
+++ b/src/test/java/org/qortal/test/apps/DecodeOnlineAccounts.java
@@ -7,7 +7,6 @@ import org.bitcoinj.core.Base58;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
import org.qortal.block.BlockChain;
-import org.qortal.controller.Controller;
import org.qortal.data.account.RewardShareData;
import org.qortal.gui.Gui;
import org.qortal.repository.DataException;
@@ -65,7 +64,7 @@ public class DecodeOnlineAccounts {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
System.err.println("Couldn't connect to repository: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/apps/orphan.java b/src/test/java/org/qortal/test/apps/orphan.java
index f847f98d..f081a880 100644
--- a/src/test/java/org/qortal/test/apps/orphan.java
+++ b/src/test/java/org/qortal/test/apps/orphan.java
@@ -3,7 +3,6 @@ import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.qortal.block.BlockChain;
-import org.qortal.controller.Controller;
import org.qortal.repository.DataException;
import org.qortal.repository.RepositoryFactory;
import org.qortal.repository.RepositoryManager;
@@ -32,7 +31,7 @@ public class orphan {
int targetHeight = Integer.parseInt(args[argIndex]);
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
System.err.println("Couldn't connect to repository: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/btcacct/BuildP2SH.java b/src/test/java/org/qortal/test/btcacct/BuildP2SH.java
index 6b6b16e1..d7b3992c 100644
--- a/src/test/java/org/qortal/test/btcacct/BuildP2SH.java
+++ b/src/test/java/org/qortal/test/btcacct/BuildP2SH.java
@@ -11,7 +11,6 @@ import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.script.Script.ScriptType;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.qortal.controller.Controller;
import org.qortal.crosschain.BTC;
import org.qortal.crosschain.BTCP2SH;
import org.qortal.crypto.Crypto;
@@ -85,7 +84,7 @@ public class BuildP2SH {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
throw new RuntimeException("Repository startup issue: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/btcacct/CheckP2SH.java b/src/test/java/org/qortal/test/btcacct/CheckP2SH.java
index e7d96bc1..0556fde8 100644
--- a/src/test/java/org/qortal/test/btcacct/CheckP2SH.java
+++ b/src/test/java/org/qortal/test/btcacct/CheckP2SH.java
@@ -13,7 +13,6 @@ import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script.ScriptType;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.qortal.controller.Controller;
import org.qortal.crosschain.BTC;
import org.qortal.crosschain.BTCP2SH;
import org.qortal.crosschain.BitcoinException;
@@ -94,7 +93,7 @@ public class CheckP2SH {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
throw new RuntimeException("Repository startup issue: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/btcacct/DeployAT.java b/src/test/java/org/qortal/test/btcacct/DeployAT.java
index ef5a0295..9e8ba4ad 100644
--- a/src/test/java/org/qortal/test/btcacct/DeployAT.java
+++ b/src/test/java/org/qortal/test/btcacct/DeployAT.java
@@ -5,7 +5,6 @@ import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.qortal.account.PrivateKeyAccount;
import org.qortal.asset.Asset;
-import org.qortal.controller.Controller;
import org.qortal.crosschain.BTCACCT;
import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.DeployAtTransactionData;
@@ -95,7 +94,7 @@ public class DeployAT {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
throw new RuntimeException("Repository startup issue: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/btcacct/Redeem.java b/src/test/java/org/qortal/test/btcacct/Redeem.java
index 0ca20608..fbb34c5f 100644
--- a/src/test/java/org/qortal/test/btcacct/Redeem.java
+++ b/src/test/java/org/qortal/test/btcacct/Redeem.java
@@ -16,7 +16,6 @@ import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script.ScriptType;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.qortal.controller.Controller;
import org.qortal.crosschain.BTC;
import org.qortal.crosschain.BTCP2SH;
import org.qortal.crosschain.BitcoinException;
@@ -98,7 +97,7 @@ public class Redeem {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
throw new RuntimeException("Repository startup issue: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/btcacct/Refund.java b/src/test/java/org/qortal/test/btcacct/Refund.java
index 184985d9..368c0bcc 100644
--- a/src/test/java/org/qortal/test/btcacct/Refund.java
+++ b/src/test/java/org/qortal/test/btcacct/Refund.java
@@ -16,7 +16,6 @@ import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.script.Script.ScriptType;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.qortal.controller.Controller;
import org.qortal.crosschain.BTC;
import org.qortal.crosschain.BTCP2SH;
import org.qortal.crosschain.BitcoinException;
@@ -98,7 +97,7 @@ public class Refund {
}
try {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Controller.getRepositoryUrl());
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(Settings.getInstance().getRepositoryPath());
RepositoryManager.setRepositoryFactory(repositoryFactory);
} catch (DataException e) {
throw new RuntimeException("Repository startup issue: " + e.getMessage());
diff --git a/src/test/java/org/qortal/test/common/Common.java b/src/test/java/org/qortal/test/common/Common.java
index 24c86690..18a21ba2 100644
--- a/src/test/java/org/qortal/test/common/Common.java
+++ b/src/test/java/org/qortal/test/common/Common.java
@@ -46,10 +46,6 @@ public class Common {
private static final Logger LOGGER = LogManager.getLogger(Common.class);
- public static final String testConnectionUrl = "jdbc:hsqldb:mem:testdb";
- // For debugging, use this instead to write DB to disk for examination:
- // public static final String testConnectionUrl = "jdbc:hsqldb:file:testdb/blockchain;create=true";
-
public static final String testSettingsFilename = "test-settings-v2.json";
static {
@@ -188,7 +184,7 @@ public class Common {
@BeforeClass
public static void setRepository() throws DataException {
- RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(testConnectionUrl);
+ RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(null);
RepositoryManager.setRepositoryFactory(repositoryFactory);
}