mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-20 02:12:00 +01:00
Add transient tx map to DaoState to speed up getTx queries
Build a HashMap of all BSQ transactions found, when loading the DaoState from disc, and store it in a transient field which is always kept in sync with the associated list of blocks. (The latter is only modified in a couple of places in DaoStateService, making this straightforward.) This is to speed up daoStateService.getTx(id), which is called from many places and appears to be a significant bottleneck. In particular, the initial load of the results in VoteResultView.doFillCycleList was very slow (taking nearly a minute on a Core i3 machine) and likely to suffer a quadratic slowdown (#cycles * #tx's) over time.
This commit is contained in:
parent
30f96643b2
commit
0fa21b5f1a
@ -22,7 +22,6 @@ import bisq.core.dao.node.parser.exceptions.BlockHashNotConnectingException;
|
||||
import bisq.core.dao.node.parser.exceptions.BlockHeightNotConnectingException;
|
||||
import bisq.core.dao.state.DaoStateService;
|
||||
import bisq.core.dao.state.model.blockchain.Block;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
|
||||
import bisq.common.app.DevEnv;
|
||||
|
||||
@ -31,7 +30,6 @@ import org.bitcoinj.core.Coin;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -106,14 +104,13 @@ public class BlockParser {
|
||||
// one get resolved.
|
||||
// Lately there is a patter with 24 iterations observed
|
||||
long startTs = System.currentTimeMillis();
|
||||
List<Tx> txList = block.getTxs();
|
||||
|
||||
rawBlock.getRawTxs().forEach(rawTx ->
|
||||
txParser.findTx(rawTx,
|
||||
genesisTxId,
|
||||
genesisBlockHeight,
|
||||
genesisTotalSupply)
|
||||
.ifPresent(txList::add));
|
||||
.ifPresent(tx -> daoStateService.onNewTxForLastBlock(block, tx)));
|
||||
|
||||
log.info("Parsing {} transactions at block height {} took {} ms", rawBlock.getRawTxs().size(),
|
||||
blockHeight, System.currentTimeMillis() - startTs);
|
||||
|
@ -35,18 +35,21 @@ import bisq.core.dao.state.model.governance.EvaluatedProposal;
|
||||
import bisq.core.dao.state.model.governance.Issuance;
|
||||
import bisq.core.dao.state.model.governance.IssuanceType;
|
||||
import bisq.core.dao.state.model.governance.ParamChange;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
import bisq.core.util.ParsingUtils;
|
||||
import bisq.core.util.coin.BsqFormatter;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
@ -115,6 +118,9 @@ public class DaoStateService implements DaoSetupService {
|
||||
|
||||
daoState.setChainHeight(snapshot.getChainHeight());
|
||||
|
||||
daoState.getTxMap().clear();
|
||||
daoState.getTxMap().putAll(snapshot.getTxMap());
|
||||
|
||||
daoState.getBlocks().clear();
|
||||
daoState.getBlocks().addAll(snapshot.getBlocks());
|
||||
|
||||
@ -226,7 +232,16 @@ public class DaoStateService implements DaoSetupService {
|
||||
}
|
||||
}
|
||||
|
||||
// Third we get the onParseBlockComplete called after all rawTxs of blocks have been parsed
|
||||
// Third we add each successfully parsed BSQ tx to the last block
|
||||
public void onNewTxForLastBlock(Block block, Tx tx) {
|
||||
// At least one block must be present else no rawTx would have been recognised as a BSQ tx.
|
||||
Preconditions.checkArgument(block == getLastBlock().orElseThrow());
|
||||
|
||||
block.getTxs().add(tx);
|
||||
daoState.getTxMap().put(tx.getId(), tx);
|
||||
}
|
||||
|
||||
// Fourth we get the onParseBlockComplete called after all rawTxs of blocks have been parsed
|
||||
public void onParseBlockComplete(Block block) {
|
||||
if (parseBlockChainComplete)
|
||||
log.info("Parse block completed: Block height {}, {} BSQ transactions.", block.getHeight(), block.getTxs().size());
|
||||
@ -348,16 +363,16 @@ public class DaoStateService implements DaoSetupService {
|
||||
.flatMap(block -> block.getTxs().stream());
|
||||
}
|
||||
|
||||
public TreeMap<String, Tx> getTxMap() {
|
||||
return new TreeMap<>(getTxStream().collect(Collectors.toMap(Tx::getId, tx -> tx)));
|
||||
public Map<String, Tx> getTxMap() {
|
||||
return daoState.getTxMap();
|
||||
}
|
||||
|
||||
public Set<Tx> getTxs() {
|
||||
return getTxStream().collect(Collectors.toSet());
|
||||
return new HashSet<>(getTxMap().values());
|
||||
}
|
||||
|
||||
public Optional<Tx> getTx(String txId) {
|
||||
return getTxStream().filter(tx -> tx.getId().equals(txId)).findAny();
|
||||
return Optional.ofNullable(getTxMap().get(txId));
|
||||
}
|
||||
|
||||
public List<Tx> getInvalidTxs() {
|
||||
|
@ -19,6 +19,7 @@ package bisq.core.dao.state.model;
|
||||
|
||||
import bisq.core.dao.state.model.blockchain.Block;
|
||||
import bisq.core.dao.state.model.blockchain.SpentInfo;
|
||||
import bisq.core.dao.state.model.blockchain.Tx;
|
||||
import bisq.core.dao.state.model.blockchain.TxOutput;
|
||||
import bisq.core.dao.state.model.blockchain.TxOutputKey;
|
||||
import bisq.core.dao.state.model.governance.Cycle;
|
||||
@ -28,16 +29,19 @@ import bisq.core.dao.state.model.governance.Issuance;
|
||||
import bisq.core.dao.state.model.governance.ParamChange;
|
||||
|
||||
import bisq.common.proto.persistable.PersistablePayload;
|
||||
import bisq.common.util.JsonExclude;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.Getter;
|
||||
@ -98,6 +102,11 @@ public class DaoState implements PersistablePayload {
|
||||
@Getter
|
||||
private final List<DecryptedBallotsWithMerits> decryptedBallotsWithMeritsList;
|
||||
|
||||
// Transient data used only as an index - must be kept in sync with the block list
|
||||
@Getter
|
||||
@JsonExclude
|
||||
private transient final Map<String, Tx> txMap; // key is txId
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
@ -145,6 +154,10 @@ public class DaoState implements PersistablePayload {
|
||||
this.paramChangeList = paramChangeList;
|
||||
this.evaluatedProposalList = evaluatedProposalList;
|
||||
this.decryptedBallotsWithMeritsList = decryptedBallotsWithMeritsList;
|
||||
|
||||
txMap = blocks.stream()
|
||||
.flatMap(block -> block.getTxs().stream())
|
||||
.collect(Collectors.toMap(Tx::getId, Function.identity(), (x, y) -> y, HashMap::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -237,6 +250,7 @@ public class DaoState implements PersistablePayload {
|
||||
",\n paramChangeList=" + paramChangeList +
|
||||
",\n evaluatedProposalList=" + evaluatedProposalList +
|
||||
",\n decryptedBallotsWithMeritsList=" + decryptedBallotsWithMeritsList +
|
||||
",\n txMap=" + txMap +
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user