forked from Qortal/qortal
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e93b262049 |
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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 <tt>connectionUrl</tt>.
|
||||
* Constructs new RepositoryFactory using passed repository path, or null for in-memory.
|
||||
*
|
||||
* @param connectionUrl
|
||||
* @param repositoryPath
|
||||
* @throws DataException <i>without throwable</i> if repository in use by another process.
|
||||
* @throws DataException <i>with throwable</i> 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");
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user