forked from AcalaNetwork/Acala
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a new module-idle-scheduler (AcalaNetwork#1494)
* Added a new module-idle-scheduler Integrated the new module into Mandala and karura and acala runtime * Addressed some of the PR comments * Adjusted the minimumWeightRemainInBlock from 10% to 2% * Addressed some PR comments. * Fixed a clippy build error * Addressed some PR comments Co-authored-by: Roy Yang <[email protected]>
- Loading branch information
Showing
14 changed files
with
669 additions
and
7 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
[package] | ||
name = "module-idle-scheduler" | ||
version = "1.5.0" | ||
authors = ["Acala Developers"] | ||
edition = "2018" | ||
|
||
[dependencies] | ||
serde = { version = "1.0.124", optional = true } | ||
scale-info = { version = "1.0", default-features = false, features = ["derive"] } | ||
codec = { package = "parity-scale-codec", version = "2.3.1", default-features = false } | ||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11", default-features = false } | ||
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11", default-features = false } | ||
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11", default-features = false } | ||
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11", default-features = false } | ||
acala-primitives = { path = "../../primitives", default-features = false } | ||
|
||
[dev-dependencies] | ||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11" } | ||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.11" } | ||
|
||
[features] | ||
default = ["std"] | ||
std = [ | ||
"serde", | ||
"codec/std", | ||
"sp-runtime/std", | ||
"sp-std/std", | ||
"frame-support/std", | ||
"frame-system/std", | ||
"acala-primitives/std", | ||
] | ||
try-runtime = ["frame-support/try-runtime"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// This file is part of Acala. | ||
|
||
// Copyright (C) 2020-2021 Acala Foundation. | ||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
//! # Example Module | ||
//! | ||
//! A simple example of a FRAME pallet demonstrating | ||
//! concepts, APIs and structures common to most FRAME runtimes. | ||
#![cfg_attr(not(feature = "std"), no_std)] | ||
#![allow(clippy::unused_unit)] | ||
#![allow(unused_must_use)] | ||
use acala_primitives::{ | ||
task::{DispatchableTask, IdelScheduler}, | ||
Nonce, | ||
}; | ||
use codec::{Codec, EncodeLike}; | ||
use frame_support::pallet_prelude::*; | ||
use frame_system::pallet_prelude::*; | ||
use scale_info::TypeInfo; | ||
use sp_runtime::traits::{One, Zero}; | ||
use sp_std::{cmp::PartialEq, fmt::Debug, prelude::*}; | ||
|
||
mod mock; | ||
mod tests; | ||
mod weights; | ||
pub use module::*; | ||
pub use weights::WeightInfo; | ||
|
||
#[frame_support::pallet] | ||
pub mod module { | ||
use super::*; | ||
|
||
#[pallet::config] | ||
pub trait Config: frame_system::Config { | ||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>; | ||
|
||
/// Weight information for the extrinsics in this module. | ||
type WeightInfo: WeightInfo; | ||
|
||
/// Dispatchable tasks | ||
type Task: DispatchableTask + Codec + EncodeLike + Debug + Clone + PartialEq + TypeInfo; | ||
|
||
/// The minimum weight that should remain before idle tasks are dispatched. | ||
#[pallet::constant] | ||
type MinimumWeightRemainInBlock: Get<Weight>; | ||
} | ||
|
||
#[pallet::event] | ||
#[pallet::generate_deposit(pub fn deposit_event)] | ||
pub enum Event<T: Config> { | ||
/// A task has been dispatched on_idle. | ||
/// \[TaskId\] | ||
TaskDispatched(Nonce), | ||
} | ||
|
||
/// Some documentation | ||
#[pallet::storage] | ||
#[pallet::getter(fn tasks)] | ||
pub type Tasks<T: Config> = StorageMap<_, Twox64Concat, Nonce, T::Task, OptionQuery>; | ||
|
||
#[pallet::storage] | ||
#[pallet::getter(fn next_task_id)] | ||
pub type NextTaskId<T: Config> = StorageValue<_, Nonce, ValueQuery>; | ||
|
||
#[pallet::pallet] | ||
pub struct Pallet<T>(_); | ||
|
||
#[pallet::hooks] | ||
impl<T: Config> Hooks<T::BlockNumber> for Pallet<T> { | ||
fn on_idle(_n: T::BlockNumber, remaining_weight: Weight) -> Weight { | ||
Self::do_dispatch_tasks(remaining_weight) | ||
} | ||
} | ||
|
||
#[pallet::call] | ||
impl<T: Config> Pallet<T> { | ||
#[pallet::weight(< T as Config >::WeightInfo::schedule_task())] | ||
pub fn schedule_task(origin: OriginFor<T>, task: T::Task) -> DispatchResult { | ||
ensure_root(origin)?; | ||
Self::do_schedule_task(task); | ||
Ok(()) | ||
} | ||
} | ||
} | ||
|
||
impl<T: Config> Pallet<T> { | ||
/// Add the task to the queue to be dispatched later | ||
fn do_schedule_task(task: T::Task) { | ||
let id = Self::get_next_task_id(); | ||
Tasks::<T>::insert(id, task); | ||
} | ||
|
||
/// Retrieves the next task ID from storage, and increment it by one. | ||
fn get_next_task_id() -> Nonce { | ||
NextTaskId::<T>::mutate(|current| { | ||
let id = *current; | ||
*current = current.saturating_add(One::one()); | ||
id | ||
}) | ||
} | ||
|
||
/// Keep dispatching tasks in Storage, until insufficient weight remains. | ||
pub fn do_dispatch_tasks(total_weight: Weight) -> Weight { | ||
let mut weight_remaining = total_weight; | ||
if weight_remaining <= T::MinimumWeightRemainInBlock::get() { | ||
return Zero::zero(); | ||
} | ||
|
||
let mut completed_tasks: Vec<Nonce> = vec![]; | ||
|
||
for (id, task) in Tasks::<T>::iter() { | ||
let result = task.dispatch(weight_remaining); | ||
weight_remaining = weight_remaining.saturating_sub(result.used_weight); | ||
if result.finished { | ||
completed_tasks.push(id); | ||
} | ||
|
||
// If remaining weight falls below the minimmum, break from the loop. | ||
if weight_remaining <= T::MinimumWeightRemainInBlock::get() { | ||
break; | ||
} | ||
} | ||
|
||
// Deposit event and remove completed tasks. | ||
for id in completed_tasks { | ||
Self::deposit_event(Event::<T>::TaskDispatched(id)); | ||
Tasks::<T>::remove(id); | ||
} | ||
|
||
total_weight.saturating_sub(weight_remaining) | ||
} | ||
} | ||
|
||
impl<T: Config> IdelScheduler<T::Task> for Pallet<T> { | ||
fn schedule(task: T::Task) { | ||
Self::do_schedule_task(task); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// This file is part of Acala. | ||
|
||
// Copyright (C) 2020-2021 Acala Foundation. | ||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 | ||
|
||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
//! Mocks for example module. | ||
#![cfg(test)] | ||
|
||
use crate as module_idle_scheduler; | ||
use acala_primitives::{ | ||
define_combined_task, | ||
task::{DispatchableTask, TaskResult}, | ||
}; | ||
use frame_support::weights::Weight; | ||
use frame_support::{construct_runtime, parameter_types, traits::Everything}; | ||
|
||
use codec::{Decode, Encode}; | ||
use scale_info::TypeInfo; | ||
|
||
pub const BASE_WEIGHT: Weight = 1_000_000; | ||
|
||
parameter_types!( | ||
pub const BlockHashCount: u32 = 250; | ||
); | ||
|
||
pub type AccountId = u32; | ||
impl frame_system::Config for Runtime { | ||
type BaseCallFilter = Everything; | ||
type Origin = Origin; | ||
type Index = u64; | ||
type BlockNumber = u64; | ||
type Call = Call; | ||
type Hash = sp_runtime::testing::H256; | ||
type Hashing = sp_runtime::traits::BlakeTwo256; | ||
type AccountId = AccountId; | ||
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>; | ||
type Header = sp_runtime::testing::Header; | ||
type Event = Event; | ||
type BlockHashCount = BlockHashCount; | ||
type BlockWeights = (); | ||
type BlockLength = (); | ||
type DbWeight = (); | ||
type Version = (); | ||
type PalletInfo = PalletInfo; | ||
type AccountData = (); | ||
type OnNewAccount = (); | ||
type OnKilledAccount = (); | ||
type SystemWeightInfo = (); | ||
type SS58Prefix = (); | ||
type OnSetCode = (); | ||
} | ||
|
||
parameter_types!( | ||
pub const MinimumWeightRemainInBlock: Weight = 100_000_000_000; | ||
); | ||
|
||
impl module_idle_scheduler::Config for Runtime { | ||
type Event = Event; | ||
type WeightInfo = (); | ||
type Task = ScheduledTasks; | ||
type MinimumWeightRemainInBlock = MinimumWeightRemainInBlock; | ||
} | ||
|
||
// Mock dispatachable tasks | ||
#[derive(Clone, Debug, PartialEq, Encode, Decode, TypeInfo)] | ||
pub enum BalancesTask { | ||
#[codec(index = 0)] | ||
OnIdle, | ||
} | ||
impl DispatchableTask for BalancesTask { | ||
fn dispatch(self, weight: Weight) -> TaskResult { | ||
TaskResult { | ||
used_weight: BASE_WEIGHT, | ||
finished: weight >= BASE_WEIGHT, | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug, PartialEq, Encode, Decode, TypeInfo)] | ||
pub enum HomaLiteTask { | ||
#[codec(index = 0)] | ||
OnIdle, | ||
} | ||
impl DispatchableTask for HomaLiteTask { | ||
fn dispatch(self, weight: Weight) -> TaskResult { | ||
TaskResult { | ||
used_weight: BASE_WEIGHT, | ||
finished: weight >= BASE_WEIGHT, | ||
} | ||
} | ||
} | ||
|
||
define_combined_task! { | ||
pub enum ScheduledTasks { | ||
BalancesTask, | ||
HomaLiteTask, | ||
} | ||
} | ||
|
||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Runtime>; | ||
type Block = frame_system::mocking::MockBlock<Runtime>; | ||
|
||
construct_runtime!( | ||
pub enum Runtime where | ||
Block = Block, | ||
NodeBlock = Block, | ||
UncheckedExtrinsic = UncheckedExtrinsic | ||
{ | ||
System: frame_system::{Pallet, Call, Event<T>}, | ||
// NOTE: name Example here is needed in order to have same module prefix | ||
IdleScheduler: module_idle_scheduler::{Pallet, Call, Event<T>, Storage}, | ||
} | ||
); | ||
|
||
#[derive(Default)] | ||
pub struct ExtBuilder; | ||
impl ExtBuilder { | ||
pub fn build(self) -> sp_io::TestExternalities { | ||
let t = frame_system::GenesisConfig::default() | ||
.build_storage::<Runtime>() | ||
.unwrap(); | ||
|
||
let mut ext = sp_io::TestExternalities::new(t); | ||
ext.execute_with(|| System::set_block_number(1)); | ||
ext | ||
} | ||
} |
Oops, something went wrong.