From 897c44ffe87843c839deb1bb71be7544717cdb35 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 12 Aug 2023 11:14:21 +0100 Subject: [PATCH] Optimized transaction importing by using a temporary cache of unconfirmed transactions. --- .../org/qortal/controller/TransactionImporter.java | 12 ++++++++++++ .../java/org/qortal/transaction/Transaction.java | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/qortal/controller/TransactionImporter.java b/src/main/java/org/qortal/controller/TransactionImporter.java index 5c70f369..4f3b6f2f 100644 --- a/src/main/java/org/qortal/controller/TransactionImporter.java +++ b/src/main/java/org/qortal/controller/TransactionImporter.java @@ -47,6 +47,9 @@ public class TransactionImporter extends Thread { /** Map of recent invalid unconfirmed transactions. Key is base58 transaction signature, value is do-not-request expiry timestamp. */ private final Map invalidUnconfirmedTransactions = Collections.synchronizedMap(new HashMap<>()); + /** Cached list of unconfirmed transactions, used when counting per creator. This is replaced regularly */ + public static List unconfirmedTransactionsCache = null; + public static synchronized TransactionImporter getInstance() { if (instance == null) { @@ -254,6 +257,12 @@ public class TransactionImporter extends Thread { int processedCount = 0; try (final Repository repository = RepositoryManager.getRepository()) { + // Use a single copy of the unconfirmed transactions list for each cycle, to speed up constant lookups + // when counting unconfirmed transactions by creator. + List unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); + unconfirmedTransactions.removeIf(t -> t.getType() == Transaction.TransactionType.CHAT); + unconfirmedTransactionsCache = unconfirmedTransactions; + // Import transactions with valid signatures try { for (int i = 0; i < sigValidTransactions.size(); ++i) { @@ -317,6 +326,9 @@ public class TransactionImporter extends Thread { } finally { LOGGER.debug("Finished importing {} incoming transaction{}", processedCount, (processedCount == 1 ? "" : "s")); blockchainLock.unlock(); + + // Clear the unconfirmed transaction cache so new data can be populated in the next cycle + unconfirmedTransactionsCache = null; } } catch (DataException e) { LOGGER.error("Repository issue while importing incoming transactions", e); diff --git a/src/main/java/org/qortal/transaction/Transaction.java b/src/main/java/org/qortal/transaction/Transaction.java index bd91f25a..a9eb6ae7 100644 --- a/src/main/java/org/qortal/transaction/Transaction.java +++ b/src/main/java/org/qortal/transaction/Transaction.java @@ -13,6 +13,7 @@ import org.qortal.account.PublicKeyAccount; import org.qortal.asset.Asset; import org.qortal.block.BlockChain; import org.qortal.controller.Controller; +import org.qortal.controller.TransactionImporter; import org.qortal.crypto.Crypto; import org.qortal.data.block.BlockData; import org.qortal.data.group.GroupApprovalData; @@ -617,7 +618,10 @@ public abstract class Transaction { } private int countUnconfirmedByCreator(PublicKeyAccount creator) throws DataException { - List unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); + List unconfirmedTransactions = TransactionImporter.getInstance().unconfirmedTransactionsCache; + if (unconfirmedTransactions == null) { + unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(); + } // We exclude CHAT transactions as they never get included into blocks and // have spam/DoS prevention by requiring proof of work