From 8cedf618f45a5d1ab24633e9b32972e663c3dcdd Mon Sep 17 00:00:00 2001 From: CalDescent Date: Fri, 7 Oct 2022 14:46:09 +0100 Subject: [PATCH] Skip GET_BLOCK_SUMMARIES requests if it can already be fulfilled entirely from the peer's chain tip block summaries cache. Loading from the cache should speed up sync decisions, particularly when choose which peer to sync from. The greater the number of connected peers, the more significant this optimization will be. It should also reduce wasted network requests and data usage. Adding this check prior to making a network request is a simple way to introduce the new cached summaries from BLOCK_SUMMARIES_V2 without having to rewrite a lot of the complex sync / peer comparison logic. Longer term we may want to rewrite that logic to read from the cache directly, but it doesn't make sense to introduce that level of risk at this point time, especially as the Synchronizer may be rewritten soon to prefer longer chains. Even so, this is still quite a high risk commit so lots of testing will be needed. --- .../org/qortal/controller/Synchronizer.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/main/java/org/qortal/controller/Synchronizer.java b/src/main/java/org/qortal/controller/Synchronizer.java index 0fe9a56b..dc70db2a 100644 --- a/src/main/java/org/qortal/controller/Synchronizer.java +++ b/src/main/java/org/qortal/controller/Synchronizer.java @@ -8,6 +8,7 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; +import java.util.stream.IntStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -1556,7 +1557,41 @@ public class Synchronizer extends Thread { return SynchronizationResult.OK; } + private List getBlockSummariesFromCache(Peer peer, byte[] parentSignature, int numberRequested) { + List peerSummaries = peer.getChainTipSummaries(); + if (peerSummaries == null) + return null; + + // Check if the requested parent block exists in peer's summaries cache + int parentIndex = IntStream.range(0, peerSummaries.size()).filter(i -> Arrays.equals(peerSummaries.get(i).getSignature(), parentSignature)).findFirst().orElse(-1); + if (parentIndex < 0) + return null; + + // Peer's summaries contains the requested parent, so return summaries after that + // Make sure we have at least one block after the parent block + int summariesAvailable = peerSummaries.size() - parentIndex - 1; + if (summariesAvailable <= 0) + return null; + + // Don't try and return more summaries than we have, or more than were requested + int summariesToReturn = Math.min(numberRequested, summariesAvailable); + int startIndex = parentIndex + 1; + int endIndex = startIndex + summariesToReturn - 1; + if (endIndex > peerSummaries.size() - 1) + return null; + + LOGGER.trace("Serving {} block summaries from cache", summariesToReturn); + return peerSummaries.subList(startIndex, endIndex); + } + private List getBlockSummaries(Peer peer, byte[] parentSignature, int numberRequested) throws InterruptedException { + // We might be able to shortcut the response if we already have the summaries in the peer's chain tip data + List cachedSummaries = this.getBlockSummariesFromCache(peer, parentSignature, numberRequested); + if (cachedSummaries != null && !cachedSummaries.isEmpty()) + return cachedSummaries; + + LOGGER.trace("Requesting {} block summaries from peer {}", numberRequested, peer); + Message getBlockSummariesMessage = new GetBlockSummariesMessage(parentSignature, numberRequested); Message message = peer.getResponse(getBlockSummariesMessage);