Skip to content
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

Update Scheduler to Support Relay Chain Block Number Provider #6362

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,15 @@ parameter_types! {
#[cfg(not(feature = "runtime-benchmarks"))]
parameter_types! {
pub const MaxScheduledPerBlock: u32 = 50;
pub const MaxScheduledBlocks: u32 = 50;
pub const MaxStaleTaskAge: u32 = 10;
}

#[cfg(feature = "runtime-benchmarks")]
parameter_types! {
pub const MaxScheduledPerBlock: u32 = 200;
pub const MaxScheduledBlocks: u32 = 200;
pub const MaxStaleTaskAge: u32 = 40;
}

impl pallet_scheduler::Config for Runtime {
Expand All @@ -624,6 +628,9 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = EqualOrGreatestRootCmp;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
type MaxStaleTaskAge = MaxStaleTaskAge;
}

parameter_types! {
Expand Down
5 changes: 5 additions & 0 deletions polkadot/runtime/rococo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ parameter_types! {
pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
pub const MaxScheduledPerBlock: u32 = 50;
pub const MaxScheduledBlocks: u32 = 50;
pub const MaxStaleTaskAge: u32 = 10;
pub const NoPreimagePostponement: Option<u32> = Some(10);
}

Expand Down Expand Up @@ -331,6 +333,9 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = OriginPrivilegeCmp;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
type MaxStaleTaskAge = MaxStaleTaskAge;
}

parameter_types! {
Expand Down
5 changes: 5 additions & 0 deletions polkadot/runtime/westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ parameter_types! {
pub MaximumSchedulerWeight: frame_support::weights::Weight = Perbill::from_percent(80) *
BlockWeights::get().max_block;
pub const MaxScheduledPerBlock: u32 = 50;
pub const MaxScheduledBlocks: u32 = 50;
pub const MaxStaleTaskAge: u32 = 10;
pub const NoPreimagePostponement: Option<u32> = Some(10);
}

Expand All @@ -248,6 +250,9 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = weights::pallet_scheduler::WeightInfo<Runtime>;
type OriginPrivilegeCmp = frame_support::traits::EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
type MaxScheduledBlocks = MaxScheduledBlocks;
type MaxStaleTaskAge = MaxStaleTaskAge;
}

parameter_types! {
Expand Down
23 changes: 23 additions & 0 deletions prdoc/pr_6362.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
title: Update Scheduler to Support Relay Chain Block Number Provider

doc:
- audience: Runtime Dev
description: |
This PR adds the ability for the Scheduler pallet to specify its source of the block number.
This is needed for the scheduler pallet to work on a parachain which does not produce blocks
on a regular schedule, thus can use the relay chain as a block provider. Because blocks are
not produced regularly, we cannot make the assumption that the block number increases
monotonically, and thus have a new logic via a `Queue` to handle multiple blocks with valid
agenda passed between them.

This change only needs a migration for the `Queue`:
1. If the `BlockNumberProvider` continues to use the system pallet's block number
2. When a pallet deployed on the relay chain is moved to a parachain, but still uses the
relay chain's block number

However, we would need migrations if the deployed pallets are upgraded on an existing parachain,
and the `BlockNumberProvider` uses the relay chain block number.

crates:
- name: pallet-scheduler
bump: major
6 changes: 6 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,12 @@ impl pallet_scheduler::Config for Runtime {
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Runtime>;
#[cfg(feature = "runtime-benchmarks")]
type MaxScheduledBlocks = ConstU32<512>;
#[cfg(not(feature = "runtime-benchmarks"))]
type MaxScheduledBlocks = ConstU32<50>;
type MaxStaleTaskAge = ConstU32<10>;
}

impl pallet_glutton::Config for Runtime {
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/democracy/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ impl pallet_scheduler::Config for Test {
type WeightInfo = ();
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = ();
type BlockNumberProvider = frame_system::Pallet<Test>;
type MaxScheduledBlocks = ConstU32<100>;
type MaxStaleTaskAge = ConstU64<10>;
}

#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
Expand Down
3 changes: 3 additions & 0 deletions substrate/frame/referenda/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ impl pallet_scheduler::Config for Test {
type WeightInfo = ();
type OriginPrivilegeCmp = EqualPrivilegeOnly;
type Preimages = Preimage;
type BlockNumberProvider = frame_system::Pallet<Test>;
type MaxScheduledBlocks = ConstU32<100>;
type MaxStaleTaskAge = ConstU64<10>;
}
#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
impl pallet_balances::Config for Test {
Expand Down
82 changes: 57 additions & 25 deletions substrate/frame/scheduler/src/benchmarking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use alloc::vec;
use frame_benchmarking::v2::*;
use frame_support::{
ensure,
storage::bounded_vec::BoundedVec,
traits::{schedule::Priority, BoundedInline},
weights::WeightMeter,
};
Expand All @@ -32,7 +33,10 @@ type SystemCall<T> = frame_system::Call<T>;
type SystemOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;

const SEED: u32 = 0;
const BLOCK_NUMBER: u32 = 2;

fn block_number<T: Config>() -> u32 {
T::MaxScheduledBlocks::get()
}

fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
let events = frame_system::Pallet::<T>::events();
Expand All @@ -42,17 +46,23 @@ fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
assert_eq!(event, &system_event);
}

fn fill_queue<T: Config>(n: u32) {
let mut vec = Vec::<BlockNumberFor<T>>::new();
for i in 0..n {
vec.push(i.into());
}
let bounded_vec = BoundedVec::try_from(vec).unwrap();
Queue::<T>::put::<BoundedVec<_, _>>(bounded_vec);
}

/// Add `n` items to the schedule.
///
/// For `resolved`:
/// - `
/// - `None`: aborted (hash without preimage)
/// - `Some(true)`: hash resolves into call if possible, plain call otherwise
/// - `Some(false)`: plain call
fn fill_schedule<T: Config>(
when: frame_system::pallet_prelude::BlockNumberFor<T>,
n: u32,
) -> Result<(), &'static str> {
fn fill_schedule<T: Config>(when: BlockNumberFor<T>, n: u32) -> Result<(), &'static str> {
let t = DispatchTime::At(when);
let origin: <T as Config>::PalletsOrigin = frame_system::RawOrigin::Root.into();
for i in 0..n {
Expand Down Expand Up @@ -136,26 +146,28 @@ fn make_origin<T: Config>(signed: bool) -> <T as Config>::PalletsOrigin {
mod benchmarks {
use super::*;

// `service_agendas` when no work is done.
// `service_agenda` when no work is done.
#[benchmark]
fn service_agendas_base() {
let now = BLOCK_NUMBER.into();
IncompleteSince::<T>::put(now - One::one());
let now = BlockNumberFor::<T>::from(block_number::<T>());
Queue::<T>::put::<BoundedVec<_, _>>(BoundedVec::try_from(vec![now + One::one()]).unwrap());

#[block]
{
Pallet::<T>::service_agendas(&mut WeightMeter::new(), now, 0);
}

assert_eq!(IncompleteSince::<T>::get(), Some(now - One::one()));
let expected: BoundedVec<BlockNumberFor<T>, T::MaxScheduledBlocks> =
BoundedVec::try_from(vec![now + One::one()]).unwrap();
assert_eq!(Queue::<T>::get(), expected);
}

// `service_agenda` when no work is done.
#[benchmark]
fn service_agenda_base(
s: Linear<0, { T::MaxScheduledPerBlock::get() }>,
) -> Result<(), BenchmarkError> {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
fill_schedule::<T>(now, s)?;
let mut executed = 0;

Expand All @@ -173,7 +185,7 @@ mod benchmarks {
// dispatched (e.g. due to being overweight).
#[benchmark]
fn service_task_base() {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
let task = make_task::<T>(false, false, false, None, 0);
// prevent any tasks from actually being executed as we only want the surrounding weight.
let mut counter = WeightMeter::with_limit(Weight::zero());
Expand All @@ -196,7 +208,7 @@ mod benchmarks {
fn service_task_fetched(
s: Linear<{ BoundedInline::bound() as u32 }, { T::Preimages::MAX_LENGTH as u32 }>,
) {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
let task = make_task::<T>(false, false, false, Some(s), 0);
// prevent any tasks from actually being executed as we only want the surrounding weight.
let mut counter = WeightMeter::with_limit(Weight::zero());
Expand All @@ -214,7 +226,7 @@ mod benchmarks {
// dispatched (e.g. due to being overweight).
#[benchmark]
fn service_task_named() {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
let task = make_task::<T>(false, true, false, None, 0);
// prevent any tasks from actually being executed as we only want the surrounding weight.
let mut counter = WeightMeter::with_limit(Weight::zero());
Expand All @@ -232,7 +244,7 @@ mod benchmarks {
// dispatched (e.g. due to being overweight).
#[benchmark]
fn service_task_periodic() {
let now = BLOCK_NUMBER.into();
let now = block_number::<T>().into();
let task = make_task::<T>(true, false, false, None, 0);
// prevent any tasks from actually being executed as we only want the surrounding weight.
let mut counter = WeightMeter::with_limit(Weight::zero());
Expand Down Expand Up @@ -286,27 +298,33 @@ mod benchmarks {
fn schedule(
s: Linear<0, { T::MaxScheduledPerBlock::get() - 1 }>,
) -> Result<(), BenchmarkError> {
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();
let periodic = Some((BlockNumberFor::<T>::one(), 100));
let priority = 0;
// Essentially a no-op call.
let call = Box::new(SystemCall::set_storage { items: vec![] }.into());

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get() - 1);

#[extrinsic_call]
_(RawOrigin::Root, when, periodic, priority, call);

ensure!(Agenda::<T>::get(when).len() == s as usize + 1, "didn't add to schedule");
ensure!(Agenda::<T>::get(when).len() == (s + 1) as usize, "didn't add to schedule");
ensure!(
Queue::<T>::get().len() == T::MaxScheduledBlocks::get() as usize,
"didn't add to queue"
);

Ok(())
}

#[benchmark]
fn cancel(s: Linear<1, { T::MaxScheduledPerBlock::get() }>) -> Result<(), BenchmarkError> {
let when = BLOCK_NUMBER.into();
let when = (block_number::<T>() - 1).into();

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get());
assert_eq!(Agenda::<T>::get(when).len(), s as usize);
let schedule_origin =
T::ScheduleOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
Expand All @@ -327,6 +345,10 @@ mod benchmarks {
s > 1 || Agenda::<T>::get(when).len() == 0,
"remove from schedule if only 1 task scheduled for `when`"
);
ensure!(
s > 1 || Queue::<T>::get().len() == T::MaxScheduledBlocks::get() as usize - 1,
"didn't remove from queue"
);

Ok(())
}
Expand All @@ -336,18 +358,23 @@ mod benchmarks {
s: Linear<0, { T::MaxScheduledPerBlock::get() - 1 }>,
) -> Result<(), BenchmarkError> {
let id = u32_to_name(s);
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();
let periodic = Some((BlockNumberFor::<T>::one(), 100));
let priority = 0;
// Essentially a no-op call.
let call = Box::new(SystemCall::set_storage { items: vec![] }.into());

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get() - 1);

#[extrinsic_call]
_(RawOrigin::Root, id, when, periodic, priority, call);

ensure!(Agenda::<T>::get(when).len() == s as usize + 1, "didn't add to schedule");
ensure!(Agenda::<T>::get(when).len() == (s + 1) as usize, "didn't add to schedule");
ensure!(
Queue::<T>::get().len() == T::MaxScheduledBlocks::get() as usize,
"didn't add to queue"
);

Ok(())
}
Expand All @@ -356,9 +383,10 @@ mod benchmarks {
fn cancel_named(
s: Linear<1, { T::MaxScheduledPerBlock::get() }>,
) -> Result<(), BenchmarkError> {
let when = BLOCK_NUMBER.into();
let when = (block_number::<T>() - 1).into();

fill_schedule::<T>(when, s)?;
fill_queue::<T>(T::MaxScheduledBlocks::get());

#[extrinsic_call]
_(RawOrigin::Root, u32_to_name(0));
Expand All @@ -376,6 +404,10 @@ mod benchmarks {
s > 1 || Agenda::<T>::get(when).len() == 0,
"remove from schedule if only 1 task scheduled for `when`"
);
ensure!(
s > 1 || Queue::<T>::get().len() == T::MaxScheduledBlocks::get() as usize - 1,
"didn't remove from queue"
);

Ok(())
}
Expand All @@ -384,7 +416,7 @@ mod benchmarks {
fn schedule_retry(
s: Linear<1, { T::MaxScheduledPerBlock::get() }>,
) -> Result<(), BenchmarkError> {
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand Down Expand Up @@ -420,7 +452,7 @@ mod benchmarks {
#[benchmark]
fn set_retry() -> Result<(), BenchmarkError> {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -445,7 +477,7 @@ mod benchmarks {
#[benchmark]
fn set_retry_named() -> Result<(), BenchmarkError> {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -470,7 +502,7 @@ mod benchmarks {
#[benchmark]
fn cancel_retry() -> Result<(), BenchmarkError> {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand All @@ -491,7 +523,7 @@ mod benchmarks {
#[benchmark]
fn cancel_retry_named() -> Result<(), BenchmarkError> {
let s = T::MaxScheduledPerBlock::get();
let when = BLOCK_NUMBER.into();
let when = block_number::<T>().into();

fill_schedule::<T>(when, s)?;
let name = u32_to_name(s - 1);
Expand Down
Loading
Loading