Skip to content

Commit 523eb4f

Browse files
fix(sync): archival node should request old blocks from archival nodes (#3369)
Currently when an archival node syncs old blocks, they will still try to request them from regular nodes and this causes requests to be wasted and also, if the archival peers are close to the end of `highest_height_peers`, it could potentially cause the node to get stuck in syncing. Fixes #3365 Test plan --------- `test_block_sync_archival`
1 parent 48cc4f6 commit 523eb4f

2 files changed

Lines changed: 84 additions & 17 deletions

File tree

chain/client/src/client_actor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,7 @@ impl ClientActor {
890890
if (head.height < block.header().height()
891891
|| &head.epoch_id == block.header().epoch_id())
892892
&& provenance == Provenance::NONE
893+
&& !self.client.sync_status.is_syncing()
893894
{
894895
self.client.rebroadcast_block(block.clone());
895896
}

chain/client/src/sync.rs

Lines changed: 83 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,23 @@ fn get_locator_heights(height: u64) -> Vec<u64> {
324324
heights
325325
}
326326

327+
#[derive(Clone, Debug)]
328+
struct BlockHashAndHeight {
329+
hash: CryptoHash,
330+
height: BlockHeight,
331+
}
332+
333+
impl BlockHashAndHeight {
334+
pub fn new(hash: CryptoHash, height: BlockHeight) -> Self {
335+
BlockHashAndHeight { hash, height }
336+
}
337+
}
338+
327339
/// Cache for block sync that stores the hashes to request in insertion order.
328340
/// It also maintains the last final block header to minimize the impact of reorgs.
329341
#[derive(Default)]
330342
struct BlockSyncCache {
331-
hashes: LinkedHashMap<CryptoHash, ()>,
343+
hashes: LinkedHashMap<CryptoHash, BlockHeight>,
332344
last_header: Option<BlockHeader>,
333345
}
334346

@@ -337,8 +349,8 @@ impl BlockSyncCache {
337349
self.hashes.len()
338350
}
339351

340-
fn insert(&mut self, block_hash: CryptoHash) {
341-
self.hashes.insert(block_hash, ());
352+
fn insert(&mut self, block_hash: CryptoHash, block_height: BlockHeight) {
353+
self.hashes.insert(block_hash, block_height);
342354
}
343355

344356
fn remove(&mut self, block_hash: &CryptoHash) {
@@ -432,13 +444,13 @@ impl BlockSync {
432444

433445
fn compute_hashes_to_request(
434446
&mut self,
435-
new_hashes: &[CryptoHash],
447+
new_blocks: &[BlockHashAndHeight],
436448
chain: &mut Chain,
437449
block_count: usize,
438-
) -> Vec<CryptoHash> {
450+
) -> Vec<BlockHashAndHeight> {
439451
let mut res = vec![];
440452
let mut hashes_to_remove = vec![];
441-
for (block_hash, _) in self.cache.hashes.iter() {
453+
for (block_hash, block_height) in self.cache.hashes.iter() {
442454
if res.len() >= block_count {
443455
break;
444456
}
@@ -451,14 +463,16 @@ impl BlockSync {
451463
if block_exists || chain.is_orphan(block_hash) || chain.is_chunk_orphan(block_hash) {
452464
continue;
453465
} else {
454-
res.push(*block_hash);
466+
res.push(BlockHashAndHeight::new(*block_hash, *block_height));
455467
}
456468
}
457469
for hash in hashes_to_remove {
458470
self.cache.remove(&hash);
459471
}
460-
for hash in new_hashes.iter().rev().take(block_count.saturating_sub(res.len())) {
461-
res.push(*hash);
472+
for block_hash_and_height in
473+
new_blocks.iter().rev().take(block_count.saturating_sub(res.len()))
474+
{
475+
res.push(block_hash_and_height.clone());
462476
}
463477
res
464478
}
@@ -468,7 +482,7 @@ impl BlockSync {
468482
&mut self,
469483
chain: &mut Chain,
470484
block_count: usize,
471-
) -> Result<Vec<CryptoHash>, near_chain::Error> {
485+
) -> Result<Vec<BlockHashAndHeight>, near_chain::Error> {
472486
let last_header = match self.cache.last_header {
473487
Some(ref h) => h.clone(),
474488
None => chain.head_header()?.clone(),
@@ -496,14 +510,14 @@ impl BlockSync {
496510
}
497511
}
498512

499-
hashes_to_request.push(*header.hash());
513+
hashes_to_request.push(BlockHashAndHeight::new(*header.hash(), header.height()));
500514
current = chain.get_previous_header(&header).map(|h| h.clone());
501515
}
502516
let res = self.compute_hashes_to_request(&hashes_to_request, chain, block_count);
503517

504518
self.cache.hashes.reserve(hashes_to_request.len());
505-
for hash in hashes_to_request.into_iter().rev() {
506-
self.cache.insert(hash);
519+
for block_hash_and_height in hashes_to_request.into_iter().rev() {
520+
self.cache.insert(block_hash_and_height.hash, block_hash_and_height.height);
507521
}
508522
self.cache.last_header =
509523
chain.get_block_header(&header_head.last_block_hash).map(|h| h.clone()).ok();
@@ -538,11 +552,22 @@ impl BlockSync {
538552
self.blocks_requested = 0;
539553
self.receive_timeout = Utc::now() + Duration::seconds(BLOCK_REQUEST_TIMEOUT);
540554

541-
let mut peers_iter = highest_height_peers.iter().cycle();
542-
for hash in hashes_to_request {
543-
if let Some(peer) = peers_iter.next() {
555+
let gc_stop_height =
556+
chain.runtime_adapter.get_gc_stop_height(&header_head.last_block_hash);
557+
let mut archival_peer_iter =
558+
highest_height_peers.iter().filter(|p| p.chain_info.archival).cycle();
559+
let mut peer_iter = highest_height_peers.iter().cycle();
560+
for block_hash_and_height in hashes_to_request {
561+
let request_from_archival =
562+
self.archive && block_hash_and_height.height < gc_stop_height;
563+
let peer = if request_from_archival {
564+
archival_peer_iter.next()
565+
} else {
566+
peer_iter.next()
567+
};
568+
if let Some(peer) = peer {
544569
self.network_adapter.do_send(NetworkRequests::BlockRequest {
545-
hash: hash.clone(),
570+
hash: block_hash_and_height.hash,
546571
peer_id: peer.peer_info.id.clone(),
547572
});
548573
self.blocks_requested += 1;
@@ -1557,4 +1582,45 @@ mod test {
15571582
assert_eq!(block_sync.cache.len(), expected_block_hashes.len());
15581583
assert_eq!(block_sync.cache.last_header, Some(fork_block.header().clone()));
15591584
}
1585+
1586+
#[test]
1587+
fn test_block_sync_archival() {
1588+
let network_adapter = Arc::new(MockNetworkAdapter::default());
1589+
let block_fetch_horizon = 10;
1590+
let mut block_sync = BlockSync::new(network_adapter.clone(), block_fetch_horizon, true);
1591+
let mut chain_genesis = ChainGenesis::test();
1592+
chain_genesis.epoch_length = 5;
1593+
let mut env = TestEnv::new(chain_genesis, 2, 1);
1594+
let mut blocks = vec![];
1595+
for i in 1..31 {
1596+
let block = env.clients[0].produce_block(i).unwrap().unwrap();
1597+
blocks.push(block.clone());
1598+
env.process_block(0, block, Provenance::PRODUCED);
1599+
}
1600+
let block_headers = blocks.iter().map(|b| b.header().clone()).collect::<Vec<_>>();
1601+
let peer_infos = create_peer_infos(2);
1602+
env.clients[1].chain.sync_block_headers(block_headers, |_| unreachable!()).unwrap();
1603+
let is_state_sync = block_sync.block_sync(&mut env.clients[1].chain, &peer_infos).unwrap();
1604+
assert!(!is_state_sync);
1605+
let requested_block_hashes = collect_hashes_from_network_adapter(network_adapter.clone());
1606+
assert_eq!(
1607+
requested_block_hashes,
1608+
blocks[4..].iter().map(|b| *b.hash()).collect::<HashSet<_>>()
1609+
);
1610+
let last_block_header = blocks.last().unwrap().header().clone();
1611+
assert_eq!(block_sync.cache.len() as u64, last_block_header.height());
1612+
assert_eq!(block_sync.cache.last_header, Some(last_block_header.clone()));
1613+
1614+
let mut peer_infos = create_peer_infos(2);
1615+
for peer in peer_infos.iter_mut() {
1616+
peer.chain_info.archival = true;
1617+
}
1618+
let is_state_sync = block_sync.block_sync(&mut env.clients[1].chain, &peer_infos).unwrap();
1619+
assert!(!is_state_sync);
1620+
let requested_block_hashes = collect_hashes_from_network_adapter(network_adapter.clone());
1621+
assert_eq!(
1622+
requested_block_hashes,
1623+
blocks.iter().map(|b| *b.hash()).collect::<HashSet<_>>()
1624+
);
1625+
}
15601626
}

0 commit comments

Comments
 (0)