Browse Source

Prune all blocks up until the blockPruneLimit

By default, this leaves only the last 1450 blocks in the database. Only applies when pruning mode is enabled.
block-archive
CalDescent 3 years ago
parent
commit
1b4c75a76e
  1. 86
      src/main/java/org/qortal/controller/pruning/BlockPruner.java
  2. 1
      src/main/java/org/qortal/controller/pruning/PruneManager.java
  3. 14
      src/main/java/org/qortal/repository/BlockRepository.java
  4. 47
      src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java
  5. 1
      src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java
  6. 37
      src/main/java/org/qortal/settings/Settings.java

86
src/main/java/org/qortal/controller/pruning/BlockPruner.java

@ -0,0 +1,86 @@
package org.qortal.controller.pruning;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
public class BlockPruner implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(BlockPruner.class);
@Override
public void run() {
Thread.currentThread().setName("Block pruner");
if (!Settings.getInstance().isPruningEnabled()) {
return;
}
try (final Repository repository = RepositoryManager.getRepository()) {
int pruneStartHeight = repository.getBlockRepository().getBlockPruneHeight();
while (!Controller.isStopping()) {
repository.discardChanges();
Thread.sleep(Settings.getInstance().getBlockPruneInterval());
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 (Controller.getInstance().isSynchronizing())
continue;
// Prune all blocks up until our latest minus pruneBlockLimit
final int ourLatestHeight = chainTip.getHeight();
final int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
int upperBatchHeight = pruneStartHeight + Settings.getInstance().getBlockPruneBatchSize();
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
if (pruneStartHeight >= upperPruneHeight) {
continue;
}
LOGGER.debug(String.format("Pruning blocks between %d and %d...", pruneStartHeight, upperPruneHeight));
int numBlocksPruned = repository.getBlockRepository().pruneBlocks(pruneStartHeight, upperPruneHeight);
repository.saveChanges();
if (numBlocksPruned > 0) {
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Pruned %d block%s between %d and %d",
numBlocksPruned, (numBlocksPruned != 1 ? "s" : ""),
finalPruneStartHeight, upperPruneHeight));
} else {
// Can we move onto next batch?
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getBlockRepository().setBlockPruneHeight(pruneStartHeight);
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Bumping block 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
Thread.sleep(10*60*1000L);
}
}
}
} catch (DataException e) {
LOGGER.warn(String.format("Repository issue trying to prune blocks: %s", e.getMessage()));
} catch (InterruptedException e) {
// Time to exit
}
}
}

1
src/main/java/org/qortal/controller/pruning/PruneManager.java

@ -23,6 +23,7 @@ public class PruneManager {
// Start individual pruning processes // Start individual pruning processes
ExecutorService pruneExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory()); ExecutorService pruneExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory());
pruneExecutor.execute(new AtStatesPruner()); pruneExecutor.execute(new AtStatesPruner());
pruneExecutor.execute(new BlockPruner());
} }
public static synchronized PruneManager getInstance() { public static synchronized PruneManager getInstance() {

14
src/main/java/org/qortal/repository/BlockRepository.java

@ -166,6 +166,20 @@ public interface BlockRepository {
*/ */
public BlockData getDetachedBlockSignature(int startHeight) throws DataException; public BlockData getDetachedBlockSignature(int startHeight) throws DataException;
/** Returns height of first prunable block. */
public int getBlockPruneHeight() throws DataException;
/** Sets new base height for block pruning.
* <p>
* NOTE: performs implicit <tt>repository.saveChanges()</tt>.
*/
public void setBlockPruneHeight(int pruneHeight) throws DataException;
/** Prunes full block data between passed heights. Returns number of pruned rows. */
public int pruneBlocks(int minHeight, int maxHeight) throws DataException;
/** /**
* Saves block into repository. * Saves block into repository.
* *

47
src/main/java/org/qortal/repository/hsqldb/HSQLDBBlockRepository.java

@ -509,6 +509,53 @@ public class HSQLDBBlockRepository implements BlockRepository {
} }
} }
@Override
public int getBlockPruneHeight() throws DataException {
String sql = "SELECT block_prune_height FROM DatabaseInfo";
try (ResultSet resultSet = this.repository.checkedExecute(sql)) {
if (resultSet == null)
return 0;
return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch block prune height from repository", e);
}
}
@Override
public void setBlockPruneHeight(int pruneHeight) throws DataException {
// trimHeightsLock is to prevent concurrent update on DatabaseInfo
// that could result in "transaction rollback: serialization failure"
synchronized (this.repository.trimHeightsLock) {
String updateSql = "UPDATE DatabaseInfo SET block_prune_height = ?";
try {
this.repository.executeCheckedUpdate(updateSql, pruneHeight);
this.repository.saveChanges();
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to set block prune height in repository", e);
}
}
}
@Override
public int pruneBlocks(int minHeight, int maxHeight) throws DataException {
// Don't prune the genesis block
if (minHeight <= 1) {
minHeight = 2;
}
try {
return this.repository.delete("Blocks", "height BETWEEN ? AND ?", minHeight, maxHeight);
} catch (SQLException e) {
throw new DataException("Unable to prune blocks from repository", e);
}
}
@Override @Override
public BlockData getDetachedBlockSignature(int startHeight) throws DataException { public BlockData getDetachedBlockSignature(int startHeight) throws DataException {
String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks " String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks "

1
src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabaseUpdates.java

@ -870,6 +870,7 @@ public class HSQLDBDatabaseUpdates {
case 35: case 35:
// Support for pruning // Support for pruning
stmt.execute("ALTER TABLE DatabaseInfo ADD AT_prune_height INT NOT NULL DEFAULT 0"); stmt.execute("ALTER TABLE DatabaseInfo ADD AT_prune_height INT NOT NULL DEFAULT 0");
stmt.execute("ALTER TABLE DatabaseInfo ADD block_prune_height INT NOT NULL DEFAULT 0");
break; break;
default: default:

37
src/main/java/org/qortal/settings/Settings.java

@ -106,23 +106,31 @@ public class Settings {
/** Max number of AT states to trim in one go. */ /** Max number of AT states to trim in one go. */
private int atStatesTrimLimit = 4000; // records private int atStatesTrimLimit = 4000; // records
/** How often to attempt online accounts signatures trimming (ms). */
private long onlineSignaturesTrimInterval = 9876L; // milliseconds
/** Block height range to scan for trimmable online accounts signatures.<br>
* This has a significant effect on execution time. */
private int onlineSignaturesTrimBatchSize = 100; // blocks
/** Whether we should prune old data to reduce database size /** Whether we should prune old data to reduce database size
* This prevents the node from being able to serve older blocks */ * This prevents the node from being able to serve older blocks */
private boolean pruningEnabled = false; private boolean pruningEnabled = false;
/** The amount of recent blocks we should keep when pruning */ /** The amount of recent blocks we should keep when pruning */
private int pruneBlockLimit = 1440; private int pruneBlockLimit = 1450;
/** How often to attempt AT state pruning (ms). */ /** How often to attempt AT state pruning (ms). */
private long atStatesPruneInterval = 3219L; // milliseconds private long atStatesPruneInterval = 3219L; // milliseconds
/** Block height range to scan for trimmable AT states.<br> /** Block height range to scan for prunable AT states.<br>
* This has a significant effect on execution time. */ * This has a significant effect on execution time. */
private int atStatesPruneBatchSize = 10; // blocks private int atStatesPruneBatchSize = 10; // blocks
/** How often to attempt online accounts signatures trimming (ms). */ /** How often to attempt block pruning (ms). */
private long onlineSignaturesTrimInterval = 9876L; // milliseconds private long blockPruneInterval = 3219L; // milliseconds
/** Block height range to scan for trimmable online accounts signatures.<br> /** Block height range to scan for prunable blocks.<br>
* This has a significant effect on execution time. */ * This has a significant effect on execution time. */
private int onlineSignaturesTrimBatchSize = 100; // blocks private int blockPruneBatchSize = 10000; // blocks
// Peer-to-peer related // Peer-to-peer related
private boolean isTestNet = false; private boolean isTestNet = false;
@ -540,6 +548,15 @@ public class Settings {
return this.atStatesTrimLimit; return this.atStatesTrimLimit;
} }
public long getOnlineSignaturesTrimInterval() {
return this.onlineSignaturesTrimInterval;
}
public int getOnlineSignaturesTrimBatchSize() {
return this.onlineSignaturesTrimBatchSize;
}
public boolean isPruningEnabled() { public boolean isPruningEnabled() {
return this.pruningEnabled; return this.pruningEnabled;
} }
@ -556,12 +573,12 @@ public class Settings {
return this.atStatesPruneBatchSize; return this.atStatesPruneBatchSize;
} }
public long getOnlineSignaturesTrimInterval() { public long getBlockPruneInterval() {
return this.onlineSignaturesTrimInterval; return this.blockPruneInterval;
} }
public int getOnlineSignaturesTrimBatchSize() { public int getBlockPruneBatchSize() {
return this.onlineSignaturesTrimBatchSize; return this.blockPruneBatchSize;
} }
} }

Loading…
Cancel
Save