mirror of
https://github.com/Qortal/qortal.git
synced 2025-03-14 03:22:32 +00:00
Added initial version of ReindexManager
This commit is contained in:
parent
9574100a08
commit
5b1f05d1d9
212
src/main/java/org/qortal/repository/ReindexManager.java
Normal file
212
src/main/java/org/qortal/repository/ReindexManager.java
Normal file
@ -0,0 +1,212 @@
|
||||
package org.qortal.repository;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.block.Block;
|
||||
import org.qortal.block.GenesisBlock;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.data.block.BlockArchiveData;
|
||||
import org.qortal.data.block.BlockData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.transaction.Transaction;
|
||||
import org.qortal.transform.block.BlockTransformation;
|
||||
import org.qortal.utils.Base58;
|
||||
import org.qortal.utils.NTP;
|
||||
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class ReindexManager {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(ReindexManager.class);
|
||||
|
||||
private Repository repository;
|
||||
|
||||
private final int pruneAndTrimBlockInterval = 2000;
|
||||
private final int maintenanceBlockInterval = 50000;
|
||||
|
||||
private boolean resume = false;
|
||||
|
||||
public ReindexManager() {
|
||||
|
||||
}
|
||||
|
||||
public void reindex() throws DataException {
|
||||
try {
|
||||
this.runPreChecks();
|
||||
this.rebuildRepository();
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
this.repository = repository;
|
||||
this.requestCheckpoint();
|
||||
this.processGenesisBlock();
|
||||
this.processBlocks();
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
throw new DataException("Interrupted before complete");
|
||||
}
|
||||
}
|
||||
|
||||
private void runPreChecks() throws DataException, InterruptedException {
|
||||
LOGGER.info("Running pre-checks...");
|
||||
if (Settings.getInstance().isTopOnly()) {
|
||||
throw new DataException("Reindexing not supported in top-only mode. Please bootstrap or resync from genesis.");
|
||||
}
|
||||
if (Settings.getInstance().isLite()) {
|
||||
throw new DataException("Reindexing not supported in lite mode.");
|
||||
}
|
||||
|
||||
while (NTP.getTime() == null) {
|
||||
LOGGER.info("Waiting for NTP...");
|
||||
Thread.sleep(5000L);
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuildRepository() throws DataException {
|
||||
if (resume) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Rebuilding repository...");
|
||||
RepositoryManager.rebuild();
|
||||
}
|
||||
|
||||
private void requestCheckpoint() {
|
||||
RepositoryManager.setRequestedCheckpoint(Boolean.TRUE);
|
||||
}
|
||||
|
||||
private void processGenesisBlock() throws DataException, InterruptedException {
|
||||
if (resume) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Processing genesis block...");
|
||||
|
||||
GenesisBlock genesisBlock = GenesisBlock.getInstance(repository);
|
||||
|
||||
// Add Genesis Block to blockchain
|
||||
genesisBlock.process();
|
||||
|
||||
this.repository.saveChanges();
|
||||
}
|
||||
|
||||
private void processBlocks() throws DataException {
|
||||
LOGGER.info("Processing blocks...");
|
||||
|
||||
int height = this.repository.getBlockRepository().getBlockchainHeight();
|
||||
while (true) {
|
||||
height++;
|
||||
|
||||
boolean processed = this.processBlock(height);
|
||||
if (!processed) {
|
||||
LOGGER.info("Block {} couldn't be processed. If this is the last archived block, then the process is complete.", height);
|
||||
break; // TODO: check if complete
|
||||
}
|
||||
|
||||
// Prune and trim regularly, leaving a buffer
|
||||
if (height >= pruneAndTrimBlockInterval*2 && height % pruneAndTrimBlockInterval == 0) {
|
||||
int startHeight = Math.max(height - pruneAndTrimBlockInterval*2, 2);
|
||||
int endHeight = height - pruneAndTrimBlockInterval;
|
||||
LOGGER.info("Pruning and trimming blocks {} to {}...", startHeight, endHeight);
|
||||
this.repository.getATRepository().rebuildLatestAtStates(height - 250);
|
||||
this.repository.saveChanges();
|
||||
this.prune(startHeight, endHeight);
|
||||
this.trim(startHeight, endHeight);
|
||||
}
|
||||
|
||||
// Run repository maintenance regularly, to keep blockchain.data size down
|
||||
if (height % maintenanceBlockInterval == 0) {
|
||||
this.runRepositoryMaintenance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processBlock(int height) throws DataException {
|
||||
Block block = this.fetchBlock(height);
|
||||
if (block == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transactions are stored without approval status so determine that now
|
||||
for (Transaction transaction : block.getTransactions())
|
||||
transaction.setInitialApprovalStatus();
|
||||
|
||||
// It's best not to run preProcess() until there is a reason to
|
||||
// block.preProcess();
|
||||
|
||||
Block.ValidationResult validationResult = block.isValid();
|
||||
if (validationResult != Block.ValidationResult.OK) {
|
||||
throw new DataException(String.format("Invalid block at height %d: %s", height, validationResult));
|
||||
}
|
||||
|
||||
// Save transactions attached to this block
|
||||
for (Transaction transaction : block.getTransactions()) {
|
||||
TransactionData transactionData = transaction.getTransactionData();
|
||||
this.repository.getTransactionRepository().save(transactionData);
|
||||
}
|
||||
|
||||
block.process();
|
||||
|
||||
LOGGER.info(String.format("Reindexed block height %d, sig %.8s", block.getBlockData().getHeight(), Base58.encode(block.getBlockData().getSignature())));
|
||||
|
||||
// Add to block archive table, since this originated from the archive but the chainstate has to be rebuilt
|
||||
this.addToBlockArchive(block.getBlockData());
|
||||
|
||||
this.repository.saveChanges();
|
||||
|
||||
Controller.getInstance().onNewBlock(block.getBlockData());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Block fetchBlock(int height) {
|
||||
BlockTransformation b = BlockArchiveReader.getInstance().fetchBlockAtHeight(height);
|
||||
if (b != null) {
|
||||
if (b.getAtStatesHash() != null) {
|
||||
return new Block(this.repository, b.getBlockData(), b.getTransactions(), b.getAtStatesHash());
|
||||
}
|
||||
else {
|
||||
return new Block(this.repository, b.getBlockData(), b.getTransactions(), b.getAtStates());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void addToBlockArchive(BlockData blockData) throws DataException {
|
||||
// Write the signature and height into the BlockArchive table
|
||||
BlockArchiveData blockArchiveData = new BlockArchiveData(blockData);
|
||||
this.repository.getBlockArchiveRepository().save(blockArchiveData);
|
||||
this.repository.saveChanges();
|
||||
}
|
||||
|
||||
private void prune(int startHeight, int endHeight) throws DataException {
|
||||
this.repository.getBlockRepository().pruneBlocks(startHeight, endHeight);
|
||||
this.repository.getATRepository().pruneAtStates(startHeight, endHeight);
|
||||
this.repository.getATRepository().setAtPruneHeight(endHeight+1);
|
||||
this.repository.saveChanges();
|
||||
}
|
||||
|
||||
private void trim(int startHeight, int endHeight) throws DataException {
|
||||
this.repository.getBlockRepository().trimOldOnlineAccountsSignatures(startHeight, endHeight);
|
||||
|
||||
int count = 1; // Any number greater than 0
|
||||
while (count > 0) {
|
||||
count = this.repository.getATRepository().trimAtStates(startHeight, endHeight, Settings.getInstance().getAtStatesTrimLimit());
|
||||
}
|
||||
|
||||
this.repository.getBlockRepository().setBlockPruneHeight(endHeight+1);
|
||||
this.repository.getATRepository().setAtTrimHeight(endHeight+1);
|
||||
this.repository.saveChanges();
|
||||
}
|
||||
|
||||
private void runRepositoryMaintenance() throws DataException {
|
||||
try {
|
||||
this.repository.performPeriodicMaintenance(1000L);
|
||||
} catch (TimeoutException e) {
|
||||
LOGGER.info("Timed out waiting for repository before running maintenance");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user