-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Scheduler on_initialize supports skipped blocks
#8723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
87e088f
3671207
831e8e1
06575f9
0db2a47
80c385e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| title: Scheduler `on_initialize` supports skipped blocks | ||
| doc: | ||
| - audience: Runtime Dev | ||
| description: |- | ||
| Scheduler correctly handles situations where `on_initialize` is invoked with block numbers that: | ||
| - increase but are not strictly consecutive (e.g., jump from 5 → 10), or | ||
| - are repeated (e.g., multiple blocks are built at the same Relay Chain parent block, all reporting the same `BlockNumberProvider` value). | ||
| This situation may occur when the `BlockNumberProvider` is not local - for example, on a parachain using the Relay Chain block number provider. | ||
| Implementation notes: | ||
| - The `IncompleteSince` value is always set to the next block `(now + 1)`. | ||
| - A scheduled task is considered permanently overweight only if it fails during the first agenda processing. | ||
|
|
||
| crates: | ||
| - name: pallet-scheduler | ||
| bump: patch | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1224,30 +1224,37 @@ impl<T: Config> Pallet<T> { | |
|
|
||
| let mut incomplete_since = now + One::one(); | ||
| let mut when = IncompleteSince::<T>::take().unwrap_or(now); | ||
| let mut executed = 0; | ||
| let mut is_first = true; // first task from the first agenda. | ||
|
|
||
| let max_items = T::MaxScheduledPerBlock::get(); | ||
| let mut count_down = max; | ||
| let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items); | ||
| while count_down > 0 && when <= now && weight.can_consume(service_agenda_base_weight) { | ||
| if !Self::service_agenda(weight, &mut executed, now, when, u32::MAX) { | ||
| if !Self::service_agenda(weight, is_first, now, when, u32::MAX) { | ||
| incomplete_since = incomplete_since.min(when); | ||
| } | ||
| is_first = false; | ||
| when.saturating_inc(); | ||
| count_down.saturating_dec(); | ||
| } | ||
| incomplete_since = incomplete_since.min(when); | ||
| if incomplete_since <= now { | ||
| Self::deposit_event(Event::AgendaIncomplete { when: incomplete_since }); | ||
| IncompleteSince::<T>::put(incomplete_since); | ||
| } else { | ||
| // The next scheduler iteration should typically start from `now + 1` (`next_iter_now`). | ||
| // However, if the [`Config::BlockNumberProvider`] is not a local block number provider, | ||
| // then `next_iter_now` could be `now + n` where `n > 1`. In this case, we want to start | ||
| // from `now + 1` to ensure we don't miss any agendas. | ||
| IncompleteSince::<T>::put(now + One::one()); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we basically "abuse"
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also considered adding a new storage item like |
||
| } | ||
| } | ||
|
|
||
| /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a | ||
| /// later block. | ||
| fn service_agenda( | ||
| weight: &mut WeightMeter, | ||
| executed: &mut u32, | ||
| mut is_first: bool, | ||
| now: BlockNumberFor<T>, | ||
| when: BlockNumberFor<T>, | ||
| max: u32, | ||
|
|
@@ -1283,7 +1290,7 @@ impl<T: Config> Pallet<T> { | |
| agenda[agenda_index as usize] = Some(task); | ||
| break | ||
| } | ||
| let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task); | ||
| let result = Self::service_task(weight, now, when, agenda_index, is_first, task); | ||
| agenda[agenda_index as usize] = match result { | ||
| Err((Unavailable, slot)) => { | ||
| dropped += 1; | ||
|
|
@@ -1294,7 +1301,7 @@ impl<T: Config> Pallet<T> { | |
| slot | ||
| }, | ||
| Ok(()) => { | ||
| *executed += 1; | ||
| is_first = false; | ||
| None | ||
| }, | ||
| }; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.