forked from Qortal/qortal
Browse Source
Initially just deleting old and unused AT states, to get this table under control. I have had to delete them individually as the table can't handle complex queries due to its size. Nodes in pruning mode will be unable to serve older blocks to peers.block-archive
CalDescent
3 years ago
8 changed files with 306 additions and 1 deletions
@ -0,0 +1,95 @@
|
||||
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 AtStatesPruner implements Runnable { |
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(AtStatesPruner.class); |
||||
|
||||
@Override |
||||
public void run() { |
||||
Thread.currentThread().setName("AT States pruner"); |
||||
|
||||
if (!Settings.getInstance().isPruningEnabled()) { |
||||
return; |
||||
} |
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) { |
||||
int pruneStartHeight = repository.getATRepository().getAtPruneHeight(); |
||||
|
||||
// repository.getATRepository().prepareForAtStatePruning();
|
||||
// repository.saveChanges();
|
||||
|
||||
while (!Controller.isStopping()) { |
||||
repository.discardChanges(); |
||||
|
||||
Thread.sleep(Settings.getInstance().getAtStatesPruneInterval()); |
||||
|
||||
if (PruneManager.getInstance().getBuiltLatestATStates() == false) { |
||||
// Wait for latest AT states table to be built first
|
||||
// This has a dependency on the AtStatesTrimmer running,
|
||||
// which should be okay, given that it isn't something
|
||||
// is disabled in normal operation.
|
||||
continue; |
||||
} |
||||
|
||||
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; |
||||
|
||||
long currentPrunableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime(); |
||||
// We want to keep AT states near the tip of our copy of blockchain so we can process/orphan nearby blocks
|
||||
long chainPrunableTimestamp = chainTip.getTimestamp() - Settings.getInstance().getAtStatesMaxLifetime(); |
||||
|
||||
long upperPrunableTimestamp = Math.min(currentPrunableTimestamp, chainPrunableTimestamp); |
||||
int upperPrunableHeight = repository.getBlockRepository().getHeightFromTimestamp(upperPrunableTimestamp); |
||||
|
||||
int upperBatchHeight = pruneStartHeight + Settings.getInstance().getAtStatesPruneBatchSize(); |
||||
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight); |
||||
|
||||
if (pruneStartHeight >= upperPruneHeight) |
||||
continue; |
||||
|
||||
LOGGER.debug(String.format("Pruning AT states between blocks %d and %d...", pruneStartHeight, upperPruneHeight)); |
||||
|
||||
int numAtStatesPruned = repository.getATRepository().pruneAtStates(pruneStartHeight, upperPruneHeight); |
||||
repository.saveChanges(); |
||||
|
||||
if (numAtStatesPruned > 0) { |
||||
final int finalPruneStartHeight = pruneStartHeight; |
||||
LOGGER.debug(() -> String.format("Pruned %d AT state%s between blocks %d and %d", |
||||
numAtStatesPruned, (numAtStatesPruned != 1 ? "s" : ""), |
||||
finalPruneStartHeight, upperPruneHeight)); |
||||
} else { |
||||
// Can we move onto next batch?
|
||||
if (upperPrunableHeight > upperBatchHeight) { |
||||
pruneStartHeight = upperBatchHeight; |
||||
repository.getATRepository().setAtPruneHeight(pruneStartHeight); |
||||
repository.getATRepository().prepareForAtStatePruning(); |
||||
repository.saveChanges(); |
||||
|
||||
final int finalPruneStartHeight = pruneStartHeight; |
||||
LOGGER.debug(() -> String.format("Bumping AT state base prune height to %d", finalPruneStartHeight)); |
||||
} |
||||
} |
||||
} |
||||
} catch (DataException e) { |
||||
LOGGER.warn(String.format("Repository issue trying to prune AT states: %s", e.getMessage())); |
||||
} catch (InterruptedException e) { |
||||
// Time to exit
|
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@
|
||||
package org.qortal.controller.pruning; |
||||
|
||||
import org.qortal.controller.Controller; |
||||
|
||||
import org.qortal.data.block.BlockData; |
||||
import org.qortal.repository.DataException; |
||||
import org.qortal.repository.Repository; |
||||
import org.qortal.settings.Settings; |
||||
import org.qortal.utils.DaemonThreadFactory; |
||||
|
||||
import java.util.concurrent.ExecutorService; |
||||
import java.util.concurrent.Executors; |
||||
|
||||
public class PruneManager { |
||||
|
||||
private static PruneManager instance; |
||||
|
||||
private boolean pruningEnabled = Settings.getInstance().isPruningEnabled(); |
||||
private int pruneBlockLimit = Settings.getInstance().getPruneBlockLimit(); |
||||
private boolean builtLatestATStates = false; |
||||
|
||||
private PruneManager() { |
||||
// Start individual pruning processes
|
||||
ExecutorService pruneExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory()); |
||||
pruneExecutor.execute(new AtStatesPruner()); |
||||
} |
||||
|
||||
public static synchronized PruneManager getInstance() { |
||||
if (instance == null) |
||||
instance = new PruneManager(); |
||||
|
||||
return instance; |
||||
} |
||||
|
||||
public boolean isBlockPruned(int height, Repository repository) throws DataException { |
||||
if (!this.pruningEnabled) { |
||||
return false; |
||||
} |
||||
|
||||
BlockData chainTip = Controller.getInstance().getChainTip(); |
||||
if (chainTip == null) { |
||||
throw new DataException("Unable to determine chain tip when checking if a block is pruned"); |
||||
} |
||||
|
||||
final int ourLatestHeight = chainTip.getHeight(); |
||||
final int latestUnprunedHeight = ourLatestHeight - this.pruneBlockLimit; |
||||
|
||||
return (height < latestUnprunedHeight); |
||||
} |
||||
|
||||
|
||||
public void setBuiltLatestATStates(boolean builtLatestATStates) { |
||||
this.builtLatestATStates = builtLatestATStates; |
||||
} |
||||
|
||||
public boolean getBuiltLatestATStates() { |
||||
return this.builtLatestATStates; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue