From af77a9aa9e0d09ad152272d0e1203685da378537 Mon Sep 17 00:00:00 2001 From: Robert Habermeier Date: Wed, 27 May 2020 22:26:54 -0400 Subject: [PATCH] scheduler module skeleton --- runtime/parachains/src/scheduler.rs | 123 ++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/runtime/parachains/src/scheduler.rs b/runtime/parachains/src/scheduler.rs index 1f45de2df705..51490f02e8e6 100644 --- a/runtime/parachains/src/scheduler.rs +++ b/runtime/parachains/src/scheduler.rs @@ -13,3 +13,126 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . + +//! The scheduler module for parachains and parathreads. +//! +//! This module is responsible for two main tasks: +//! - Paritioning validators into groups and assigning groups to parachains and parathreads +//! - Scheduling parachains and parathreads +//! +//! It aims to achieve these tasks with these goals in mind: +//! - It should be possible to know at least a block ahead-of-time, ideally more, +//! which validators are going to be assigned to which parachains. +//! - Parachains that have a candidate pending availability in this fork of the chain +//! should not be assigned. +//! - Validator assignments should not be gameable. Malicious cartels should not be able to +//! manipulate the scheduler to assign themselves as desired. +//! - High or close to optimal throughput of parachains and parathreads. Work among validator groups should be balanced. +//! +//! The Scheduler manages resource allocation using the concept of "Execution Cores". +//! There will be one execution core for each parachain, and a fixed number of cores +//! used for multiplexing parathreads. Validators will be partitioned into groups, with the same +//! number of groups as execution cores. Validator groups will be assigned to different execution cores +//! over time. + +use sp_std::prelude::*; +use primitives::{ + parachain::{ValidatorId, Id as ParaId, CollatorId, ValidatorIndex}, +}; +use frame_support::{ + decl_storage, decl_module, decl_error, + dispatch::DispatchResult, + weights::{DispatchClass, Weight}, +}; +use codec::{Encode, Decode}; +use system::ensure_root; + +use crate::{configuration, paras}; + +// A claim on authorship for a specific parathread. +#[derive(Encode, Decode)] +struct ParathreadClaim(ParaId, CollatorId); + +// A parathread that is scheduled onto a specific core. +#[derive(Encode, Decode)] +struct ParathreadEntry { + claim: ParathreadClaim, + core: CoreIndex, +} + +// what a core is occupied by +#[derive(Encode, Decode)] +enum CoreOccupied { + Parathread(ParathreadClaim, u32), // claim & retries + Parachain, +} + +/// The unique (during session) index of a core. +#[derive(Encode, Decode)] +pub(crate) struct CoreIndex(u32); + +pub trait Trait: system::Trait + configuration::Trait + paras::Trait { } + +decl_storage! { + trait Store for Module as Scheduler { + /// All of the validator groups, one for each core. + ValidatorGroups: Vec>; + /// A queue of upcoming claims and which core they should be mapped onto. + ParathreadQueue: Vec; + /// One entry for each execution core. Entries are `None` if the core is not currently occupied. + /// Can be temporarily `Some` if scheduled but not occupied. + /// The i'th parachain belongs to the i'th core, with the remaining cores all being + /// parathread-multiplexers. + ExecutionCores: Vec>; + /// An index used to ensure that only one claim on a parathread exists in the queue or retry queue + /// or is currently being handled by an occupied core. + ParathreadClaimIndex: Vec<(ParaId, CollatorId)>; + /// The block number where the session start occurred. Used to track how many group rotation have + /// occurred. + SessionStartBlock: BlockNumber, + /// Currently scheduled cores - free but up to be occupied. Ephemeral storage item that's wiped on + /// finalization. + Scheduled get(fn scheduled): Vec, // sorted by ParaId + } +} + +impl Module { + /// Called by the initializer to initialize the scheduler module. + pub(crate) fn initializer_initialize(_now: T::BlockNumber) -> Weight { + Self::schedule(Vec::new()); + + 0 + } + + /// Called by the initializer to finalize the scheduler module. + pub(crate) fn initializer_finalize() { + // TODO [now]: free all scheduled cores and return parathread claims to queue, with retries incremented. + } + + /// Called by the initializer to note that a new session has started. + pub(crate) fn initializer_on_new_session(_validators: &[ValidatorId], _queued: &[ValidatorId]) { + let config = >::config(); + + SessionStartBlock::set(>::block_number()); + ExecutionCores::mutate(|cores| { + // clear all occupied cores. + for maybe_occupied in cores.iter_mut() { + if let Some(CoreOccupied::Parathread(claim, retries)) = maybe_occupied.take() { + // TODO [now]: return to parathread queue, do not increment retries + } + } + + let n_parachains = >::parachains().len(); + + cores.resize(n_parachains + config.parathread_cores, None); + }); + + // TODO [now]: shuffle validators into groups + + // TODO [now]: prune out all parathread claims with too many retries. + } + + pub(crate) fn schedule(just_freed_cores: Vec) { + // TODO [now]: schedule new core assignments. + } +}