forked from Qortal/qortal
Improve/fix use of latest block cache, for more cache hits, faster chain-tip response, etc.
This commit is contained in:
parent
20777363cf
commit
5549eded38
@ -1225,12 +1225,24 @@ public class Controller extends Thread {
|
||||
final byte[] parentSignature = getBlockSummariesMessage.getParentSignature();
|
||||
this.stats.getBlockSummariesStats.requests.incrementAndGet();
|
||||
|
||||
// If peer's parent signature matches our latest block signature
|
||||
// then we can short-circuit with an empty response
|
||||
BlockData chainTip = getChainTip();
|
||||
if (chainTip != null && Arrays.equals(parentSignature, chainTip.getSignature())) {
|
||||
Message blockSummariesMessage = new BlockSummariesMessage(Collections.emptyList());
|
||||
blockSummariesMessage.setId(message.getId());
|
||||
if (!peer.sendMessage(blockSummariesMessage))
|
||||
peer.disconnect("failed to send block summaries");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
List<BlockSummaryData> blockSummaries = new ArrayList<>();
|
||||
|
||||
// Attempt to serve from our cache of latest blocks
|
||||
synchronized (this.latestBlocks) {
|
||||
blockSummaries = this.latestBlocks.stream()
|
||||
.dropWhile(cachedBlockData -> Arrays.equals(cachedBlockData.getSignature(), parentSignature))
|
||||
.dropWhile(cachedBlockData -> !Arrays.equals(cachedBlockData.getReference(), parentSignature))
|
||||
.map(BlockSummaryData::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
@ -1268,12 +1280,24 @@ public class Controller extends Thread {
|
||||
final byte[] parentSignature = getSignaturesMessage.getParentSignature();
|
||||
this.stats.getBlockSignaturesV2Stats.requests.incrementAndGet();
|
||||
|
||||
// If peer's parent signature matches our latest block signature
|
||||
// then we can short-circuit with an empty response
|
||||
BlockData chainTip = getChainTip();
|
||||
if (chainTip != null && Arrays.equals(parentSignature, chainTip.getSignature())) {
|
||||
Message signaturesMessage = new SignaturesMessage(Collections.emptyList());
|
||||
signaturesMessage.setId(message.getId());
|
||||
if (!peer.sendMessage(signaturesMessage))
|
||||
peer.disconnect("failed to send signatures (v2)");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
List<byte[]> signatures = new ArrayList<>();
|
||||
|
||||
// Attempt to serve from our cache of latest blocks
|
||||
synchronized (this.latestBlocks) {
|
||||
signatures = this.latestBlocks.stream()
|
||||
.dropWhile(cachedBlockData -> Arrays.equals(cachedBlockData.getSignature(), parentSignature))
|
||||
.dropWhile(cachedBlockData -> !Arrays.equals(cachedBlockData.getReference(), parentSignature))
|
||||
.map(BlockData::getSignature)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package org.qortal.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -133,6 +137,129 @@ public class BlockTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLatestBlockCacheWithLatestBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Deque<BlockData> latestBlockCache = buildLatestBlockCache(repository, 20);
|
||||
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
byte[] parentSignature = latestBlock.getSignature();
|
||||
|
||||
List<BlockData> childBlocks = findCachedChildBlocks(latestBlockCache, parentSignature);
|
||||
|
||||
assertEquals(true, childBlocks.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLatestBlockCacheWithPenultimateBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Deque<BlockData> latestBlockCache = buildLatestBlockCache(repository, 20);
|
||||
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
BlockData penultimateBlock = repository.getBlockRepository().fromHeight(latestBlock.getHeight() - 1);
|
||||
byte[] parentSignature = penultimateBlock.getSignature();
|
||||
|
||||
List<BlockData> childBlocks = findCachedChildBlocks(latestBlockCache, parentSignature);
|
||||
|
||||
assertEquals(false, childBlocks.isEmpty());
|
||||
assertEquals(1, childBlocks.size());
|
||||
|
||||
BlockData expectedBlock = latestBlock;
|
||||
BlockData actualBlock = childBlocks.get(0);
|
||||
assertArrayEquals(expectedBlock.getSignature(), actualBlock.getSignature());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLatestBlockCacheWithMiddleBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Deque<BlockData> latestBlockCache = buildLatestBlockCache(repository, 20);
|
||||
|
||||
int tipOffset = 5;
|
||||
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
BlockData parentBlock = repository.getBlockRepository().fromHeight(latestBlock.getHeight() - tipOffset);
|
||||
byte[] parentSignature = parentBlock.getSignature();
|
||||
|
||||
List<BlockData> childBlocks = findCachedChildBlocks(latestBlockCache, parentSignature);
|
||||
|
||||
assertEquals(false, childBlocks.isEmpty());
|
||||
assertEquals(tipOffset, childBlocks.size());
|
||||
|
||||
BlockData expectedFirstBlock = repository.getBlockRepository().fromHeight(parentBlock.getHeight() + 1);
|
||||
BlockData actualFirstBlock = childBlocks.get(0);
|
||||
assertArrayEquals(expectedFirstBlock.getSignature(), actualFirstBlock.getSignature());
|
||||
|
||||
BlockData expectedLastBlock = latestBlock;
|
||||
BlockData actualLastBlock = childBlocks.get(childBlocks.size() - 1);
|
||||
assertArrayEquals(expectedLastBlock.getSignature(), actualLastBlock.getSignature());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLatestBlockCacheWithFirstBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Deque<BlockData> latestBlockCache = buildLatestBlockCache(repository, 20);
|
||||
|
||||
int tipOffset = latestBlockCache.size();
|
||||
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
BlockData parentBlock = repository.getBlockRepository().fromHeight(latestBlock.getHeight() - tipOffset);
|
||||
byte[] parentSignature = parentBlock.getSignature();
|
||||
|
||||
List<BlockData> childBlocks = findCachedChildBlocks(latestBlockCache, parentSignature);
|
||||
|
||||
assertEquals(false, childBlocks.isEmpty());
|
||||
assertEquals(tipOffset, childBlocks.size());
|
||||
|
||||
BlockData expectedFirstBlock = repository.getBlockRepository().fromHeight(parentBlock.getHeight() + 1);
|
||||
BlockData actualFirstBlock = childBlocks.get(0);
|
||||
assertArrayEquals(expectedFirstBlock.getSignature(), actualFirstBlock.getSignature());
|
||||
|
||||
BlockData expectedLastBlock = latestBlock;
|
||||
BlockData actualLastBlock = childBlocks.get(childBlocks.size() - 1);
|
||||
assertArrayEquals(expectedLastBlock.getSignature(), actualLastBlock.getSignature());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLatestBlockCacheWithNoncachedBlock() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Deque<BlockData> latestBlockCache = buildLatestBlockCache(repository, 20);
|
||||
|
||||
int tipOffset = latestBlockCache.size() + 1; // outside of cache
|
||||
|
||||
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
|
||||
BlockData parentBlock = repository.getBlockRepository().fromHeight(latestBlock.getHeight() - tipOffset);
|
||||
byte[] parentSignature = parentBlock.getSignature();
|
||||
|
||||
List<BlockData> childBlocks = findCachedChildBlocks(latestBlockCache, parentSignature);
|
||||
|
||||
assertEquals(true, childBlocks.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private Deque<BlockData> buildLatestBlockCache(Repository repository, int count) throws DataException {
|
||||
Deque<BlockData> latestBlockCache = new LinkedList<>();
|
||||
|
||||
// Mint some blocks
|
||||
for (int h = 0; h < count; ++h)
|
||||
latestBlockCache.addLast(BlockUtils.mintBlock(repository).getBlockData());
|
||||
|
||||
// Reduce cache down to latest 10 blocks
|
||||
while (latestBlockCache.size() > 10)
|
||||
latestBlockCache.removeFirst();
|
||||
|
||||
return latestBlockCache;
|
||||
}
|
||||
|
||||
private List<BlockData> findCachedChildBlocks(Deque<BlockData> latestBlockCache, byte[] parentSignature) {
|
||||
return latestBlockCache.stream()
|
||||
.dropWhile(cachedBlockData -> !Arrays.equals(cachedBlockData.getReference(), parentSignature))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommonBlockSearch() {
|
||||
// Given a list of block summaries, trim all trailing summaries after common block
|
||||
|
Loading…
Reference in New Issue
Block a user