git.delta.rocks / unique-network / refs/commits / 25bffe721f74

difftreelog

fix periodic tasks always find place

Daniel Shiposha2022-10-26parent: #ccd56ff.patch.diff
in: master

2 files changed

modifiedpallets/scheduler-v2/src/lib.rsdiffbeforeafterboth
96 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;
101101
102pub use pallet::*;102pub use pallet::*;
254}254}
255255
256impl<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 }
261261
262 self.free_places = self.free_places.saturating_sub(1);262 self.free_places = self.free_places.saturating_sub(1);
263263
264 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 }
685
686 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 }
689
690 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 }
690696
691 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 }
707714
708 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;
721
722 let index = loop {
723 agenda = Agenda::<T>::get(*when);
724
713 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 };
716734
717 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 }
745763
746 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 }
814832
815 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 rescheduled
1082 // 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 {
modifiedpallets/scheduler-v2/src/tests.rsdiffbeforeafterboth
319 .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}
325325
326#[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();
330330
357 ));357 ));
358 }358 }
359359
360 // 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);
363362
363 // The periodic task should be postponed
364 assert_eq!(364 assert_eq!(<Agenda<Test>>::get(29).agenda.len(), 1);
365 System::events().last().unwrap().event,365
366 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: None368
369 }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]),