leader_scheduler: reduce the amount of special case handling for tick_height 0

This commit is contained in:
Michael Vines 2019-02-11 10:38:56 -08:00
parent 2e1dcd84f9
commit f6979a090e
3 changed files with 79 additions and 94 deletions

View File

@ -936,13 +936,10 @@ mod tests {
// transition to a validator. // transition to a validator.
info!("Unpause the Tvu"); info!("Unpause the Tvu");
pause_tvu.store(false, Ordering::Relaxed); pause_tvu.store(false, Ordering::Relaxed);
let expected_rotations = vec![ let expected_rotations = vec![(
(FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot),
(
FullnodeReturnType::LeaderToValidatorRotation, FullnodeReturnType::LeaderToValidatorRotation,
2 * ticks_per_slot, ticks_per_slot,
), )];
];
for expected_rotation in expected_rotations { for expected_rotation in expected_rotations {
loop { loop {

View File

@ -145,16 +145,16 @@ impl LeaderScheduler {
tick_height, tick_height,
epoch, epoch,
); );
if epoch < self.current_epoch {
return; if tick_height == 0 {
// Special case: tick_height starts at 0 instead of -1, so generate_schedule() for 0
// here before moving on to tick_height + 1
self.generate_schedule(0, bank);
} }
// Leader schedule is computed at tick 0 (for bootstrap) and then on the second tick 1 for // If we're about to cross an epoch boundary generate the schedule for the next epoch
// the current slot, so that generally the schedule applies to the range [slot N tick 1, if self.tick_height_to_epoch(tick_height + 1) == epoch + 1 {
// slot N+1 tick 0). The schedule is shifted right 1 tick from the slot rotation interval so that self.generate_schedule(tick_height + 1, bank);
// the next leader is always known *before* a rotation occurs
if tick_height == 0 || tick_height % self.ticks_per_epoch == 1 {
self.generate_schedule(tick_height, bank);
} }
} }
@ -239,11 +239,7 @@ impl LeaderScheduler {
// Updates the leader schedule to include ticks from tick_height to the first tick of the next epoch // Updates the leader schedule to include ticks from tick_height to the first tick of the next epoch
fn generate_schedule(&mut self, tick_height: u64, bank: &Bank) { fn generate_schedule(&mut self, tick_height: u64, bank: &Bank) {
let epoch = if tick_height == 0 { let epoch = self.tick_height_to_epoch(tick_height);
0
} else {
self.tick_height_to_epoch(tick_height) + 1
};
trace!( trace!(
"generate_schedule: tick_height={} (epoch={})", "generate_schedule: tick_height={} (epoch={})",
tick_height, tick_height,
@ -545,12 +541,10 @@ pub mod tests {
// The leader outside of the newly generated schedule window: // The leader outside of the newly generated schedule window:
// (0, ticks_per_epoch] // (0, ticks_per_epoch]
info!("yyy");
assert_eq!( assert_eq!(
leader_scheduler.get_leader_for_slot(0), leader_scheduler.get_leader_for_slot(0),
Some(genesis_block.bootstrap_leader_id) Some(genesis_block.bootstrap_leader_id)
); );
info!("xxxx");
assert_eq!( assert_eq!(
leader_scheduler leader_scheduler
.get_leader_for_slot(leader_scheduler.tick_height_to_slot(ticks_per_epoch)), .get_leader_for_slot(leader_scheduler.tick_height_to_slot(ticks_per_epoch)),
@ -559,12 +553,12 @@ pub mod tests {
// Generate schedule for second epoch. This schedule won't be used but the schedule for // Generate schedule for second epoch. This schedule won't be used but the schedule for
// the third epoch cannot be generated without an existing schedule for the second epoch // the third epoch cannot be generated without an existing schedule for the second epoch
leader_scheduler.generate_schedule(1, &bank); leader_scheduler.generate_schedule(ticks_per_epoch, &bank);
// Generate schedule for third epoch to ensure the bootstrap leader will not be added to // Generate schedule for third epoch to ensure the bootstrap leader will not be added to
// the schedule, as the bootstrap leader did not vote in the second epoch but all other // the schedule, as the bootstrap leader did not vote in the second epoch but all other
// validators did // validators did
leader_scheduler.generate_schedule(ticks_per_epoch + 1, &bank); leader_scheduler.generate_schedule(2 * ticks_per_epoch, &bank);
// For the next ticks_per_epoch entries, call get_leader_for_slot every // For the next ticks_per_epoch entries, call get_leader_for_slot every
// ticks_per_slot entries, and the next leader should be the next validator // ticks_per_slot entries, and the next leader should be the next validator
@ -993,11 +987,11 @@ pub mod tests {
} }
assert_eq!(leader_scheduler.current_epoch, 0); assert_eq!(leader_scheduler.current_epoch, 0);
leader_scheduler.generate_schedule(1, &bank); leader_scheduler.generate_schedule(1, &bank);
assert_eq!(leader_scheduler.current_epoch, 1); assert_eq!(leader_scheduler.current_epoch, 0);
for i in 0..=num_validators { for i in 0..=num_validators {
info!("i === {}", i); info!("i === {}", i);
leader_scheduler.generate_schedule((i + 1) * active_window_tick_length, &bank); leader_scheduler.generate_schedule((i + 1) * active_window_tick_length, &bank);
assert_eq!(leader_scheduler.current_epoch, i + 2); assert_eq!(leader_scheduler.current_epoch, i + 1);
if i == 0 { if i == 0 {
assert_eq!( assert_eq!(
vec![genesis_block.bootstrap_leader_id], vec![genesis_block.bootstrap_leader_id],
@ -1096,9 +1090,17 @@ pub mod tests {
assert_eq!(bank.tick_height(), 0); assert_eq!(bank.tick_height(), 0);
// //
// tick_height == 0 is a special case // Check various tick heights in epoch 0 up to the last tick
// //
leader_scheduler.update_tick_height(0, &bank); for tick_height in &[
0,
1,
ticks_per_slot,
ticks_per_slot + 1,
ticks_per_epoch - 2,
] {
info!("Checking tick_height {}", *tick_height);
leader_scheduler.update_tick_height(*tick_height, &bank);
assert_eq!(leader_scheduler.current_epoch, 0); assert_eq!(leader_scheduler.current_epoch, 0);
// The schedule for epoch 0 is known // The schedule for epoch 0 is known
assert_eq!( assert_eq!(
@ -1110,17 +1112,20 @@ pub mod tests {
Some(genesis_block.bootstrap_leader_id) Some(genesis_block.bootstrap_leader_id)
); );
// The schedule for epoch 1 is unknown // The schedule for epoch 1 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(2), None,); assert_eq!(leader_scheduler.get_leader_for_slot(2), None);
}
// //
// Check various tick heights in epoch 0, and tick 0 of epoch 1 // Check the last tick of epoch 0, various tick heights in epoch 1 up to the last tick
// //
for tick_height in &[ for tick_height in &[
1,
ticks_per_slot,
ticks_per_slot + 1,
ticks_per_epoch - 1, ticks_per_epoch - 1,
ticks_per_epoch, ticks_per_epoch,
ticks_per_epoch + 1,
ticks_per_epoch + ticks_per_slot,
ticks_per_epoch + ticks_per_slot + 1,
ticks_per_epoch + ticks_per_epoch - 2,
// ticks_per_epoch + ticks_per_epoch,
] { ] {
info!("Checking tick_height {}", *tick_height); info!("Checking tick_height {}", *tick_height);
leader_scheduler.update_tick_height(*tick_height, &bank); leader_scheduler.update_tick_height(*tick_height, &bank);
@ -1134,6 +1139,7 @@ pub mod tests {
leader_scheduler.get_leader_for_slot(1), leader_scheduler.get_leader_for_slot(1),
Some(genesis_block.bootstrap_leader_id) Some(genesis_block.bootstrap_leader_id)
); );
// The schedule for epoch 1 is known // The schedule for epoch 1 is known
assert_eq!( assert_eq!(
leader_scheduler.get_leader_for_slot(2), leader_scheduler.get_leader_for_slot(2),
@ -1144,21 +1150,10 @@ pub mod tests {
Some(genesis_block.bootstrap_leader_id) Some(genesis_block.bootstrap_leader_id)
); );
// The schedule for epoch 2 is unknown // The schedule for epoch 2 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(4), None); assert_eq!(leader_scheduler.get_leader_for_slot(6), None);
} }
// leader_scheduler.update_tick_height(ticks_per_epoch + ticks_per_epoch - 1, &bank);
// Check various tick heights in epoch 1, and tick 0 of epoch 2
//
for tick_height in &[
ticks_per_epoch + 1,
ticks_per_epoch + ticks_per_slot,
ticks_per_epoch + ticks_per_slot + 1,
ticks_per_epoch + ticks_per_epoch - 1,
ticks_per_epoch + ticks_per_epoch,
] {
info!("Checking tick_height {}", *tick_height);
leader_scheduler.update_tick_height(*tick_height, &bank);
assert_eq!(leader_scheduler.current_epoch, 2); assert_eq!(leader_scheduler.current_epoch, 2);
// The schedule for epoch 0 is unknown // The schedule for epoch 0 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(0), None); assert_eq!(leader_scheduler.get_leader_for_slot(0), None);
@ -1184,7 +1179,6 @@ pub mod tests {
// The schedule for epoch 3 is unknown // The schedule for epoch 3 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(6), None); assert_eq!(leader_scheduler.get_leader_for_slot(6), None);
} }
}
#[test] #[test]
fn test_constructors() { fn test_constructors() {
@ -1299,7 +1293,7 @@ pub mod tests {
// Make sure the validator, not the leader is selected on the first slot of the // Make sure the validator, not the leader is selected on the first slot of the
// next epoch // next epoch
leader_scheduler.generate_schedule(1, &bank); leader_scheduler.generate_schedule(1, &bank);
assert_eq!(leader_scheduler.current_epoch, 1); assert_eq!(leader_scheduler.current_epoch, 0);
if add_validator { if add_validator {
assert_eq!(leader_scheduler.epoch_schedule[0][0], validator_id); assert_eq!(leader_scheduler.epoch_schedule[0][0], validator_id);
} else { } else {

View File

@ -911,16 +911,10 @@ fn test_leader_to_validator_transition() {
let (rotation_sender, rotation_receiver) = channel(); let (rotation_sender, rotation_receiver) = channel();
let leader_exit = leader.run(Some(rotation_sender)); let leader_exit = leader.run(Some(rotation_sender));
// There will be two rotations: let expected_rotations = vec![(
// slot 0 -> slot 1: bootstrap leader remains the leader
// slot 1 -> slot 2: bootstrap leader to the validator
let expected_rotations = vec![
(FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot),
(
FullnodeReturnType::LeaderToValidatorRotation, FullnodeReturnType::LeaderToValidatorRotation,
2 * ticks_per_slot, ticks_per_slot,
), )];
];
for expected_rotation in expected_rotations { for expected_rotation in expected_rotations {
loop { loop {
@ -944,7 +938,7 @@ fn test_leader_to_validator_transition() {
assert_eq!( assert_eq!(
bank.tick_height(), bank.tick_height(),
2 * fullnode_config.leader_scheduler_config.ticks_per_slot - 1 fullnode_config.leader_scheduler_config.ticks_per_slot - 1
); );
remove_dir_all(leader_ledger_path).unwrap(); remove_dir_all(leader_ledger_path).unwrap();
} }