Simplify subsystem jobs (#2037)

* Simplify subsystem jobs

This pr simplifies the subsystem jobs interface. Instead of requiring an
extra message that is used to signal that a job should be ended, a job
now ends when the receiver returns `None`. Besides that it changes the
interface to enforce that messages to a job provide a relay parent.

* Drop ToJobTrait

* Remove FromJob

We always convert this message to FromJobCommand anyway.
This commit is contained in:
Bastian Köcher
2020-11-30 17:01:26 +01:00
committed by GitHub
parent 2d4aa3a42e
commit 536dceb4f6
9 changed files with 246 additions and 686 deletions
+23 -106
View File
@@ -40,8 +40,7 @@ use polkadot_subsystem::{
messages::{
AllMessages, AvailabilityStoreMessage, CandidateBackingMessage, CandidateSelectionMessage,
CandidateValidationMessage, NewBackedCandidate, PoVDistributionMessage, ProvisionableData,
ProvisionerMessage, RuntimeApiMessage, StatementDistributionMessage, ValidationFailed,
RuntimeApiRequest,
ProvisionerMessage, StatementDistributionMessage, ValidationFailed, RuntimeApiRequest,
},
};
use polkadot_node_subsystem_util::{
@@ -93,9 +92,9 @@ struct CandidateBackingJob {
/// The hash of the relay parent on top of which this job is doing it's work.
parent: Hash,
/// Inbound message channel receiving part.
rx_to: mpsc::Receiver<ToJob>,
rx_to: mpsc::Receiver<CandidateBackingMessage>,
/// Outbound message channel sending part.
tx_from: mpsc::Sender<FromJob>,
tx_from: mpsc::Sender<FromJobCommand>,
/// The `ParaId` assigned to this validator
assignment: ParaId,
/// The collator required to author the candidate, if any.
@@ -151,84 +150,6 @@ impl TableContextTrait for TableContext {
}
}
/// A message type that is sent from `CandidateBackingSubsystem` to `CandidateBackingJob`.
pub enum ToJob {
/// A `CandidateBackingMessage`.
CandidateBacking(CandidateBackingMessage),
/// Stop working.
Stop,
}
impl TryFrom<AllMessages> for ToJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::CandidateBacking(msg) => Ok(ToJob::CandidateBacking(msg)),
_ => Err(()),
}
}
}
impl From<CandidateBackingMessage> for ToJob {
fn from(msg: CandidateBackingMessage) -> Self {
Self::CandidateBacking(msg)
}
}
impl util::ToJobTrait for ToJob {
const STOP: Self = ToJob::Stop;
fn relay_parent(&self) -> Option<Hash> {
match self {
Self::CandidateBacking(cb) => cb.relay_parent(),
Self::Stop => None,
}
}
}
/// A message type that is sent from `CandidateBackingJob` to `CandidateBackingSubsystem`.
enum FromJob {
AvailabilityStore(AvailabilityStoreMessage),
RuntimeApiMessage(RuntimeApiMessage),
CandidateValidation(CandidateValidationMessage),
CandidateSelection(CandidateSelectionMessage),
Provisioner(ProvisionerMessage),
PoVDistribution(PoVDistributionMessage),
StatementDistribution(StatementDistributionMessage),
}
impl From<FromJob> for FromJobCommand {
fn from(f: FromJob) -> FromJobCommand {
FromJobCommand::SendMessage(match f {
FromJob::AvailabilityStore(msg) => AllMessages::AvailabilityStore(msg),
FromJob::RuntimeApiMessage(msg) => AllMessages::RuntimeApi(msg),
FromJob::CandidateValidation(msg) => AllMessages::CandidateValidation(msg),
FromJob::CandidateSelection(msg) => AllMessages::CandidateSelection(msg),
FromJob::StatementDistribution(msg) => AllMessages::StatementDistribution(msg),
FromJob::PoVDistribution(msg) => AllMessages::PoVDistribution(msg),
FromJob::Provisioner(msg) => AllMessages::Provisioner(msg),
})
}
}
impl TryFrom<AllMessages> for FromJob {
type Error = &'static str;
fn try_from(f: AllMessages) -> Result<Self, Self::Error> {
match f {
AllMessages::AvailabilityStore(msg) => Ok(FromJob::AvailabilityStore(msg)),
AllMessages::RuntimeApi(msg) => Ok(FromJob::RuntimeApiMessage(msg)),
AllMessages::CandidateValidation(msg) => Ok(FromJob::CandidateValidation(msg)),
AllMessages::CandidateSelection(msg) => Ok(FromJob::CandidateSelection(msg)),
AllMessages::StatementDistribution(msg) => Ok(FromJob::StatementDistribution(msg)),
AllMessages::PoVDistribution(msg) => Ok(FromJob::PoVDistribution(msg)),
AllMessages::Provisioner(msg) => Ok(FromJob::Provisioner(msg)),
_ => Err("can't convert this AllMessages variant to FromJob"),
}
}
}
struct InvalidErasureRoot;
// It looks like it's not possible to do an `impl From` given the current state of
@@ -301,12 +222,10 @@ fn table_attested_to_backed(
impl CandidateBackingJob {
/// Run asynchronously.
async fn run_loop(mut self) -> Result<(), Error> {
while let Some(msg) = self.rx_to.next().await {
match msg {
ToJob::CandidateBacking(msg) => {
self.process_msg(msg).await?;
}
ToJob::Stop => break,
loop {
match self.rx_to.next().await {
Some(msg) => self.process_msg(msg).await?,
None => break,
}
}
@@ -317,9 +236,7 @@ impl CandidateBackingJob {
&mut self,
candidate: CandidateReceipt,
) -> Result<(), Error> {
self.tx_from.send(FromJob::CandidateSelection(
CandidateSelectionMessage::Invalid(self.parent, candidate)
)).await?;
self.tx_from.send(AllMessages::from(CandidateSelectionMessage::Invalid(self.parent, candidate)).into()).await?;
Ok(())
}
@@ -664,7 +581,7 @@ impl CandidateBackingJob {
}
async fn send_to_provisioner(&mut self, msg: ProvisionerMessage) -> Result<(), Error> {
self.tx_from.send(FromJob::Provisioner(msg)).await?;
self.tx_from.send(AllMessages::from(msg).into()).await?;
Ok(())
}
@@ -674,9 +591,9 @@ impl CandidateBackingJob {
descriptor: CandidateDescriptor,
pov: Arc<PoV>,
) -> Result<(), Error> {
self.tx_from.send(FromJob::PoVDistribution(
self.tx_from.send(AllMessages::from(
PoVDistributionMessage::DistributePoV(self.parent, descriptor, pov),
)).await.map_err(Into::into)
).into()).await.map_err(Into::into)
}
async fn request_pov_from_distribution(
@@ -685,9 +602,9 @@ impl CandidateBackingJob {
) -> Result<Arc<PoV>, Error> {
let (tx, rx) = oneshot::channel();
self.tx_from.send(FromJob::PoVDistribution(
self.tx_from.send(AllMessages::from(
PoVDistributionMessage::FetchPoV(self.parent, descriptor, tx)
)).await?;
).into()).await?;
Ok(rx.await?)
}
@@ -699,13 +616,14 @@ impl CandidateBackingJob {
) -> Result<ValidationResult, Error> {
let (tx, rx) = oneshot::channel();
self.tx_from.send(FromJob::CandidateValidation(
self.tx_from.send(
AllMessages::from(
CandidateValidationMessage::ValidateFromChainState(
candidate,
pov,
tx,
)
)
).into(),
).await?;
Ok(rx.await??)
@@ -719,7 +637,7 @@ impl CandidateBackingJob {
available_data: AvailableData,
) -> Result<(), Error> {
let (tx, rx) = oneshot::channel();
self.tx_from.send(FromJob::AvailabilityStore(
self.tx_from.send(AllMessages::from(
AvailabilityStoreMessage::StoreAvailableData(
candidate_hash,
id,
@@ -727,7 +645,7 @@ impl CandidateBackingJob {
available_data,
tx,
)
)
).into(),
).await?;
let _ = rx.await?;
@@ -777,15 +695,14 @@ impl CandidateBackingJob {
async fn distribute_signed_statement(&mut self, s: SignedFullStatement) -> Result<(), Error> {
let smsg = StatementDistributionMessage::Share(self.parent, s);
self.tx_from.send(FromJob::StatementDistribution(smsg)).await?;
self.tx_from.send(AllMessages::from(smsg).into()).await?;
Ok(())
}
}
impl util::JobTrait for CandidateBackingJob {
type ToJob = ToJob;
type FromJob = FromJob;
type ToJob = CandidateBackingMessage;
type Error = Error;
type RunArgs = SyncCryptoStorePtr;
type Metrics = Metrics;
@@ -798,7 +715,7 @@ impl util::JobTrait for CandidateBackingJob {
keystore: SyncCryptoStorePtr,
metrics: Metrics,
rx_to: mpsc::Receiver<Self::ToJob>,
mut tx_from: mpsc::Sender<Self::FromJob>,
mut tx_from: mpsc::Sender<FromJobCommand>,
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
async move {
macro_rules! try_runtime_api {
@@ -1000,7 +917,7 @@ impl metrics::Metrics for Metrics {
}
}
delegated_subsystem!(CandidateBackingJob(SyncCryptoStorePtr, Metrics) <- ToJob as CandidateBackingSubsystem);
delegated_subsystem!(CandidateBackingJob(SyncCryptoStorePtr, Metrics) <- CandidateBackingMessage as CandidateBackingSubsystem);
#[cfg(test)]
mod tests {
@@ -1013,7 +930,7 @@ mod tests {
GroupRotationInfo,
};
use polkadot_subsystem::{
messages::RuntimeApiRequest,
messages::{RuntimeApiRequest, RuntimeApiMessage},
ActiveLeavesUpdate, FromOverseer, OverseerSignal,
};
use polkadot_node_primitives::InvalidCandidate;
@@ -14,4 +14,3 @@ polkadot-node-subsystem-util = { path = "../../subsystem-util" }
sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" }
wasm-timer = "0.2.5"
thiserror = "1.0.22"
derive_more = "0.99.11"
+39 -93
View File
@@ -25,16 +25,15 @@ use sp_keystore::{Error as KeystoreError, SyncCryptoStorePtr};
use polkadot_node_subsystem::{
messages::{
AllMessages, AvailabilityStoreMessage, BitfieldDistributionMessage,
BitfieldSigningMessage, CandidateBackingMessage, RuntimeApiMessage, RuntimeApiRequest,
BitfieldSigningMessage, RuntimeApiMessage, RuntimeApiRequest,
},
errors::RuntimeApiError,
};
use polkadot_node_subsystem_util::{
self as util, JobManager, JobTrait, ToJobTrait, Validator, FromJobCommand,
metrics::{self, prometheus},
self as util, JobManager, JobTrait, Validator, FromJobCommand, metrics::{self, prometheus},
};
use polkadot_primitives::v1::{AvailabilityBitfield, CoreState, Hash, ValidatorIndex};
use std::{convert::TryFrom, pin::Pin, time::Duration, iter::FromIterator};
use std::{pin::Pin, time::Duration, iter::FromIterator};
use wasm_timer::{Delay, Instant};
use thiserror::Error;
@@ -45,76 +44,6 @@ const LOG_TARGET: &str = "bitfield_signing";
/// Each `BitfieldSigningJob` prepares a signed bitfield for a single relay parent.
pub struct BitfieldSigningJob;
/// Messages which a `BitfieldSigningJob` is prepared to receive.
#[allow(missing_docs)]
pub enum ToJob {
BitfieldSigning(BitfieldSigningMessage),
Stop,
}
impl ToJobTrait for ToJob {
const STOP: Self = ToJob::Stop;
fn relay_parent(&self) -> Option<Hash> {
match self {
Self::BitfieldSigning(bsm) => bsm.relay_parent(),
Self::Stop => None,
}
}
}
impl TryFrom<AllMessages> for ToJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::BitfieldSigning(bsm) => Ok(ToJob::BitfieldSigning(bsm)),
_ => Err(()),
}
}
}
impl From<BitfieldSigningMessage> for ToJob {
fn from(bsm: BitfieldSigningMessage) -> ToJob {
ToJob::BitfieldSigning(bsm)
}
}
/// Messages which may be sent from a `BitfieldSigningJob`.
#[allow(missing_docs)]
#[derive(Debug, derive_more::From)]
pub enum FromJob {
AvailabilityStore(AvailabilityStoreMessage),
BitfieldDistribution(BitfieldDistributionMessage),
CandidateBacking(CandidateBackingMessage),
RuntimeApi(RuntimeApiMessage),
}
impl From<FromJob> for FromJobCommand {
fn from(from_job: FromJob) -> FromJobCommand {
FromJobCommand::SendMessage(match from_job {
FromJob::AvailabilityStore(asm) => AllMessages::AvailabilityStore(asm),
FromJob::BitfieldDistribution(bdm) => AllMessages::BitfieldDistribution(bdm),
FromJob::CandidateBacking(cbm) => AllMessages::CandidateBacking(cbm),
FromJob::RuntimeApi(ram) => AllMessages::RuntimeApi(ram),
})
}
}
impl TryFrom<AllMessages> for FromJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::AvailabilityStore(asm) => Ok(Self::AvailabilityStore(asm)),
AllMessages::BitfieldDistribution(bdm) => Ok(Self::BitfieldDistribution(bdm)),
AllMessages::CandidateBacking(cbm) => Ok(Self::CandidateBacking(cbm)),
AllMessages::RuntimeApi(ram) => Ok(Self::RuntimeApi(ram)),
_ => Err(()),
}
}
}
/// Errors we may encounter in the course of executing the `BitfieldSigningSubsystem`.
#[derive(Debug, Error)]
pub enum Error {
@@ -145,7 +74,7 @@ async fn get_core_availability(
relay_parent: Hash,
core: CoreState,
validator_idx: ValidatorIndex,
sender: &Mutex<&mut mpsc::Sender<FromJob>>,
sender: &Mutex<&mut mpsc::Sender<FromJobCommand>>,
) -> Result<bool, Error> {
if let CoreState::Occupied(core) = core {
let (tx, rx) = oneshot::channel();
@@ -153,10 +82,10 @@ async fn get_core_availability(
.lock()
.await
.send(
RuntimeApiMessage::Request(
AllMessages::from(RuntimeApiMessage::Request(
relay_parent,
RuntimeApiRequest::CandidatePendingAvailability(core.para_id, tx),
).into(),
)).into(),
)
.await?;
@@ -174,11 +103,11 @@ async fn get_core_availability(
.lock()
.await
.send(
AvailabilityStoreMessage::QueryChunkAvailability(
AllMessages::from(AvailabilityStoreMessage::QueryChunkAvailability(
committed_candidate_receipt.hash(),
validator_idx,
tx,
).into(),
)).into(),
)
.await?;
return rx.await.map_err(Into::into);
@@ -188,9 +117,14 @@ async fn get_core_availability(
}
/// delegates to the v1 runtime API
async fn get_availability_cores(relay_parent: Hash, sender: &mut mpsc::Sender<FromJob>) -> Result<Vec<CoreState>, Error> {
async fn get_availability_cores(
relay_parent: Hash,
sender: &mut mpsc::Sender<FromJobCommand>,
) -> Result<Vec<CoreState>, Error> {
let (tx, rx) = oneshot::channel();
sender.send(RuntimeApiMessage::Request(relay_parent, RuntimeApiRequest::AvailabilityCores(tx)).into()).await?;
sender
.send(AllMessages::from(RuntimeApiMessage::Request(relay_parent, RuntimeApiRequest::AvailabilityCores(tx))).into())
.await?;
match rx.await {
Ok(Ok(out)) => Ok(out),
Ok(Err(runtime_err)) => Err(runtime_err.into()),
@@ -206,7 +140,7 @@ async fn get_availability_cores(relay_parent: Hash, sender: &mut mpsc::Sender<Fr
async fn construct_availability_bitfield(
relay_parent: Hash,
validator_idx: ValidatorIndex,
sender: &mut mpsc::Sender<FromJob>,
sender: &mut mpsc::Sender<FromJobCommand>,
) -> Result<AvailabilityBitfield, Error> {
// get the set of availability cores from the runtime
let availability_cores = get_availability_cores(relay_parent, sender).await?;
@@ -275,8 +209,7 @@ impl metrics::Metrics for Metrics {
}
impl JobTrait for BitfieldSigningJob {
type ToJob = ToJob;
type FromJob = FromJob;
type ToJob = BitfieldSigningMessage;
type Error = Error;
type RunArgs = SyncCryptoStorePtr;
type Metrics = Metrics;
@@ -289,8 +222,8 @@ impl JobTrait for BitfieldSigningJob {
relay_parent: Hash,
keystore: Self::RunArgs,
metrics: Self::Metrics,
_receiver: mpsc::Receiver<ToJob>,
mut sender: mpsc::Sender<FromJob>,
_receiver: mpsc::Receiver<BitfieldSigningMessage>,
mut sender: mpsc::Sender<FromJobCommand>,
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
let metrics = metrics.clone();
async move {
@@ -330,7 +263,11 @@ impl JobTrait for BitfieldSigningJob {
metrics.on_bitfield_signed();
sender
.send(BitfieldDistributionMessage::DistributeBitfield(relay_parent, signed_bitfield).into())
.send(
AllMessages::from(
BitfieldDistributionMessage::DistributeBitfield(relay_parent, signed_bitfield),
).into(),
)
.await
.map_err(Into::into)
}
@@ -345,8 +282,7 @@ pub type BitfieldSigningSubsystem<Spawner, Context> = JobManager<Spawner, Contex
mod tests {
use super::*;
use futures::{pin_mut, executor::block_on};
use polkadot_primitives::v1::{OccupiedCore};
use FromJob::*;
use polkadot_primitives::v1::OccupiedCore;
fn occupied_core(para_id: u32) -> CoreState {
CoreState::Occupied(OccupiedCore {
@@ -373,12 +309,18 @@ mod tests {
loop {
futures::select! {
m = receiver.next() => match m.unwrap() {
RuntimeApi(RuntimeApiMessage::Request(rp, RuntimeApiRequest::AvailabilityCores(tx))) => {
FromJobCommand::SendMessage(
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(rp, RuntimeApiRequest::AvailabilityCores(tx)),
),
) => {
assert_eq!(relay_parent, rp);
tx.send(Ok(vec![CoreState::Free, occupied_core(1), occupied_core(2)])).unwrap();
},
RuntimeApi(
RuntimeApiMessage::Request(rp, RuntimeApiRequest::CandidatePendingAvailability(para_id, tx))
FromJobCommand::SendMessage(
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(rp, RuntimeApiRequest::CandidatePendingAvailability(para_id, tx)),
),
) => {
assert_eq!(relay_parent, rp);
@@ -388,7 +330,11 @@ mod tests {
tx.send(Ok(None)).unwrap();
}
},
AvailabilityStore(AvailabilityStoreMessage::QueryChunkAvailability(_, vidx, tx)) => {
FromJobCommand::SendMessage(
AllMessages::AvailabilityStore(
AvailabilityStoreMessage::QueryChunkAvailability(_, vidx, tx),
),
) => {
assert_eq!(validator_index, vidx);
tx.send(true).unwrap();
+47 -131
View File
@@ -30,86 +30,21 @@ use polkadot_node_subsystem::{
},
};
use polkadot_node_subsystem_util::{
self as util, delegated_subsystem, JobTrait, ToJobTrait, FromJobCommand,
metrics::{self, prometheus},
self as util, delegated_subsystem, JobTrait, FromJobCommand, metrics::{self, prometheus},
};
use polkadot_primitives::v1::{CandidateReceipt, CollatorId, Hash, Id as ParaId, PoV};
use std::{convert::TryFrom, pin::Pin};
use std::pin::Pin;
use thiserror::Error;
const LOG_TARGET: &'static str = "candidate_selection";
struct CandidateSelectionJob {
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJobCommand>,
receiver: mpsc::Receiver<CandidateSelectionMessage>,
metrics: Metrics,
seconded_candidate: Option<CollatorId>,
}
/// This enum defines the messages that the provisioner is prepared to receive.
#[derive(Debug)]
pub enum ToJob {
/// The provisioner message is the main input to the provisioner.
CandidateSelection(CandidateSelectionMessage),
/// This message indicates that the provisioner should shut itself down.
Stop,
}
impl ToJobTrait for ToJob {
const STOP: Self = Self::Stop;
fn relay_parent(&self) -> Option<Hash> {
match self {
Self::CandidateSelection(csm) => csm.relay_parent(),
Self::Stop => None,
}
}
}
impl TryFrom<AllMessages> for ToJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::CandidateSelection(csm) => Ok(Self::CandidateSelection(csm)),
_ => Err(()),
}
}
}
impl From<CandidateSelectionMessage> for ToJob {
fn from(csm: CandidateSelectionMessage) -> Self {
Self::CandidateSelection(csm)
}
}
#[derive(Debug)]
enum FromJob {
Backing(CandidateBackingMessage),
Collator(CollatorProtocolMessage),
}
impl From<FromJob> for FromJobCommand {
fn from(from_job: FromJob) -> FromJobCommand {
FromJobCommand::SendMessage(match from_job {
FromJob::Backing(msg) => AllMessages::CandidateBacking(msg),
FromJob::Collator(msg) => AllMessages::CollatorProtocol(msg),
})
}
}
impl TryFrom<AllMessages> for FromJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::CandidateBacking(msg) => Ok(FromJob::Backing(msg)),
AllMessages::CollatorProtocol(msg) => Ok(FromJob::Collator(msg)),
_ => Err(()),
}
}
}
#[derive(Debug, Error)]
enum Error {
#[error(transparent)]
@@ -123,40 +58,32 @@ enum Error {
}
impl JobTrait for CandidateSelectionJob {
type ToJob = ToJob;
type FromJob = FromJob;
type ToJob = CandidateSelectionMessage;
type Error = Error;
type RunArgs = ();
type Metrics = Metrics;
const NAME: &'static str = "CandidateSelectionJob";
/// Run a job for the parent block indicated
//
// this function is in charge of creating and executing the job's main loop
#[tracing::instrument(skip(_relay_parent, _run_args, metrics, receiver, sender), fields(subsystem = LOG_TARGET))]
fn run(
_relay_parent: Hash,
_run_args: Self::RunArgs,
metrics: Self::Metrics,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<CandidateSelectionMessage>,
sender: mpsc::Sender<FromJobCommand>,
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
Box::pin(async move {
let job = CandidateSelectionJob::new(metrics, sender, receiver);
// it isn't necessary to break run_loop into its own function,
// but it's convenient to separate the concerns in this way
job.run_loop().await
})
async move {
CandidateSelectionJob::new(metrics, sender, receiver).run_loop().await
}.boxed()
}
}
impl CandidateSelectionJob {
pub fn new(
metrics: Metrics,
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJobCommand>,
receiver: mpsc::Receiver<CandidateSelectionMessage>,
) -> Self {
Self {
sender,
@@ -166,28 +93,23 @@ impl CandidateSelectionJob {
}
}
async fn run_loop(mut self) -> Result<(), Error> {
self.run_loop_borrowed().await
}
/// this function exists for testing and should not generally be used; use `run_loop` instead.
async fn run_loop_borrowed(&mut self) -> Result<(), Error> {
while let Some(msg) = self.receiver.next().await {
match msg {
ToJob::CandidateSelection(CandidateSelectionMessage::Collation(
async fn run_loop(&mut self) -> Result<(), Error> {
loop {
match self.receiver.next().await {
Some(CandidateSelectionMessage::Collation(
relay_parent,
para_id,
collator_id,
)) => {
self.handle_collation(relay_parent, para_id, collator_id).await;
}
ToJob::CandidateSelection(CandidateSelectionMessage::Invalid(
Some(CandidateSelectionMessage::Invalid(
_,
candidate_receipt,
)) => {
self.handle_invalid(candidate_receipt).await;
}
ToJob::Stop => break,
None => break,
}
}
@@ -283,16 +205,16 @@ async fn get_collation(
relay_parent: Hash,
para_id: ParaId,
collator_id: CollatorId,
mut sender: mpsc::Sender<FromJob>,
mut sender: mpsc::Sender<FromJobCommand>,
) -> Result<(CandidateReceipt, PoV), Error> {
let (tx, rx) = oneshot::channel();
sender
.send(FromJob::Collator(CollatorProtocolMessage::FetchCollation(
.send(AllMessages::from(CollatorProtocolMessage::FetchCollation(
relay_parent,
collator_id,
para_id,
tx,
)))
)).into())
.await?;
rx.await.map_err(Into::into)
}
@@ -301,15 +223,15 @@ async fn second_candidate(
relay_parent: Hash,
candidate_receipt: CandidateReceipt,
pov: PoV,
sender: &mut mpsc::Sender<FromJob>,
sender: &mut mpsc::Sender<FromJobCommand>,
metrics: &Metrics,
) -> Result<(), Error> {
match sender
.send(FromJob::Backing(CandidateBackingMessage::Second(
.send(AllMessages::from(CandidateBackingMessage::Second(
relay_parent,
candidate_receipt,
pov,
)))
)).into())
.await
{
Err(err) => {
@@ -326,12 +248,12 @@ async fn second_candidate(
async fn forward_invalidity_note(
received_from: &CollatorId,
sender: &mut mpsc::Sender<FromJob>,
sender: &mut mpsc::Sender<FromJobCommand>,
) -> Result<(), Error> {
sender
.send(FromJob::Collator(CollatorProtocolMessage::ReportCollator(
.send(AllMessages::from(CollatorProtocolMessage::ReportCollator(
received_from.clone(),
)))
)).into())
.await
.map_err(Into::into)
}
@@ -420,7 +342,7 @@ impl metrics::Metrics for Metrics {
}
}
delegated_subsystem!(CandidateSelectionJob((), Metrics) <- ToJob as CandidateSelectionSubsystem);
delegated_subsystem!(CandidateSelectionJob((), Metrics) <- CandidateSelectionMessage as CandidateSelectionSubsystem);
#[cfg(test)]
mod tests {
@@ -436,7 +358,7 @@ mod tests {
postconditions: Postconditions,
) where
Preconditions: FnOnce(&mut CandidateSelectionJob),
TestBuilder: FnOnce(mpsc::Sender<ToJob>, mpsc::Receiver<FromJob>) -> Test,
TestBuilder: FnOnce(mpsc::Sender<CandidateSelectionMessage>, mpsc::Receiver<FromJobCommand>) -> Test,
Test: Future<Output = ()>,
Postconditions: FnOnce(CandidateSelectionJob, Result<(), Error>),
{
@@ -453,7 +375,7 @@ mod tests {
let (_, job_result) = futures::executor::block_on(future::join(
test(to_job_tx, from_job_rx),
job.run_loop_borrowed(),
job.run_loop(),
));
postconditions(job, job_result);
@@ -479,12 +401,10 @@ mod tests {
|_job| {},
|mut to_job, mut from_job| async move {
to_job
.send(ToJob::CandidateSelection(
CandidateSelectionMessage::Collation(
relay_parent,
para_id,
collator_id_clone.clone(),
),
.send(CandidateSelectionMessage::Collation(
relay_parent,
para_id,
collator_id_clone.clone(),
))
.await
.unwrap();
@@ -492,12 +412,12 @@ mod tests {
while let Some(msg) = from_job.next().await {
match msg {
FromJob::Collator(CollatorProtocolMessage::FetchCollation(
FromJobCommand::SendMessage(AllMessages::CollatorProtocol(CollatorProtocolMessage::FetchCollation(
got_relay_parent,
collator_id,
got_para_id,
return_sender,
)) => {
))) => {
assert_eq!(got_relay_parent, relay_parent);
assert_eq!(got_para_id, para_id);
assert_eq!(collator_id, collator_id_clone);
@@ -506,11 +426,11 @@ mod tests {
.send((candidate_receipt.clone(), pov.clone()))
.unwrap();
}
FromJob::Backing(CandidateBackingMessage::Second(
FromJobCommand::SendMessage(AllMessages::CandidateBacking(CandidateBackingMessage::Second(
got_relay_parent,
got_candidate_receipt,
got_pov,
)) => {
))) => {
assert_eq!(got_relay_parent, relay_parent);
assert_eq!(got_candidate_receipt, candidate_receipt);
assert_eq!(got_pov, pov);
@@ -546,12 +466,10 @@ mod tests {
|job| job.seconded_candidate = Some(prev_collator_id.clone()),
|mut to_job, mut from_job| async move {
to_job
.send(ToJob::CandidateSelection(
CandidateSelectionMessage::Collation(
relay_parent,
para_id,
collator_id_clone,
),
.send(CandidateSelectionMessage::Collation(
relay_parent,
para_id,
collator_id_clone,
))
.await
.unwrap();
@@ -559,11 +477,11 @@ mod tests {
while let Some(msg) = from_job.next().await {
match msg {
FromJob::Backing(CandidateBackingMessage::Second(
FromJobCommand::SendMessage(AllMessages::CandidateBacking(CandidateBackingMessage::Second(
_got_relay_parent,
_got_candidate_receipt,
_got_pov,
)) => {
))) => {
*was_seconded_clone.lock().await = true;
}
other => panic!("unexpected message from job: {:?}", other),
@@ -595,18 +513,16 @@ mod tests {
|job| job.seconded_candidate = Some(collator_id.clone()),
|mut to_job, mut from_job| async move {
to_job
.send(ToJob::CandidateSelection(
CandidateSelectionMessage::Invalid(relay_parent, candidate_receipt),
))
.send(CandidateSelectionMessage::Invalid(relay_parent, candidate_receipt))
.await
.unwrap();
std::mem::drop(to_job);
while let Some(msg) = from_job.next().await {
match msg {
FromJob::Collator(CollatorProtocolMessage::ReportCollator(
FromJobCommand::SendMessage(AllMessages::CollatorProtocol(CollatorProtocolMessage::ReportCollator(
got_collator_id,
)) => {
))) => {
assert_eq!(got_collator_id, collator_id_clone);
*sent_report_clone.lock().await = true;
+26 -95
View File
@@ -26,99 +26,31 @@ use futures::{
};
use polkadot_node_subsystem::{
errors::{ChainApiError, RuntimeApiError},
messages::{
AllMessages, ChainApiMessage, ProvisionableData, ProvisionerInherentData,
ProvisionerMessage, RuntimeApiMessage,
},
messages::{ChainApiMessage, ProvisionableData, ProvisionerInherentData, ProvisionerMessage, AllMessages},
};
use polkadot_node_subsystem_util::{
self as util,
delegated_subsystem, FromJobCommand,
request_availability_cores, request_persisted_validation_data, JobTrait, ToJobTrait,
metrics::{self, prometheus},
self as util, delegated_subsystem, FromJobCommand,
request_availability_cores, request_persisted_validation_data, JobTrait, metrics::{self, prometheus},
};
use polkadot_primitives::v1::{
BackedCandidate, BlockNumber, CoreState, Hash, OccupiedCoreAssumption,
SignedAvailabilityBitfield, ValidatorIndex,
};
use std::{convert::TryFrom, pin::Pin};
use std::collections::BTreeMap;
use std::{pin::Pin, collections::BTreeMap};
use thiserror::Error;
const LOG_TARGET: &str = "provisioner";
struct ProvisioningJob {
relay_parent: Hash,
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJobCommand>,
receiver: mpsc::Receiver<ProvisionerMessage>,
provisionable_data_channels: Vec<mpsc::Sender<ProvisionableData>>,
backed_candidates: Vec<BackedCandidate>,
signed_bitfields: Vec<SignedAvailabilityBitfield>,
metrics: Metrics,
}
/// This enum defines the messages that the provisioner is prepared to receive.
pub enum ToJob {
/// The provisioner message is the main input to the provisioner.
Provisioner(ProvisionerMessage),
/// This message indicates that the provisioner should shut itself down.
Stop,
}
impl ToJobTrait for ToJob {
const STOP: Self = Self::Stop;
fn relay_parent(&self) -> Option<Hash> {
match self {
Self::Provisioner(pm) => pm.relay_parent(),
Self::Stop => None,
}
}
}
impl TryFrom<AllMessages> for ToJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::Provisioner(pm) => Ok(Self::Provisioner(pm)),
_ => Err(()),
}
}
}
impl From<ProvisionerMessage> for ToJob {
fn from(pm: ProvisionerMessage) -> Self {
Self::Provisioner(pm)
}
}
enum FromJob {
ChainApi(ChainApiMessage),
Runtime(RuntimeApiMessage),
}
impl From<FromJob> for FromJobCommand {
fn from(from_job: FromJob) -> FromJobCommand {
FromJobCommand::SendMessage(match from_job {
FromJob::ChainApi(cam) => AllMessages::ChainApi(cam),
FromJob::Runtime(ram) => AllMessages::RuntimeApi(ram),
})
}
}
impl TryFrom<AllMessages> for FromJob {
type Error = ();
fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
match msg {
AllMessages::ChainApi(chain) => Ok(FromJob::ChainApi(chain)),
AllMessages::RuntimeApi(runtime) => Ok(FromJob::Runtime(runtime)),
_ => Err(()),
}
}
}
#[derive(Debug, Error)]
enum Error {
#[error(transparent)]
@@ -141,8 +73,7 @@ enum Error {
}
impl JobTrait for ProvisioningJob {
type ToJob = ToJob;
type FromJob = FromJob;
type ToJob = ProvisionerMessage;
type Error = Error;
type RunArgs = ();
type Metrics = Metrics;
@@ -157,8 +88,8 @@ impl JobTrait for ProvisioningJob {
relay_parent: Hash,
_run_args: Self::RunArgs,
metrics: Self::Metrics,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<ProvisionerMessage>,
sender: mpsc::Sender<FromJobCommand>,
) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
async move {
let job = ProvisioningJob::new(relay_parent, metrics, sender, receiver);
@@ -175,8 +106,8 @@ impl ProvisioningJob {
pub fn new(
relay_parent: Hash,
metrics: Metrics,
sender: mpsc::Sender<FromJob>,
receiver: mpsc::Receiver<ToJob>,
sender: mpsc::Sender<FromJobCommand>,
receiver: mpsc::Receiver<ProvisionerMessage>,
) -> Self {
Self {
relay_parent,
@@ -190,13 +121,13 @@ impl ProvisioningJob {
}
async fn run_loop(mut self) -> Result<(), Error> {
while let Some(msg) = self.receiver.next().await {
use ProvisionerMessage::{
ProvisionableData, RequestBlockAuthorshipData, RequestInherentData,
};
use ProvisionerMessage::{
ProvisionableData, RequestBlockAuthorshipData, RequestInherentData,
};
match msg {
ToJob::Provisioner(RequestInherentData(_, return_sender)) => {
loop {
match self.receiver.next().await {
Some(RequestInherentData(_, return_sender)) => {
let _timer = self.metrics.time_request_inherent_data();
if let Err(err) = send_inherent_data(
@@ -214,10 +145,10 @@ impl ProvisioningJob {
self.metrics.on_inherent_data_request(Ok(()));
}
}
ToJob::Provisioner(RequestBlockAuthorshipData(_, sender)) => {
Some(RequestBlockAuthorshipData(_, sender)) => {
self.provisionable_data_channels.push(sender)
}
ToJob::Provisioner(ProvisionableData(_, data)) => {
Some(ProvisionableData(_, data)) => {
let _timer = self.metrics.time_provisionable_data();
let mut bad_indices = Vec::new();
@@ -252,7 +183,7 @@ impl ProvisioningJob {
.map(|(_, item)| item)
.collect();
}
ToJob::Stop => break,
None => break,
}
}
@@ -298,7 +229,7 @@ async fn send_inherent_data(
bitfields: &[SignedAvailabilityBitfield],
candidates: &[BackedCandidate],
return_sender: oneshot::Sender<ProvisionerInherentData>,
mut from_job: mpsc::Sender<FromJob>,
mut from_job: mpsc::Sender<FromJobCommand>,
) -> Result<(), Error> {
let availability_cores = request_availability_cores(relay_parent, &mut from_job)
.await?
@@ -368,7 +299,7 @@ async fn select_candidates(
bitfields: &[SignedAvailabilityBitfield],
candidates: &[BackedCandidate],
relay_parent: Hash,
sender: &mut mpsc::Sender<FromJob>,
sender: &mut mpsc::Sender<FromJobCommand>,
) -> Result<Vec<BackedCandidate>, Error> {
let block_number = get_block_number_under_construction(relay_parent, sender).await?;
@@ -432,14 +363,14 @@ async fn select_candidates(
#[tracing::instrument(level = "trace", skip(sender), fields(subsystem = LOG_TARGET))]
async fn get_block_number_under_construction(
relay_parent: Hash,
sender: &mut mpsc::Sender<FromJob>,
sender: &mut mpsc::Sender<FromJobCommand>,
) -> Result<BlockNumber, Error> {
let (tx, rx) = oneshot::channel();
sender
.send(FromJob::ChainApi(ChainApiMessage::BlockNumber(
.send(AllMessages::from(ChainApiMessage::BlockNumber(
relay_parent,
tx,
)))
)).into())
.await
.map_err(|e| Error::ChainApiMessageSend(e))?;
match rx.await? {
@@ -558,7 +489,7 @@ impl metrics::Metrics for Metrics {
}
delegated_subsystem!(ProvisioningJob((), Metrics) <- ToJob as ProvisioningSubsystem);
delegated_subsystem!(ProvisioningJob((), Metrics) <- ProvisionerMessage as ProvisioningSubsystem);
#[cfg(test)]
mod tests;
+14 -14
View File
@@ -193,13 +193,13 @@ mod select_candidates {
use futures_timer::Delay;
use super::super::*;
use super::{build_occupied_core, default_bitvec, occupied_core, scheduled_core};
use polkadot_node_subsystem::messages::RuntimeApiRequest::{
AvailabilityCores, PersistedValidationData as PersistedValidationDataReq,
use polkadot_node_subsystem::messages::{
AllMessages, RuntimeApiMessage,
RuntimeApiRequest::{AvailabilityCores, PersistedValidationData as PersistedValidationDataReq},
};
use polkadot_primitives::v1::{
BlockNumber, CandidateDescriptor, CommittedCandidateReceipt, PersistedValidationData,
};
use FromJob::{ChainApi, Runtime};
const BLOCK_UNDER_PRODUCTION: BlockNumber = 128;
@@ -207,9 +207,9 @@ mod select_candidates {
overseer_factory: OverseerFactory,
test_factory: TestFactory,
) where
OverseerFactory: FnOnce(mpsc::Receiver<FromJob>) -> Overseer,
OverseerFactory: FnOnce(mpsc::Receiver<FromJobCommand>) -> Overseer,
Overseer: Future<Output = ()>,
TestFactory: FnOnce(mpsc::Sender<FromJob>) -> Test,
TestFactory: FnOnce(mpsc::Sender<FromJobCommand>) -> Test,
Test: Future<Output = ()>,
{
let (tx, rx) = mpsc::channel(64);
@@ -297,20 +297,20 @@ mod select_candidates {
]
}
async fn mock_overseer(mut receiver: mpsc::Receiver<FromJob>) {
async fn mock_overseer(mut receiver: mpsc::Receiver<FromJobCommand>) {
use ChainApiMessage::BlockNumber;
use RuntimeApiMessage::Request;
while let Some(from_job) = receiver.next().await {
match from_job {
ChainApi(BlockNumber(_relay_parent, tx)) => {
FromJobCommand::SendMessage(AllMessages::ChainApi(BlockNumber(_relay_parent, tx))) => {
tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap()
}
Runtime(Request(
FromJobCommand::SendMessage(AllMessages::RuntimeApi(Request(
_parent_hash,
PersistedValidationDataReq(_para_id, _assumption, tx),
)) => tx.send(Ok(Some(Default::default()))).unwrap(),
Runtime(Request(_parent_hash, AvailabilityCores(tx))) => {
))) => tx.send(Ok(Some(Default::default()))).unwrap(),
FromJobCommand::SendMessage(AllMessages::RuntimeApi(Request(_parent_hash, AvailabilityCores(tx)))) => {
tx.send(Ok(mock_availability_cores())).unwrap()
}
// non-exhaustive matches are fine for testing
@@ -321,14 +321,14 @@ mod select_candidates {
#[test]
fn handles_overseer_failure() {
let overseer = |rx: mpsc::Receiver<FromJob>| async move {
let overseer = |rx: mpsc::Receiver<FromJobCommand>| async move {
// drop the receiver so it closes and the sender can't send, then just sleep long enough that
// this is almost certainly not the first of the two futures to complete
std::mem::drop(rx);
Delay::new(std::time::Duration::from_secs(1)).await;
};
let test = |mut tx: mpsc::Sender<FromJob>| async move {
let test = |mut tx: mpsc::Sender<FromJobCommand>| async move {
// wait so that the overseer can drop the rx before we attempt to send
Delay::new(std::time::Duration::from_millis(50)).await;
let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await;
@@ -341,7 +341,7 @@ mod select_candidates {
#[test]
fn can_succeed() {
test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJob>| async move {
test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJobCommand>| async move {
let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await;
println!("{:?}", result);
assert!(result.is_ok());
@@ -403,7 +403,7 @@ mod select_candidates {
.map(|&idx| candidates[idx].clone())
.collect();
test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJob>| async move {
test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJobCommand>| async move {
let result =
select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx)
.await;