difftreelog
fix periodic tasks always find place
in: master
2 files changed
pallets/scheduler-v2/src/lib.rsdiffbeforeafterboth96 BoundedVec, RuntimeDebug, DispatchErrorWithPostInfo,96 BoundedVec, RuntimeDebug, DispatchErrorWithPostInfo,97};97};98use sp_core::H160;98use sp_core::H160;99use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};99use sp_std::{cmp::Ordering, marker::PhantomData, prelude::*};100pub use weights::WeightInfo;100pub use weights::WeightInfo;101101102pub use pallet::*;102pub use pallet::*;254}254}255255256impl<T: Config> BlockAgenda<T> {256impl<T: Config> BlockAgenda<T> {257 fn try_push(&mut self, scheduled: ScheduledOf<T>) -> Option<u32> {257 fn try_push(&mut self, scheduled: ScheduledOf<T>) -> Result<u32, ScheduledOf<T>> {258 if self.free_places == 0 {258 if self.free_places == 0 {259 return None;259 return Err(scheduled);260 }260 }261261262 self.free_places = self.free_places.saturating_sub(1);262 self.free_places = self.free_places.saturating_sub(1);263263264 if (self.agenda.len() as u32) < T::MaxScheduledPerBlock::get() {264 if (self.agenda.len() as u32) < T::MaxScheduledPerBlock::get() {265 // will always succeed due to the above check.265 // will always succeed due to the above check.266 let _ = self.agenda.try_push(Some(scheduled));266 let _ = self.agenda.try_push(Some(scheduled));267 Some((self.agenda.len() - 1) as u32)267 Ok((self.agenda.len() - 1) as u32)268 } else {268 } else {269 match self.agenda.iter().position(|i| i.is_none()) {269 match self.agenda.iter().position(|i| i.is_none()) {270 Some(hole_index) => {270 Some(hole_index) => {271 self.agenda[hole_index] = Some(scheduled);271 self.agenda[hole_index] = Some(scheduled);272 Some(hole_index as u32)272 Ok(hole_index as u32)273 }273 }274 None => unreachable!("free_places > 0; qed"),274 None => unreachable!("free_places was greater than 0; qed"),275 }275 }276 }276 }277 }277 }476 task: TaskAddress<T::BlockNumber>,476 task: TaskAddress<T::BlockNumber>,477 id: Option<[u8; 32]>,477 id: Option<[u8; 32]>,478 },478 },479 /// The given task was unable to be renewed since the agenda is full at that block.480 PeriodicFailed {481 task: TaskAddress<T::BlockNumber>,482 id: Option<[u8; 32]>,483 },484 /// The given task can never be executed since it is overweight.479 /// The given task can never be executed since it is overweight.485 PermanentlyOverweight {480 PermanentlyOverweight {486 task: TaskAddress<T::BlockNumber>,481 task: TaskAddress<T::BlockNumber>,688 Ok(when)683 Ok(when)689 }684 }685686 fn mandatory_place_task(when: T::BlockNumber, what: ScheduledOf<T>) {687 Self::place_task(when, what, true).expect("mandatory place task always succeeds; qed");688 }689690 fn try_place_task(691 when: T::BlockNumber,692 what: ScheduledOf<T>,693 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {694 Self::place_task(when, what, false)695 }690696691 fn place_task(697 fn place_task(692 when: T::BlockNumber,698 mut when: T::BlockNumber,693 what: ScheduledOf<T>,699 what: ScheduledOf<T>,700 is_mandatory: bool,694 ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> {701 ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> {695 let maybe_name = what.maybe_id;702 let maybe_name = what.maybe_id;696 let index = Self::push_to_agenda(when, what)?;703 let index = Self::push_to_agenda(&mut when, what, is_mandatory)?;697 let address = (when, index);704 let address = (when, index);698 if let Some(name) = maybe_name {705 if let Some(name) = maybe_name {699 Lookup::<T>::insert(name, address)706 Lookup::<T>::insert(name, address)706 }713 }707714708 fn push_to_agenda(715 fn push_to_agenda(709 when: T::BlockNumber,716 when: &mut T::BlockNumber,710 what: ScheduledOf<T>,717 mut what: ScheduledOf<T>,718 is_mandatory: bool,711 ) -> Result<u32, (DispatchError, ScheduledOf<T>)> {719 ) -> Result<u32, DispatchError> {712 let mut agenda = Agenda::<T>::get(when);720 let mut agenda;721722 let index = loop {723 agenda = Agenda::<T>::get(*when);724713 let index = agenda725 match agenda.try_push(what) {714 .try_push(what.clone())726 Ok(index) => break index,727 Err(returned_what) if is_mandatory => {728 what = returned_what;715 .ok_or((<Error<T>>::AgendaIsExhausted.into(), what))?;729 when.saturating_inc();730 }731 Err(_) => return Err(<Error<T>>::AgendaIsExhausted.into()),732 }733 };716734717 Agenda::<T>::insert(when, agenda);735 Agenda::<T>::insert(when, agenda);718 Ok(index)736 Ok(index)740 origin,758 origin,741 _phantom: PhantomData,759 _phantom: PhantomData,742 };760 };743 Self::place_task(when, task).map_err(|x| x.0)761 Self::try_place_task(when, task)744 }762 }745763746 fn do_cancel(764 fn do_cancel(809 origin,827 origin,810 _phantom: Default::default(),828 _phantom: Default::default(),811 };829 };812 Self::place_task(when, task).map_err(|x| x.0)830 Self::try_place_task(when, task)813 }831 }814832815 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {833 fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult {1075 task.maybe_periodic = None;1093 task.maybe_periodic = None;1076 }1094 }1077 let wake = now.saturating_add(period);1095 let wake = now.saturating_add(period);1078 match Self::place_task(wake, task) {1096 Self::mandatory_place_task(wake, task);1079 Ok(_) => {}1080 Err((_, task)) => {1081 // TODO: Leave task in storage somewhere for it to be rescheduled1082 // manually.1083 T::Preimages::drop(&task.call);1084 Self::deposit_event(Event::PeriodicFailed {1085 task: (when, agenda_index),1086 id: task.maybe_id,1087 });1088 }1089 }1090 }1097 }1091 _ => {1098 _ => {1092 if let Some(ref id) = task.maybe_id {1099 if let Some(ref id) = task.maybe_id {pallets/scheduler-v2/src/tests.rsdiffbeforeafterboth--- a/pallets/scheduler-v2/src/tests.rs
+++ b/pallets/scheduler-v2/src/tests.rs
@@ -319,12 +319,12 @@
.into(),
);
// The call is still in the agenda.
- assert!(Agenda::<Test>::get(4)[0].is_some());
+ assert!(Agenda::<Test>::get(4).agenda[0].is_some());
});
}
#[test]
-fn scheduler_handles_periodic_failure() {
+fn scheduler_periodic_tasks_always_find_place() {
let max_weight: Weight = <Test as Config>::MaximumWeight::get();
let max_per_block = <Test as Config>::MaxScheduledPerBlock::get();
@@ -357,18 +357,17 @@
));
}
- // Going to block 24 will emit a `PeriodicFailed` event.
run_to_block(24);
assert_eq!(logger::log().len(), 6);
- assert_eq!(
- System::events().last().unwrap().event,
- crate::Event::PeriodicFailed {
- task: (24, 0),
- id: None
- }
- .into(),
- );
+ // The periodic task should be postponed
+ assert_eq!(<Agenda<Test>>::get(29).agenda.len(), 1);
+
+ run_to_block(27); // will call on_initialize(28)
+ assert_eq!(logger::log().len(), 6);
+
+ run_to_block(28); // will call on_initialize(29)
+ assert_eq!(logger::log().len(), 7);
});
}
@@ -596,7 +595,7 @@
));
run_to_block(3);
// Scheduled calls are in the agenda.
- assert_eq!(Agenda::<Test>::get(4).len(), 2);
+ assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);
assert!(logger::log().is_empty());
assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32]));
assert_ok!(Scheduler::cancel(RuntimeOrigin::root(), 4, 1));
@@ -669,7 +668,7 @@
));
run_to_block(3);
// Scheduled calls are in the agenda.
- assert_eq!(Agenda::<Test>::get(4).len(), 2);
+ assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);
assert!(logger::log().is_empty());
assert_ok!(Scheduler::cancel_named(
system::RawOrigin::Signed(1).into(),
@@ -739,7 +738,7 @@
));
run_to_block(3);
// Scheduled calls are in the agenda.
- assert_eq!(Agenda::<Test>::get(4).len(), 2);
+ assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);
assert!(logger::log().is_empty());
assert_noop!(
Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), [1u8; 32]),