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.rsdiffbeforeafterboth319 .into(),319 .into(),320 );320 );321 // The call is still in the agenda.321 // The call is still in the agenda.322 assert!(Agenda::<Test>::get(4)[0].is_some());322 assert!(Agenda::<Test>::get(4).agenda[0].is_some());323 });323 });324}324}325325326#[test]326#[test]327fn scheduler_handles_periodic_failure() {327fn scheduler_periodic_tasks_always_find_place() {328 let max_weight: Weight = <Test as Config>::MaximumWeight::get();328 let max_weight: Weight = <Test as Config>::MaximumWeight::get();329 let max_per_block = <Test as Config>::MaxScheduledPerBlock::get();329 let max_per_block = <Test as Config>::MaxScheduledPerBlock::get();330330357 ));357 ));358 }358 }359359360 // Going to block 24 will emit a `PeriodicFailed` event.361 run_to_block(24);360 run_to_block(24);362 assert_eq!(logger::log().len(), 6);361 assert_eq!(logger::log().len(), 6);363362363 // The periodic task should be postponed364 assert_eq!(364 assert_eq!(<Agenda<Test>>::get(29).agenda.len(), 1);365 System::events().last().unwrap().event,365366 crate::Event::PeriodicFailed {366 run_to_block(27); // will call on_initialize(28)367 task: (24, 0),367 assert_eq!(logger::log().len(), 6);368 id: None368369 }369 run_to_block(28); // will call on_initialize(29)370 .into(),370 assert_eq!(logger::log().len(), 7);371 );372 });371 });373}372}596 ));595 ));597 run_to_block(3);596 run_to_block(3);598 // Scheduled calls are in the agenda.597 // Scheduled calls are in the agenda.599 assert_eq!(Agenda::<Test>::get(4).len(), 2);598 assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);600 assert!(logger::log().is_empty());599 assert!(logger::log().is_empty());601 assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32]));600 assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32]));602 assert_ok!(Scheduler::cancel(RuntimeOrigin::root(), 4, 1));601 assert_ok!(Scheduler::cancel(RuntimeOrigin::root(), 4, 1));669 ));668 ));670 run_to_block(3);669 run_to_block(3);671 // Scheduled calls are in the agenda.670 // Scheduled calls are in the agenda.672 assert_eq!(Agenda::<Test>::get(4).len(), 2);671 assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);673 assert!(logger::log().is_empty());672 assert!(logger::log().is_empty());674 assert_ok!(Scheduler::cancel_named(673 assert_ok!(Scheduler::cancel_named(675 system::RawOrigin::Signed(1).into(),674 system::RawOrigin::Signed(1).into(),739 ));738 ));740 run_to_block(3);739 run_to_block(3);741 // Scheduled calls are in the agenda.740 // Scheduled calls are in the agenda.742 assert_eq!(Agenda::<Test>::get(4).len(), 2);741 assert_eq!(Agenda::<Test>::get(4).agenda.len(), 2);743 assert!(logger::log().is_empty());742 assert!(logger::log().is_empty());744 assert_noop!(743 assert_noop!(745 Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), [1u8; 32]),744 Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), [1u8; 32]),