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.
info!("Unpause the Tvu");
pause_tvu.store(false, Ordering::Relaxed);
let expected_rotations = vec![
(FullnodeReturnType::LeaderToLeaderRotation, ticks_per_slot),
(
FullnodeReturnType::LeaderToValidatorRotation,
2 * ticks_per_slot,
),
];
let expected_rotations = vec![(
FullnodeReturnType::LeaderToValidatorRotation,
ticks_per_slot,
)];
for expected_rotation in expected_rotations {
loop {

View File

@ -145,16 +145,16 @@ impl LeaderScheduler {
tick_height,
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
// the current slot, so that generally the schedule applies to the range [slot N tick 1,
// slot N+1 tick 0). The schedule is shifted right 1 tick from the slot rotation interval so that
// 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);
// If we're about to cross an epoch boundary generate the schedule for the next epoch
if self.tick_height_to_epoch(tick_height + 1) == epoch + 1 {
self.generate_schedule(tick_height + 1, 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
fn generate_schedule(&mut self, tick_height: u64, bank: &Bank) {
let epoch = if tick_height == 0 {
0
} else {
self.tick_height_to_epoch(tick_height) + 1
};
let epoch = self.tick_height_to_epoch(tick_height);
trace!(
"generate_schedule: tick_height={} (epoch={})",
tick_height,
@ -545,12 +541,10 @@ pub mod tests {
// The leader outside of the newly generated schedule window:
// (0, ticks_per_epoch]
info!("yyy");
assert_eq!(
leader_scheduler.get_leader_for_slot(0),
Some(genesis_block.bootstrap_leader_id)
);
info!("xxxx");
assert_eq!(
leader_scheduler
.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
// 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
// the schedule, as the bootstrap leader did not vote in the second epoch but all other
// 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
// 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);
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 {
info!("i === {}", i);
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 {
assert_eq!(
vec![genesis_block.bootstrap_leader_id],
@ -1096,31 +1090,42 @@ pub mod tests {
assert_eq!(bank.tick_height(), 0);
//
// tick_height == 0 is a special case
//
leader_scheduler.update_tick_height(0, &bank);
assert_eq!(leader_scheduler.current_epoch, 0);
// The schedule for epoch 0 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(0),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(1),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 1 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(2), None,);
//
// Check various tick heights in epoch 0, and tick 0 of epoch 1
// Check various tick heights in epoch 0 up to the last tick
//
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);
// The schedule for epoch 0 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(0),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(1),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 1 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(2), None);
}
//
// Check the last tick of epoch 0, various tick heights in epoch 1 up to the last tick
//
for tick_height in &[
ticks_per_epoch - 1,
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);
leader_scheduler.update_tick_height(*tick_height, &bank);
@ -1134,6 +1139,7 @@ pub mod tests {
leader_scheduler.get_leader_for_slot(1),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 1 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(2),
@ -1144,46 +1150,34 @@ pub mod tests {
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 2 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(4), None);
}
//
// 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);
// 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(1), None);
// The schedule for epoch 1 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(2),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(3),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 2 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(4),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(5),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 3 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(6), None);
}
leader_scheduler.update_tick_height(ticks_per_epoch + ticks_per_epoch - 1, &bank);
assert_eq!(leader_scheduler.current_epoch, 2);
// 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(1), None);
// The schedule for epoch 1 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(2),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(3),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 2 is known
assert_eq!(
leader_scheduler.get_leader_for_slot(4),
Some(genesis_block.bootstrap_leader_id)
);
assert_eq!(
leader_scheduler.get_leader_for_slot(5),
Some(genesis_block.bootstrap_leader_id)
);
// The schedule for epoch 3 is unknown
assert_eq!(leader_scheduler.get_leader_for_slot(6), None);
}
#[test]
@ -1299,7 +1293,7 @@ pub mod tests {
// Make sure the validator, not the leader is selected on the first slot of the
// next epoch
leader_scheduler.generate_schedule(1, &bank);
assert_eq!(leader_scheduler.current_epoch, 1);
assert_eq!(leader_scheduler.current_epoch, 0);
if add_validator {
assert_eq!(leader_scheduler.epoch_schedule[0][0], validator_id);
} else {

View File

@ -911,16 +911,10 @@ fn test_leader_to_validator_transition() {
let (rotation_sender, rotation_receiver) = channel();
let leader_exit = leader.run(Some(rotation_sender));
// There will be two rotations:
// 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,
2 * ticks_per_slot,
),
];
let expected_rotations = vec![(
FullnodeReturnType::LeaderToValidatorRotation,
ticks_per_slot,
)];
for expected_rotation in expected_rotations {
loop {
@ -944,7 +938,7 @@ fn test_leader_to_validator_transition() {
assert_eq!(
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();
}