diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 5c50933839..5e23615fdc 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -5885,6 +5885,7 @@ dependencies = [ "parking_lot 0.10.2", "sc-block-builder", "sc-client-api", + "sc-proposer-metrics", "sc-telemetry", "sc-transaction-pool", "sp-api", @@ -5894,6 +5895,7 @@ dependencies = [ "sp-inherents", "sp-runtime", "sp-transaction-pool", + "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tokio-executor 0.2.0-alpha.6", ] @@ -6601,6 +6603,14 @@ dependencies = [ "wasm-timer", ] +[[package]] +name = "sc-proposer-metrics" +version = "0.8.0-dev" +dependencies = [ + "log", + "substrate-prometheus-endpoint", +] + [[package]] name = "sc-rpc" version = "2.0.0-dev" diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index d9ee8709d1..8fbe1cf0d8 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -45,6 +45,7 @@ members = [ "client/network-gossip", "client/offchain", "client/peerset", + "client/proposer-metrics", "client/rpc-servers", "client/rpc", "client/rpc-api", diff --git a/substrate/bin/node-template/node/src/service.rs b/substrate/bin/node-template/node/src/service.rs index d02e9ea95e..8e57a04137 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/substrate/bin/node-template/node/src/service.rs @@ -106,8 +106,11 @@ pub fn new_full(config: Configuration) -> Result. + //! A consensus proposer for "basic" chains which use the primitive inherent-data. // FIXME #1021 move this into sp-consensus @@ -38,21 +39,31 @@ use futures::{executor, future, future::Either}; use sp_blockchain::{HeaderBackend, ApplyExtrinsicFailed::Validity, Error::ApplyExtrinsicFailed}; use std::marker::PhantomData; +use prometheus_endpoint::Registry as PrometheusRegistry; +use sc_proposer_metrics::MetricsLink as PrometheusMetrics; + /// Proposer factory. pub struct ProposerFactory { /// The client instance. client: Arc, /// The transaction pool. transaction_pool: Arc, + /// Prometheus Link, + metrics: PrometheusMetrics, /// phantom member to pin the `Backend` type. _phantom: PhantomData, } impl ProposerFactory { - pub fn new(client: Arc, transaction_pool: Arc) -> Self { + pub fn new( + client: Arc, + transaction_pool: Arc, + prometheus: Option<&PrometheusRegistry>, + ) -> Self { ProposerFactory { client, transaction_pool, + metrics: PrometheusMetrics::new(prometheus), _phantom: PhantomData, } } @@ -87,6 +98,7 @@ impl ProposerFactory parent_number: *parent_header.number(), transaction_pool: self.transaction_pool.clone(), now, + metrics: self.metrics.clone(), _phantom: PhantomData, }), }; @@ -131,6 +143,7 @@ struct ProposerInner { parent_number: <::Header as HeaderT>::Number, transaction_pool: Arc, now: Box time::Instant + Send + Sync>, + metrics: PrometheusMetrics, _phantom: PhantomData, } @@ -219,6 +232,7 @@ impl ProposerInner } // proceed with transactions + let block_timer = self.metrics.report(|metrics| metrics.block_constructed.start_timer()); let mut is_first = true; let mut skipped = 0; let mut unqueue_invalid = Vec::new(); @@ -289,6 +303,9 @@ impl ProposerInner let (block, storage_changes, proof) = block_builder.build()?.into_inner(); + drop(block_timer); + self.metrics.report(|metrics| metrics.number_of_transactions.set(block.extrinsics().len() as u64)); + info!("🎁 Prepared block for proposing at {} [hash: {:?}; parent_hash: {}; extrinsics ({}): [{}]]", block.header().number(), ::Hash::from(block.header().hash()), @@ -379,7 +396,7 @@ mod tests { )) ); - let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone()); + let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone(), None); let cell = Mutex::new((false, time::Instant::now())); let mut proposer = proposer_factory.init_with_now( @@ -420,7 +437,7 @@ mod tests { ).0 ); - let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone()); + let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone(), None); let cell = Mutex::new((false, time::Instant::now())); let mut proposer = proposer_factory.init_with_now( @@ -470,7 +487,7 @@ mod tests { )) ); - let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone()); + let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone(), None); let mut proposer = proposer_factory.init_with_now( &client.header(&block_id).unwrap().unwrap(), @@ -536,7 +553,7 @@ mod tests { ]) ).unwrap(); - let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone()); + let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone(), None); let mut propose_block = | client: &TestClient, number, diff --git a/substrate/client/basic-authorship/src/lib.rs b/substrate/client/basic-authorship/src/lib.rs index 7f88844d90..7c77dde6b0 100644 --- a/substrate/client/basic-authorship/src/lib.rs +++ b/substrate/client/basic-authorship/src/lib.rs @@ -29,7 +29,7 @@ //! # let client = Arc::new(substrate_test_runtime_client::new()); //! # let txpool = Arc::new(BasicPool::new(Default::default(), Arc::new(FullChainApi::new(client.clone())), None).0); //! // The first step is to create a `ProposerFactory`. -//! let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone()); +//! let mut proposer_factory = ProposerFactory::new(client.clone(), txpool.clone(), None); //! //! // From this factory, we create a `Proposer`. //! let proposer = proposer_factory.init( diff --git a/substrate/client/consensus/manual-seal/src/lib.rs b/substrate/client/consensus/manual-seal/src/lib.rs index 754203f7ba..de9711b2a8 100644 --- a/substrate/client/consensus/manual-seal/src/lib.rs +++ b/substrate/client/consensus/manual-seal/src/lib.rs @@ -225,7 +225,8 @@ mod tests { let pool = Arc::new(BasicPool::new(Options::default(), api(), None).0); let env = ProposerFactory::new( client.clone(), - pool.clone() + pool.clone(), + None, ); // this test checks that blocks are created as soon as transactions are imported into the pool. let (sender, receiver) = futures::channel::oneshot::channel(); @@ -289,7 +290,8 @@ mod tests { let pool = Arc::new(BasicPool::new(Options::default(), api(), None).0); let env = ProposerFactory::new( client.clone(), - pool.clone() + pool.clone(), + None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. let (mut sink, stream) = futures::channel::mpsc::channel(1024); @@ -358,6 +360,7 @@ mod tests { let env = ProposerFactory::new( client.clone(), pool.clone(), + None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. let (mut sink, stream) = futures::channel::mpsc::channel(1024); diff --git a/substrate/client/proposer-metrics/Cargo.toml b/substrate/client/proposer-metrics/Cargo.toml new file mode 100644 index 0000000000..9d510027ec --- /dev/null +++ b/substrate/client/proposer-metrics/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "sc-proposer-metrics" +version = "0.8.0-dev" +authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "Basic metrics for block production." + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +log = "0.4.8" +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-dev"} diff --git a/substrate/client/proposer-metrics/src/lib.rs b/substrate/client/proposer-metrics/src/lib.rs new file mode 100644 index 0000000000..5cb749f4a2 --- /dev/null +++ b/substrate/client/proposer-metrics/src/lib.rs @@ -0,0 +1,67 @@ +// Copyright 2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate 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. + +// Substrate 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 Substrate. If not, see . + +//! Prometheus basic proposer metrics. + +use prometheus_endpoint::{register, PrometheusError, Registry, Histogram, HistogramOpts, Gauge, U64}; + +/// Optional shareable link to basic authorship metrics. +#[derive(Clone, Default)] +pub struct MetricsLink(Option); + +impl MetricsLink { + pub fn new(registry: Option<&Registry>) -> Self { + Self( + registry.and_then(|registry| + Metrics::register(registry) + .map_err(|err| log::warn!("Failed to register proposer prometheus metrics: {}", err)) + .ok() + ) + ) + } + + pub fn report(&self, do_this: impl FnOnce(&Metrics) -> O) -> Option { + Some(do_this(self.0.as_ref()?)) + } +} + +/// Authorship metrics. +#[derive(Clone)] +pub struct Metrics { + pub block_constructed: Histogram, + pub number_of_transactions: Gauge, +} + +impl Metrics { + pub fn register(registry: &Registry) -> Result { + Ok(Self { + block_constructed: register( + Histogram::with_opts(HistogramOpts::new( + "proposer_block_constructed", + "Histogram of time taken to construct new block", + ))?, + registry, + )?, + number_of_transactions: register( + Gauge::new( + "proposer_number_of_transactions", + "Number of transactions included in block", + )?, + registry, + )?, + }) + } +} diff --git a/substrate/primitives/consensus/common/src/lib.rs b/substrate/primitives/consensus/common/src/lib.rs index 52b034ffdd..fc56b22516 100644 --- a/substrate/primitives/consensus/common/src/lib.rs +++ b/substrate/primitives/consensus/common/src/lib.rs @@ -72,7 +72,9 @@ pub enum BlockStatus { Unknown, } -/// Environment producer for a Consensus instance. Creates proposer instance and communication streams. +/// Environment for a Consensus instance. +/// +/// Creates proposer instance. pub trait Environment { /// The proposer type this creates. type Proposer: Proposer + Send + 'static;