Skip to content

Commit

Permalink
Use poh grace ticks when new reset bank is pending
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Apr 13, 2024
1 parent 09241ae commit 275fa30
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 5 deletions.
31 changes: 30 additions & 1 deletion core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ impl ReplayStage {
);
let mut current_leader = None;
let mut last_reset = Hash::default();
let mut last_reset_bank_descendants = Vec::new();
let mut partition_info = PartitionInfo::new();
let mut skipped_slots_info = SkippedSlotsInfo::default();
let mut replay_timing = ReplayLoopTiming::default();
Expand Down Expand Up @@ -1017,7 +1018,17 @@ impl ReplayStage {
let mut reset_bank_time = Measure::start("reset_bank");
// Reset onto a fork
if let Some(reset_bank) = reset_bank {
if last_reset != reset_bank.last_blockhash() {
if last_reset == reset_bank.last_blockhash() {
let reset_bank_descendants =
Self::get_active_descendants(reset_bank.slot(), &progress, &blockstore);
if reset_bank_descendants != last_reset_bank_descendants {
last_reset_bank_descendants = reset_bank_descendants;
poh_recorder
.write()
.unwrap()
.update_start_bank_active_descendants(&last_reset_bank_descendants);
}
} else {
info!(
"vote bank: {:?} reset bank: {:?}",
vote_bank
Expand Down Expand Up @@ -1077,6 +1088,7 @@ impl ReplayStage {
&leader_schedule_cache,
);
last_reset = reset_bank.last_blockhash();
last_reset_bank_descendants = vec![];
tpu_has_bank = false;

if let Some(last_voted_slot) = tower.last_voted_slot() {
Expand Down Expand Up @@ -1358,6 +1370,23 @@ impl ReplayStage {
.unwrap_or(true)
}

fn get_active_descendants(
slot: Slot,
progress: &ProgressMap,
blockstore: &Blockstore,
) -> Vec<Slot> {
let Some(slot_meta) = blockstore.meta(slot).ok().flatten() else {
return vec![];
};

slot_meta
.next_slots
.iter()
.filter(|slot| !progress.is_dead(slot))
.copied()
.collect()
}

fn initialize_progress_and_fork_choice_with_locked_bank_forks(
bank_forks: &RwLock<BankForks>,
my_pubkey: &Pubkey,
Expand Down
74 changes: 70 additions & 4 deletions poh/src/poh_recorder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,9 @@ pub struct PohRecorder {
pub poh: Arc<Mutex<Poh>>,
tick_height: u64,
clear_bank_signal: Option<Sender<bool>>,
start_bank: Arc<Bank>, // parent slot
start_tick_height: u64, // first tick_height this recorder will observe
start_bank: Arc<Bank>, // parent slot
start_bank_active_descendants: Vec<Slot>,
start_tick_height: u64, // first tick_height this recorder will observe
tick_cache: Vec<(Entry, u64)>, // cache of entry and its tick_height
working_bank: Option<WorkingBank>,
sender: Sender<WorkingBankEntry>,
Expand Down Expand Up @@ -460,6 +461,14 @@ impl PohRecorder {
}
}

// Active descendants of the last reset bank that are smaller than the
// next leader slot could soon become the new reset bank.
fn is_new_reset_bank_pending(&self, next_slot: Slot) -> bool {
self.start_bank_active_descendants
.iter()
.any(|pending_slot| *pending_slot < next_slot)
}

fn reached_leader_tick(
&self,
my_pubkey: &Pubkey,
Expand All @@ -475,8 +484,12 @@ impl PohRecorder {
|| self.start_tick_height + self.grace_ticks
== leader_first_tick_height_including_grace_ticks
|| (self.tick_height >= ideal_target_tick_height
&& (self.prev_slot_was_mine(my_pubkey, next_slot)
|| !self.is_same_fork_as_previous_leader(next_slot)))
&& (self.prev_slot_was_mine(my_pubkey, next_slot) || {
// If the start bank isn't for the previous leader and there is no
// new reset bank pending, don't apply grace ticks
!self.is_same_fork_as_previous_leader(next_slot)
&& !self.is_new_reset_bank_pending(next_slot)
}))
}

pub fn start_slot(&self) -> Slot {
Expand Down Expand Up @@ -566,11 +579,18 @@ impl PohRecorder {
self.tick_cache = vec![];
if reset_start_bank {
self.start_bank = reset_bank;
self.start_bank_active_descendants = vec![];
}
self.tick_height = (self.start_slot() + 1) * self.ticks_per_slot;
self.start_tick_height = self.tick_height + 1;
}

// update the list of active descendants of the start bank to make a better
// decision about whether to use grace ticks
pub fn update_start_bank_active_descendants(&mut self, active_descendants: &[Slot]) {
self.start_bank_active_descendants = active_descendants.to_vec();
}

// synchronize PoH with a bank
pub fn reset(&mut self, reset_bank: Arc<Bank>, next_leader_slot: Option<(Slot, Slot)>) {
self.clear_bank();
Expand Down Expand Up @@ -973,6 +993,7 @@ impl PohRecorder {
poh_timing_point_sender,
clear_bank_signal,
start_bank,
start_bank_active_descendants: vec![],
start_tick_height: tick_height + 1,
leader_first_tick_height_including_grace_ticks,
leader_last_tick_height,
Expand Down Expand Up @@ -2029,6 +2050,51 @@ mod tests {
parent_slot: 4,
}
);

// Test that grace ticks are not required if the previous leader's 4
// slots got skipped.
{
poh_recorder.reset(bank4.clone(), Some((9, 9)));

// Tick until leader slot
for _ in 0..4 * bank4.ticks_per_slot() {
poh_recorder.tick();
}

// We are due to lead
assert_eq!(
poh_recorder.reached_leader_slot(&test_validator_pubkey),
PohLeaderStatus::Reached {
poh_slot: 9,
parent_slot: 4,
}
);

// Add an active descendant which is considered to be a pending new
// reset bank
poh_recorder.update_start_bank_active_descendants(&[5]);
assert!(poh_recorder.is_new_reset_bank_pending(8));

// We should now require grace ticks
assert_eq!(
poh_recorder.reached_leader_slot(&test_validator_pubkey),
PohLeaderStatus::NotReached,
);

// Tick through grace ticks
for _ in 0..poh_recorder.grace_ticks {
poh_recorder.tick();
}

// After grace ticks, we are due to lead
assert_eq!(
poh_recorder.reached_leader_slot(&test_validator_pubkey),
PohLeaderStatus::Reached {
poh_slot: 9,
parent_slot: 4,
}
);
}
}

#[test]
Expand Down

0 comments on commit 275fa30

Please sign in to comment.