Skip to content

Commit

Permalink
Add retry mechanics to pallet-scheduler (paritytech#3060)
Browse files Browse the repository at this point in the history
Fixes paritytech#3014 

This PR adds retry mechanics to `pallet-scheduler`, as described in the
issue above.

Users can now set a retry configuration for a task so that, in case its
scheduled run fails, it will be retried after a number of blocks, for a
specified number of times or until it succeeds.

If a retried task runs successfully before running out of retries, its
remaining retry counter will be reset to the initial value. If a retried
task runs out of retries, it will be removed from the schedule.

Tasks which need to be scheduled for a retry are still subject to weight
metering and agenda space, same as a regular task. Periodic tasks will
have their periodic schedule put on hold while the task is retrying.

---------

Signed-off-by: georgepisaltu <[email protected]>
Co-authored-by: command-bot <>
  • Loading branch information
georgepisaltu authored Feb 16, 2024
1 parent 780fe4d commit 0d311fd
Show file tree
Hide file tree
Showing 5 changed files with 1,817 additions and 162 deletions.
111 changes: 110 additions & 1 deletion substrate/frame/scheduler/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,28 @@ use frame_benchmarking::v1::{account, benchmarks, BenchmarkError};
use frame_support::{
ensure,
traits::{schedule::Priority, BoundedInline},
weights::WeightMeter,
};
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
use sp_std::{prelude::*, vec};

use crate::Pallet as Scheduler;
use frame_system::Call as SystemCall;
use frame_system::{Call as SystemCall, EventRecord};

const SEED: u32 = 0;

const BLOCK_NUMBER: u32 = 2;

type SystemOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;

fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
// compare to the last event record
let EventRecord { event, .. } = &events[events.len() - 1];
assert_eq!(event, &system_event);
}

/// Add `n` items to the schedule.
///
/// For `resolved`:
Expand Down Expand Up @@ -306,5 +315,105 @@ benchmarks! {
);
}

schedule_retry {
let s in 1 .. T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
let address = Lookup::<T>::get(name).unwrap();
let period: BlockNumberFor<T> = 1u32.into();
let root: <T as Config>::PalletsOrigin = frame_system::RawOrigin::Root.into();
let retry_config = RetryConfig { total_retries: 10, remaining: 10, period };
Retries::<T>::insert(address, retry_config);
let (mut when, index) = address;
let task = Agenda::<T>::get(when)[index as usize].clone().unwrap();
let mut weight_counter = WeightMeter::with_limit(T::MaximumWeight::get());
}: {
Scheduler::<T>::schedule_retry(&mut weight_counter, when, when, index, &task, retry_config);
} verify {
when = when + BlockNumberFor::<T>::one();
assert_eq!(
Retries::<T>::get((when, 0)),
Some(RetryConfig { total_retries: 10, remaining: 9, period })
);
}

set_retry {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
let address = Lookup::<T>::get(name).unwrap();
let (when, index) = address;
let period = BlockNumberFor::<T>::one();
}: _(RawOrigin::Root, (when, index), 10, period)
verify {
assert_eq!(
Retries::<T>::get((when, index)),
Some(RetryConfig { total_retries: 10, remaining: 10, period })
);
assert_last_event::<T>(
Event::RetrySet { task: address, id: None, period, retries: 10 }.into(),
);
}

set_retry_named {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
let address = Lookup::<T>::get(name).unwrap();
let (when, index) = address;
let period = BlockNumberFor::<T>::one();
}: _(RawOrigin::Root, name, 10, period)
verify {
assert_eq!(
Retries::<T>::get((when, index)),
Some(RetryConfig { total_retries: 10, remaining: 10, period })
);
assert_last_event::<T>(
Event::RetrySet { task: address, id: Some(name), period, retries: 10 }.into(),
);
}

cancel_retry {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
let address = Lookup::<T>::get(name).unwrap();
let (when, index) = address;
let period = BlockNumberFor::<T>::one();
assert!(Scheduler::<T>::set_retry(RawOrigin::Root.into(), (when, index), 10, period).is_ok());
}: _(RawOrigin::Root, (when, index))
verify {
assert!(!Retries::<T>::contains_key((when, index)));
assert_last_event::<T>(
Event::RetryCancelled { task: address, id: None }.into(),
);
}

cancel_retry_named {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
let address = Lookup::<T>::get(name).unwrap();
let (when, index) = address;
let period = BlockNumberFor::<T>::one();
assert!(Scheduler::<T>::set_retry_named(RawOrigin::Root.into(), name, 10, period).is_ok());
}: _(RawOrigin::Root, name)
verify {
assert!(!Retries::<T>::contains_key((when, index)));
assert_last_event::<T>(
Event::RetryCancelled { task: address, id: Some(name) }.into(),
);
}

impl_benchmark_test_suite!(Scheduler, crate::mock::new_test_ext(), crate::mock::Test);
}
Loading

0 comments on commit 0d311fd

Please sign in to comment.