slots: slot lenience must take into account block proposal portion (#9138)

* aura, babe: proposal slot lenience must take into account proposal portion

* slots: add support for max_block_proposal_slot_portion

* fix compilation

* slots: add tests

* aura: fix comment

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* slots: log the actual proposing duration after lenience is applied

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
André Silva
2021-06-19 21:37:33 +01:00
committed by GitHub
parent 0417e4e37f
commit 650fc2f9c9
6 changed files with 193 additions and 97 deletions
+122 -2
View File
@@ -666,6 +666,96 @@ impl SlotProportion {
}
}
/// The strategy used to calculate the slot lenience used to increase the block proposal time when
/// slots have been skipped with no blocks authored.
pub enum SlotLenienceType {
/// Increase the lenience linearly with the number of skipped slots.
Linear,
/// Increase the lenience exponentially with the number of skipped slots.
Exponential,
}
impl SlotLenienceType {
fn as_str(&self) -> &'static str {
match self {
SlotLenienceType::Linear => "linear",
SlotLenienceType::Exponential => "exponential",
}
}
}
/// Calculate the remaining duration for block proposal taking into account whether any slots have
/// been skipped and applying the given lenience strategy. If `max_block_proposal_slot_portion` is
/// not none this method guarantees that the returned duration must be lower or equal to
/// `slot_info.duration * max_block_proposal_slot_portion`.
pub fn proposing_remaining_duration<Block: BlockT>(
parent_slot: Option<Slot>,
slot_info: &SlotInfo<Block>,
block_proposal_slot_portion: &SlotProportion,
max_block_proposal_slot_portion: Option<&SlotProportion>,
slot_lenience_type: SlotLenienceType,
log_target: &str,
) -> Duration {
use sp_runtime::traits::Zero;
let proposing_duration = slot_info
.duration
.mul_f32(block_proposal_slot_portion.get());
let slot_remaining = slot_info
.ends_at
.checked_duration_since(std::time::Instant::now())
.unwrap_or_default();
let proposing_duration = std::cmp::min(slot_remaining, proposing_duration);
// If parent is genesis block, we don't require any lenience factor.
if slot_info.chain_head.number().is_zero() {
return proposing_duration;
}
let parent_slot = match parent_slot {
Some(parent_slot) => parent_slot,
None => return proposing_duration,
};
let slot_lenience = match slot_lenience_type {
SlotLenienceType::Exponential => slot_lenience_exponential(parent_slot, slot_info),
SlotLenienceType::Linear => slot_lenience_linear(parent_slot, slot_info),
};
if let Some(slot_lenience) = slot_lenience {
let lenient_proposing_duration =
proposing_duration + slot_lenience.mul_f32(block_proposal_slot_portion.get());
// if we defined a maximum portion of the slot for proposal then we must make sure the
// lenience doesn't go over it
let lenient_proposing_duration =
if let Some(ref max_block_proposal_slot_portion) = max_block_proposal_slot_portion {
std::cmp::min(
lenient_proposing_duration,
slot_info
.duration
.mul_f32(max_block_proposal_slot_portion.get()),
)
} else {
lenient_proposing_duration
};
debug!(
target: log_target,
"No block for {} slots. Applying {} lenience, total proposing duration: {}",
slot_info.slot.saturating_sub(parent_slot + 1),
slot_lenience_type.as_str(),
lenient_proposing_duration.as_secs(),
);
lenient_proposing_duration
} else {
proposing_duration
}
}
/// Calculate a slot duration lenience based on the number of missed slots from current
/// to parent. If the number of skipped slots is greated than 0 this method will apply
/// an exponential backoff of at most `2^7 * slot_duration`, if no slots were skipped
@@ -703,7 +793,7 @@ pub fn slot_lenience_exponential<Block: BlockT>(
/// a linear backoff of at most `20 * slot_duration`, if no slots were skipped
/// this method will return `None.`
pub fn slot_lenience_linear<Block: BlockT>(
parent_slot: u64,
parent_slot: Slot,
slot_info: &SlotInfo<Block>,
) -> Option<Duration> {
// never give more than 20 times more lenience.
@@ -839,7 +929,7 @@ mod test {
duration: SLOT_DURATION,
timestamp: Default::default(),
inherent_data: Default::default(),
ends_at: Instant::now(),
ends_at: Instant::now() + SLOT_DURATION,
chain_head: Header::new(
1,
Default::default(),
@@ -897,6 +987,36 @@ mod test {
);
}
#[test]
fn proposing_remaining_duration_should_apply_lenience_based_on_proposal_slot_proportion() {
assert_eq!(
proposing_remaining_duration(
Some(0.into()),
&slot(2),
&SlotProportion(0.25),
None,
SlotLenienceType::Linear,
"test",
),
SLOT_DURATION.mul_f32(0.25 * 2.0),
);
}
#[test]
fn proposing_remaining_duration_should_never_exceed_max_proposal_slot_proportion() {
assert_eq!(
proposing_remaining_duration(
Some(0.into()),
&slot(100),
&SlotProportion(0.25),
Some(SlotProportion(0.9)).as_ref(),
SlotLenienceType::Exponential,
"test",
),
SLOT_DURATION.mul_f32(0.9),
);
}
#[derive(PartialEq, Debug)]
struct HeadState {
head_number: NumberFor<Block>,