mirror of
https://github.com/mimblewimble/grin.git
synced 2025-02-08 12:21:09 +03:00
Refactor: run_sync() ballooning, extract 3 utility functions (#1562)
This commit is contained in:
parent
0d7fa06fe0
commit
3adddfba76
1 changed files with 223 additions and 187 deletions
|
@ -45,18 +45,56 @@ impl Syncer {
|
||||||
archive_mode: bool,
|
archive_mode: bool,
|
||||||
stop: Arc<AtomicBool>,
|
stop: Arc<AtomicBool>,
|
||||||
) {
|
) {
|
||||||
sync::run_sync(
|
let _ = thread::Builder::new()
|
||||||
sync_state,
|
.name("sync".to_string())
|
||||||
awaiting_peers,
|
.spawn(move || {
|
||||||
peers,
|
sync::run_sync(
|
||||||
chain,
|
sync_state,
|
||||||
skip_sync_wait,
|
awaiting_peers,
|
||||||
archive_mode,
|
peers,
|
||||||
stop,
|
chain,
|
||||||
)
|
skip_sync_wait,
|
||||||
|
archive_mode,
|
||||||
|
stop,
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wait_for_min_peers(
|
||||||
|
awaiting_peers: Arc<AtomicBool>,
|
||||||
|
peers: Arc<p2p::Peers>,
|
||||||
|
chain: Arc<chain::Chain>,
|
||||||
|
skip_sync_wait: bool,
|
||||||
|
) {
|
||||||
|
// Initial sleep to give us time to peer with some nodes.
|
||||||
|
// Note: Even if we have "skip_sync_wait" we need to wait a
|
||||||
|
// short period of time for tests to do the right thing.
|
||||||
|
let wait_secs = if skip_sync_wait { 3 } else { 30 };
|
||||||
|
|
||||||
|
let head = chain.head().unwrap();
|
||||||
|
|
||||||
|
awaiting_peers.store(true, Ordering::Relaxed);
|
||||||
|
let mut n = 0;
|
||||||
|
const MIN_PEERS: usize = 3;
|
||||||
|
loop {
|
||||||
|
let wp = peers.more_work_peers();
|
||||||
|
// exit loop when:
|
||||||
|
// * we have more than MIN_PEERS more_work peers
|
||||||
|
// * we are synced already, e.g. grin was quickly restarted
|
||||||
|
// * timeout
|
||||||
|
if wp.len() > MIN_PEERS
|
||||||
|
|| (wp.len() == 0 && peers.enough_peers() && head.total_difficulty > Difficulty::zero())
|
||||||
|
|| n > wait_secs
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread::sleep(time::Duration::from_secs(1));
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
awaiting_peers.store(false, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts the syncing loop, just spawns two threads that loop forever
|
/// Starts the syncing loop, just spawns two threads that loop forever
|
||||||
pub fn run_sync(
|
pub fn run_sync(
|
||||||
sync_state: Arc<SyncState>,
|
sync_state: Arc<SyncState>,
|
||||||
|
@ -67,193 +105,194 @@ pub fn run_sync(
|
||||||
archive_mode: bool,
|
archive_mode: bool,
|
||||||
stop: Arc<AtomicBool>,
|
stop: Arc<AtomicBool>,
|
||||||
) {
|
) {
|
||||||
let chain = chain.clone();
|
let mut si = SyncInfo::new();
|
||||||
let _ =
|
|
||||||
thread::Builder::new()
|
|
||||||
.name("sync".to_string())
|
|
||||||
.spawn(move || {
|
|
||||||
let mut si = SyncInfo::new();
|
|
||||||
|
|
||||||
{
|
// Wait for connections reach at least MIN_PEERS
|
||||||
// Initial sleep to give us time to peer with some nodes.
|
wait_for_min_peers(awaiting_peers, peers.clone(), chain.clone(), skip_sync_wait);
|
||||||
// Note: Even if we have "skip_sync_wait" we need to wait a
|
|
||||||
// short period of time for tests to do the right thing.
|
|
||||||
let wait_secs = if skip_sync_wait { 3 } else { 30 };
|
|
||||||
|
|
||||||
let head = chain.head().unwrap();
|
// fast sync has 3 "states":
|
||||||
|
// * syncing headers
|
||||||
|
// * once all headers are sync'd, requesting the txhashset state
|
||||||
|
// * once we have the state, get blocks after that
|
||||||
|
//
|
||||||
|
// full sync gets rid of the middle step and just starts from
|
||||||
|
// the genesis state
|
||||||
|
|
||||||
awaiting_peers.store(true, Ordering::Relaxed);
|
let mut history_locators: Vec<(u64, Hash)> = vec![];
|
||||||
let mut n = 0;
|
let mut body_sync_info = BodySyncInfo {
|
||||||
const MIN_PEERS: usize = 3;
|
sync_start_ts: Utc::now(),
|
||||||
loop {
|
body_sync_hashes: vec![],
|
||||||
let wp = peers.more_work_peers();
|
prev_body_received: None,
|
||||||
// exit loop when:
|
prev_tip: chain.head().unwrap(),
|
||||||
// * we have more than MIN_PEERS more_work peers
|
prev_orphans_len: 0,
|
||||||
// * we are synced already, e.g. grin was quickly restarted
|
};
|
||||||
// * timeout
|
|
||||||
if wp.len() > MIN_PEERS
|
|
||||||
|| (wp.len() == 0
|
|
||||||
&& peers.enough_peers()
|
|
||||||
&& head.total_difficulty > Difficulty::zero()) || n > wait_secs
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
thread::sleep(time::Duration::from_secs(1));
|
|
||||||
n += 1;
|
|
||||||
}
|
|
||||||
awaiting_peers.store(false, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fast sync has 3 "states":
|
// Main syncing loop
|
||||||
// * syncing headers
|
while !stop.load(Ordering::Relaxed) {
|
||||||
// * once all headers are sync'd, requesting the txhashset state
|
thread::sleep(time::Duration::from_millis(10));
|
||||||
// * once we have the state, get blocks after that
|
|
||||||
//
|
|
||||||
// full sync gets rid of the middle step and just starts from
|
|
||||||
// the genesis state
|
|
||||||
|
|
||||||
let mut history_locators: Vec<(u64, Hash)> = vec![];
|
// check whether syncing is generally needed, when we compare our state with others
|
||||||
let mut body_sync_info = BodySyncInfo {
|
let (syncing, most_work_height) =
|
||||||
sync_start_ts: Utc::now(),
|
needs_syncing(sync_state.as_ref(), peers.clone(), chain.clone());
|
||||||
body_sync_hashes: vec![],
|
|
||||||
prev_body_received: None,
|
|
||||||
prev_tip: chain.head().unwrap(),
|
|
||||||
prev_orphans_len: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
if most_work_height > 0 {
|
||||||
let horizon = global::cut_through_horizon() as u64;
|
// we can occasionally get a most work height of 0 if read locks fail
|
||||||
let head = chain.head().unwrap();
|
si.highest_height = most_work_height;
|
||||||
let header_head = chain.get_header_head().unwrap();
|
}
|
||||||
|
|
||||||
// is syncing generally needed when we compare our state with others
|
// if no syncing is needed
|
||||||
let (syncing, most_work_height) =
|
if !syncing {
|
||||||
needs_syncing(sync_state.as_ref(), peers.clone(), chain.clone());
|
sync_state.update(SyncStatus::NoSync);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if most_work_height > 0 {
|
// if syncing is needed
|
||||||
// we can occasionally get a most work height of 0 if read locks fail
|
let head = chain.head().unwrap();
|
||||||
si.highest_height = most_work_height;
|
let header_head = chain.get_header_head().unwrap();
|
||||||
}
|
|
||||||
|
|
||||||
if syncing {
|
// run the header sync in every 10s at least
|
||||||
let fast_sync_enabled = !archive_mode
|
if si.header_sync_due(&header_head) {
|
||||||
&& si.highest_height.saturating_sub(head.height) > horizon;
|
do_header_sync(
|
||||||
|
sync_state.as_ref(),
|
||||||
|
header_head.clone(),
|
||||||
|
peers.clone(),
|
||||||
|
chain.clone(),
|
||||||
|
&si,
|
||||||
|
&mut history_locators,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// run the header sync every 10s
|
// if fast_sync is enabled and needed
|
||||||
if si.header_sync_due(&header_head) {
|
let need_fast_sync = !archive_mode
|
||||||
let status = sync_state.status();
|
&& si.highest_height.saturating_sub(head.height) > global::cut_through_horizon() as u64;
|
||||||
let update_sync_state = match status {
|
if need_fast_sync {
|
||||||
SyncStatus::TxHashsetDownload => false,
|
do_fast_sync(
|
||||||
SyncStatus::NoSync | SyncStatus::Initial => {
|
sync_state.as_ref(),
|
||||||
// Reset sync_head to header_head on transition to HeaderSync,
|
header_head,
|
||||||
// but ONLY on initial transition to HeaderSync state.
|
peers.clone(),
|
||||||
let sync_head = chain.get_sync_head().unwrap();
|
chain.clone(),
|
||||||
debug!(
|
&mut si,
|
||||||
LOGGER,
|
);
|
||||||
"sync: initial transition to HeaderSync. sync_head: {} at {}, reset to: {} at {}",
|
|
||||||
sync_head.hash(),
|
|
||||||
sync_head.height,
|
|
||||||
header_head.hash(),
|
|
||||||
header_head.height,
|
|
||||||
);
|
|
||||||
chain.init_sync_head(&header_head).unwrap();
|
|
||||||
history_locators.clear();
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => true,
|
|
||||||
};
|
|
||||||
if update_sync_state {
|
|
||||||
sync_state.update(SyncStatus::HeaderSync {
|
|
||||||
current_height: header_head.height,
|
|
||||||
highest_height: si.highest_height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
header_sync(peers.clone(), chain.clone(), &mut history_locators);
|
|
||||||
}
|
|
||||||
|
|
||||||
if fast_sync_enabled {
|
continue;
|
||||||
{
|
}
|
||||||
let mut sync_need_restart = false;
|
|
||||||
// check sync error
|
|
||||||
{
|
|
||||||
let clone = sync_state.sync_error();
|
|
||||||
if let Some(ref sync_error) = *clone.read().unwrap() {
|
|
||||||
error!(
|
|
||||||
LOGGER,
|
|
||||||
"fast_sync: error = {:?}. restart fast sync",
|
|
||||||
sync_error
|
|
||||||
);
|
|
||||||
sync_need_restart = true;
|
|
||||||
}
|
|
||||||
drop(clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check peer connection status of this sync
|
// if fast_sync disabled or not needed, run the body_sync every 5s
|
||||||
if let Some(ref peer) = si.fast_sync_peer {
|
if si.body_sync_due(&head, chain.clone(), &mut body_sync_info) {
|
||||||
if let Ok(p) = peer.try_read() {
|
body_sync(peers.clone(), chain.clone(), &mut body_sync_info);
|
||||||
if !p.is_connected()
|
|
||||||
&& SyncStatus::TxHashsetDownload
|
|
||||||
== sync_state.status()
|
|
||||||
{
|
|
||||||
sync_need_restart = true;
|
|
||||||
info!(
|
|
||||||
LOGGER,
|
|
||||||
"fast_sync: peer connection lost: {:?}. restart",
|
|
||||||
p.info.addr,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if sync_need_restart {
|
sync_state.update(SyncStatus::BodySync {
|
||||||
si.fast_sync_reset();
|
current_height: head.height,
|
||||||
sync_state.clear_sync_error();
|
highest_height: si.highest_height,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run fast sync if applicable, normally only run one-time, except restart in error
|
|
||||||
if header_head.height == si.highest_height {
|
|
||||||
let (go, download_timeout) = si.fast_sync_due();
|
|
||||||
|
|
||||||
if go {
|
|
||||||
si.fast_sync_peer = None;
|
|
||||||
match fast_sync(peers.clone(), chain.clone(), &header_head) {
|
|
||||||
Ok(peer) => {
|
|
||||||
si.fast_sync_peer = Some(peer);
|
|
||||||
}
|
|
||||||
Err(e) => sync_state.set_sync_error(Error::P2P(e)),
|
|
||||||
}
|
|
||||||
sync_state.update(SyncStatus::TxHashsetDownload);
|
|
||||||
}
|
|
||||||
|
|
||||||
if SyncStatus::TxHashsetDownload == sync_state.status()
|
|
||||||
&& download_timeout
|
|
||||||
{
|
|
||||||
error!(LOGGER, "fast_sync: TxHashsetDownload status timeout in 10 minutes!");
|
|
||||||
sync_state.set_sync_error(Error::P2P(p2p::Error::Timeout));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// run the body_sync every 5s
|
|
||||||
if si.body_sync_due(&head, chain.clone(), &mut body_sync_info) {
|
|
||||||
body_sync(peers.clone(), chain.clone(), &mut body_sync_info);
|
|
||||||
sync_state.update(SyncStatus::BodySync {
|
|
||||||
current_height: head.height,
|
|
||||||
highest_height: si.highest_height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sync_state.update(SyncStatus::NoSync);
|
|
||||||
}
|
|
||||||
|
|
||||||
thread::sleep(time::Duration::from_millis(10));
|
|
||||||
|
|
||||||
if stop.load(Ordering::Relaxed) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_header_sync(
|
||||||
|
sync_state: &SyncState,
|
||||||
|
header_head: chain::Tip,
|
||||||
|
peers: Arc<p2p::Peers>,
|
||||||
|
chain: Arc<chain::Chain>,
|
||||||
|
si: &SyncInfo,
|
||||||
|
history_locators: &mut Vec<(u64, Hash)>,
|
||||||
|
) {
|
||||||
|
let status = sync_state.status();
|
||||||
|
|
||||||
|
let update_sync_state = match status {
|
||||||
|
SyncStatus::TxHashsetDownload => false,
|
||||||
|
SyncStatus::NoSync | SyncStatus::Initial => {
|
||||||
|
// Reset sync_head to header_head on transition to HeaderSync,
|
||||||
|
// but ONLY on initial transition to HeaderSync state.
|
||||||
|
let sync_head = chain.get_sync_head().unwrap();
|
||||||
|
debug!(
|
||||||
|
LOGGER,
|
||||||
|
"sync: initial transition to HeaderSync. sync_head: {} at {}, reset to: {} at {}",
|
||||||
|
sync_head.hash(),
|
||||||
|
sync_head.height,
|
||||||
|
header_head.hash(),
|
||||||
|
header_head.height,
|
||||||
|
);
|
||||||
|
chain.init_sync_head(&header_head).unwrap();
|
||||||
|
history_locators.clear();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if update_sync_state {
|
||||||
|
sync_state.update(SyncStatus::HeaderSync {
|
||||||
|
current_height: header_head.height,
|
||||||
|
highest_height: si.highest_height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
header_sync(peers, chain, history_locators);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_fast_sync(
|
||||||
|
sync_state: &SyncState,
|
||||||
|
header_head: chain::Tip,
|
||||||
|
peers: Arc<p2p::Peers>,
|
||||||
|
chain: Arc<chain::Chain>,
|
||||||
|
si: &mut SyncInfo,
|
||||||
|
) {
|
||||||
|
let mut sync_need_restart = false;
|
||||||
|
|
||||||
|
// check sync error
|
||||||
|
{
|
||||||
|
let clone = sync_state.sync_error();
|
||||||
|
if let Some(ref sync_error) = *clone.read().unwrap() {
|
||||||
|
error!(
|
||||||
|
LOGGER,
|
||||||
|
"fast_sync: error = {:?}. restart fast sync", sync_error
|
||||||
|
);
|
||||||
|
sync_need_restart = true;
|
||||||
|
}
|
||||||
|
drop(clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check peer connection status of this sync
|
||||||
|
if let Some(ref peer) = si.fast_sync_peer {
|
||||||
|
if let Ok(p) = peer.try_read() {
|
||||||
|
if !p.is_connected() && SyncStatus::TxHashsetDownload == sync_state.status() {
|
||||||
|
sync_need_restart = true;
|
||||||
|
info!(
|
||||||
|
LOGGER,
|
||||||
|
"fast_sync: peer connection lost: {:?}. restart", p.info.addr,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sync_need_restart {
|
||||||
|
si.fast_sync_reset();
|
||||||
|
sync_state.clear_sync_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
// run fast sync if applicable, normally only run one-time, except restart in error
|
||||||
|
if header_head.height == si.highest_height {
|
||||||
|
let (go, download_timeout) = si.fast_sync_due();
|
||||||
|
|
||||||
|
if go {
|
||||||
|
si.fast_sync_peer = None;
|
||||||
|
match fast_sync(peers, chain, &header_head) {
|
||||||
|
Ok(peer) => {
|
||||||
|
si.fast_sync_peer = Some(peer);
|
||||||
|
}
|
||||||
|
Err(e) => sync_state.set_sync_error(Error::P2P(e)),
|
||||||
|
}
|
||||||
|
sync_state.update(SyncStatus::TxHashsetDownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
if download_timeout && SyncStatus::TxHashsetDownload == sync_state.status() {
|
||||||
|
error!(
|
||||||
|
LOGGER,
|
||||||
|
"fast_sync: TxHashsetDownload status timeout in 10 minutes!"
|
||||||
|
);
|
||||||
|
sync_state.set_sync_error(Error::P2P(p2p::Error::Timeout));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BodySyncInfo {
|
struct BodySyncInfo {
|
||||||
|
@ -376,8 +415,7 @@ fn body_sync(peers: Arc<Peers>, chain: Arc<chain::Chain>, body_sync_info: &mut B
|
||||||
// only ask for blocks that we have not yet processed
|
// only ask for blocks that we have not yet processed
|
||||||
// either successfully stored or in our orphan list
|
// either successfully stored or in our orphan list
|
||||||
!chain.get_block(x).is_ok() && !chain.is_orphan(x)
|
!chain.get_block(x).is_ok() && !chain.is_orphan(x)
|
||||||
})
|
}).take(block_count)
|
||||||
.take(block_count)
|
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if hashes_to_get.len() > 0 {
|
if hashes_to_get.len() > 0 {
|
||||||
|
@ -788,9 +826,7 @@ mod test {
|
||||||
// headers
|
// headers
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_locator_heights(10000),
|
get_locator_heights(10000),
|
||||||
vec![
|
vec![10000, 9998, 9994, 9986, 9970, 9938, 9874, 9746, 9490, 8978, 7954, 5906, 1810, 0,]
|
||||||
10000, 9998, 9994, 9986, 9970, 9938, 9874, 9746, 9490, 8978, 7954, 5906, 1810, 0,
|
|
||||||
]
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue