mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 21:41:12 +00:00
General Message Queue Pallet (#12485)
* The message queue * Make fully generic * Refactor * Docs * Refactor * Use iter not slice * Per-origin queues * Multi-queue processing * Introduce MaxReady * Remove MaxReady in favour of ready ring * Cleanups * ReadyRing and tests * Stale page reaping * from_components -> from_parts Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Move WeightCounter to sp_weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add MockedWeightInfo Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Deploy to kitchensink Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Use WeightCounter Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Small fixes and logging Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add service_page Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Typo Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Move service_page below service_queue Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add service_message Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Use correct weight function Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Overweight execution * Refactor * Missing file * Fix WeightCounter usage in scheduler Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix peek_index Take into account that decoding from a mutable slice modifies it. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add tests and bench service_page_item Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add debug_info Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add no-progress check to service_queues Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add more benches Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Bound from_message and try_append_message Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add PageReaped event Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Rename BookStateOf and BookStateFor Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update tests and remove logging Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Remove redundant per-message origins; add footprint() and sweep_queue() * Move testing stuff to mock.rs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add integration test Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix no-progress check Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix debug_info Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fixup merge and tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix footprint tracking * Introduce * Formatting * OverweightEnqueued event, auto-servicing config item * Update tests and benchmarks Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Provide change handler * Add missing BookStateFor::insert and call QueueChangeHandler Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Docs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update benchmarks and weights Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * More tests... Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Use weight metering functions Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * weightInfo::process_message_payload is gone Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add defensive_saturating_accrue Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Rename WeightCounter to WeightMeter Ctr+Shift+H should do the trick. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Test on_initialize Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add module docs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Remove origin from MaxMessageLen The message origin is not encoded into the heap and does therefore not influence the max message length anymore. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add BoundedVec::as_slice Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Test Page::{from_message, try_append_message} Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fixup docs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Docs * Do nothing in sweep_queue if the queue does not exist ... otherwise it inserts default values into the storage. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Test ring (un)knitting Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Upgrade stress-test Change the test to not assume that all queued messages will be processed in the next block but split it over multiple. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * More tests... Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Beauty fixes Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Rename BoundedVec::as_slice to as_bounded_slice Conflicts with deref().as_slice() otherwise. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix imports Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Remove ReadyRing struct Was used for testing only. Instead use 'fn assert_ring' which also check the service head and backlinks. Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Beauty fixes Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix stale page watermark Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Cleanup Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix test feature and clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * QueueChanged handler is called correctly Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update benches Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Abstract testing functions Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * More tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Cleanup Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Clippy Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * fmt Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Simplify tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Make stuff compile Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Extend overweight execution benchmark Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Remove TODOs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Test service queue with faulty MessageProcessor Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * fmt Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Update pallet ui tests to 1.65 Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * More docs Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Review doc fixes Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com> Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Add weight_limit to extrinsic weight of execute_overweight * Correctly return unused weight * Return actual weight consumed in do_execute_overweight * Review fixes Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Set version 7.0.0-dev Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Make it compile Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Switch message_size to u64 Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Switch message_count to u64 Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix benchmarks Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Make CI green Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Docs * Update tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * ".git/.scripts/bench-bot.sh" pallet dev pallet_message_queue * Dont mention README.md in the Cargo.toml Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Remove reference to readme Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: parity-processbot <> Co-authored-by: Robert Klotzner <eskimor@users.noreply.github.com> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
Generated
+32
-8
@@ -3117,6 +3117,7 @@ dependencies = [
|
||||
"pallet-indices",
|
||||
"pallet-lottery",
|
||||
"pallet-membership",
|
||||
"pallet-message-queue",
|
||||
"pallet-mmr",
|
||||
"pallet-multisig",
|
||||
"pallet-nis",
|
||||
@@ -5322,6 +5323,28 @@ dependencies = [
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-message-queue"
|
||||
version = "7.0.0-dev"
|
||||
dependencies = [
|
||||
"frame-benchmarking",
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"log",
|
||||
"parity-scale-codec",
|
||||
"rand 0.8.5",
|
||||
"rand_distr",
|
||||
"scale-info",
|
||||
"serde",
|
||||
"sp-arithmetic",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-tracing",
|
||||
"sp-weights",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pallet-mmr"
|
||||
version = "4.0.0-dev"
|
||||
@@ -5709,6 +5732,7 @@ dependencies = [
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"sp-weights",
|
||||
"substrate-test-utils",
|
||||
]
|
||||
|
||||
@@ -8397,9 +8421,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9"
|
||||
checksum = "333af15b02563b8182cd863f925bd31ef8fa86a0e095d30c091956057d436153"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"cfg-if",
|
||||
@@ -8411,9 +8435,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "scale-info-derive"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4260c630e8a8a33429d1688eff2f163f24c65a4e1b1578ef6b565061336e4b6f"
|
||||
checksum = "53f56acbd0743d29ffa08f911ab5397def774ad01bab3786804cf6ee057fb5e1"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
@@ -8570,9 +8594,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@@ -8589,9 +8613,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
version = "1.0.145"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -121,6 +121,7 @@ members = [
|
||||
"frame/offences/benchmarking",
|
||||
"frame/preimage",
|
||||
"frame/proxy",
|
||||
"frame/message-queue",
|
||||
"frame/nomination-pools",
|
||||
"frame/nomination-pools/fuzzer",
|
||||
"frame/nomination-pools/benchmarking",
|
||||
|
||||
@@ -75,6 +75,7 @@ pallet-indices = { version = "4.0.0-dev", default-features = false, path = "../.
|
||||
pallet-identity = { version = "4.0.0-dev", default-features = false, path = "../../../frame/identity" }
|
||||
pallet-lottery = { version = "4.0.0-dev", default-features = false, path = "../../../frame/lottery" }
|
||||
pallet-membership = { version = "4.0.0-dev", default-features = false, path = "../../../frame/membership" }
|
||||
pallet-message-queue = { version = "7.0.0-dev", default-features = false, path = "../../../frame/message-queue" }
|
||||
pallet-mmr = { version = "4.0.0-dev", default-features = false, path = "../../../frame/merkle-mountain-range" }
|
||||
pallet-multisig = { version = "4.0.0-dev", default-features = false, path = "../../../frame/multisig" }
|
||||
pallet-nomination-pools = { version = "1.0.0", default-features = false, path = "../../../frame/nomination-pools"}
|
||||
@@ -150,6 +151,7 @@ std = [
|
||||
"sp-inherents/std",
|
||||
"pallet-lottery/std",
|
||||
"pallet-membership/std",
|
||||
"pallet-message-queue/std",
|
||||
"pallet-mmr/std",
|
||||
"pallet-multisig/std",
|
||||
"pallet-nomination-pools/std",
|
||||
@@ -229,6 +231,7 @@ runtime-benchmarks = [
|
||||
"pallet-indices/runtime-benchmarks",
|
||||
"pallet-lottery/runtime-benchmarks",
|
||||
"pallet-membership/runtime-benchmarks",
|
||||
"pallet-message-queue/runtime-benchmarks",
|
||||
"pallet-mmr/runtime-benchmarks",
|
||||
"pallet-multisig/runtime-benchmarks",
|
||||
"pallet-nomination-pools-benchmarking/runtime-benchmarks",
|
||||
@@ -282,6 +285,7 @@ try-runtime = [
|
||||
"pallet-identity/try-runtime",
|
||||
"pallet-lottery/try-runtime",
|
||||
"pallet-membership/try-runtime",
|
||||
"pallet-message-queue/try-runtime",
|
||||
"pallet-mmr/try-runtime",
|
||||
"pallet-multisig/try-runtime",
|
||||
"pallet-nomination-pools/try-runtime",
|
||||
|
||||
@@ -1135,6 +1135,25 @@ impl pallet_bounties::Config for Runtime {
|
||||
type ChildBountyManager = ChildBounties;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// Allocate at most 20% of each block for message processing.
|
||||
///
|
||||
/// Is set to 20% since the scheduler can already consume a maximum of 80%.
|
||||
pub MessageQueueServiceWeight: Option<Weight> = Some(Perbill::from_percent(20) * RuntimeBlockWeights::get().max_block);
|
||||
}
|
||||
|
||||
impl pallet_message_queue::Config for Runtime {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = ();
|
||||
/// NOTE: Always set this to `NoopMessageProcessor` for benchmarking.
|
||||
type MessageProcessor = pallet_message_queue::mock_helpers::NoopMessageProcessor;
|
||||
type Size = u32;
|
||||
type QueueChangeHandler = ();
|
||||
type HeapSize = ConstU32<{ 64 * 1024 }>;
|
||||
type MaxStale = ConstU32<128>;
|
||||
type ServiceWeight = MessageQueueServiceWeight;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ChildBountyValueMinimum: Balance = 1 * DOLLARS;
|
||||
}
|
||||
@@ -1699,6 +1718,7 @@ construct_runtime!(
|
||||
RankedPolls: pallet_referenda::<Instance2>,
|
||||
RankedCollective: pallet_ranked_collective,
|
||||
FastUnstake: pallet_fast_unstake,
|
||||
MessageQueue: pallet_message_queue,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1793,6 +1813,7 @@ mod benches {
|
||||
[pallet_indices, Indices]
|
||||
[pallet_lottery, Lottery]
|
||||
[pallet_membership, TechnicalMembership]
|
||||
[pallet_message_queue, MessageQueue]
|
||||
[pallet_mmr, Mmr]
|
||||
[pallet_multisig, Multisig]
|
||||
[pallet_nomination_pools, NominationPoolsBench::<Runtime>]
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
[package]
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
name = "pallet-message-queue"
|
||||
version = "7.0.0-dev"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.io"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME pallet to queue and process messages"
|
||||
|
||||
[dependencies]
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.1.2", default-features = false, features = ["derive"] }
|
||||
serde = { version = "1.0.137", optional = true, features = ["derive"] }
|
||||
log = { version = "0.4.17", default-features = false }
|
||||
|
||||
sp-core = { version = "7.0.0", default-features = false, path = "../../primitives/core" }
|
||||
sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" }
|
||||
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" }
|
||||
sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" }
|
||||
|
||||
frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" }
|
||||
frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" }
|
||||
frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-tracing = { version = "6.0.0", path = "../../primitives/tracing" }
|
||||
rand = "0.8.5"
|
||||
rand_distr = "0.4.3"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"codec/std",
|
||||
"scale-info/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
"sp-arithmetic/std",
|
||||
"sp-weights/std",
|
||||
"frame-benchmarking?/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright 2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Benchmarking for the message queue pallet.
|
||||
|
||||
#![cfg(feature = "runtime-benchmarks")]
|
||||
#![allow(unused_assignments)] // Needed for `ready_ring_knit`.
|
||||
|
||||
use super::{mock_helpers::*, Pallet as MessageQueue, *};
|
||||
|
||||
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
||||
use frame_support::traits::Get;
|
||||
use frame_system::RawOrigin;
|
||||
use sp_std::prelude::*;
|
||||
|
||||
benchmarks! {
|
||||
where_clause {
|
||||
where
|
||||
// NOTE: We need to generate multiple origins, therefore Origin is `From<u32>`. The
|
||||
// `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be
|
||||
// removed if really necessary.
|
||||
<<T as Config>::MessageProcessor as ProcessMessage>::Origin: From<u32> + PartialEq,
|
||||
<T as Config>::Size: From<u32>,
|
||||
}
|
||||
|
||||
// Worst case path of `ready_ring_knit`.
|
||||
ready_ring_knit {
|
||||
let mid: MessageOriginOf::<T> = 1.into();
|
||||
build_ring::<T>(&[0.into(), mid.clone(), 2.into()]);
|
||||
unknit::<T>(&mid);
|
||||
assert_ring::<T>(&[0.into(), 2.into()]);
|
||||
let mut neighbours = None;
|
||||
}: {
|
||||
neighbours = MessageQueue::<T>::ready_ring_knit(&mid).ok();
|
||||
} verify {
|
||||
// The neighbours needs to be modified manually.
|
||||
BookStateFor::<T>::mutate(&mid, |b| { b.ready_neighbours = neighbours });
|
||||
assert_ring::<T>(&[0.into(), 2.into(), mid]);
|
||||
}
|
||||
|
||||
// Worst case path of `ready_ring_unknit`.
|
||||
ready_ring_unknit {
|
||||
build_ring::<T>(&[0.into(), 1.into(), 2.into()]);
|
||||
assert_ring::<T>(&[0.into(), 1.into(), 2.into()]);
|
||||
let o: MessageOriginOf::<T> = 0.into();
|
||||
let neighbours = BookStateFor::<T>::get(&o).ready_neighbours.unwrap();
|
||||
}: {
|
||||
MessageQueue::<T>::ready_ring_unknit(&o, neighbours);
|
||||
} verify {
|
||||
assert_ring::<T>(&[1.into(), 2.into()]);
|
||||
}
|
||||
|
||||
// `service_queues` without any queue processing.
|
||||
service_queue_base {
|
||||
}: {
|
||||
MessageQueue::<T>::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX)
|
||||
}
|
||||
|
||||
// `service_page` without any message processing but with page completion.
|
||||
service_page_base_completion {
|
||||
let origin: MessageOriginOf<T> = 0.into();
|
||||
let page = PageOf::<T>::default();
|
||||
Pages::<T>::insert(&origin, 0, &page);
|
||||
let mut book_state = single_page_book::<T>();
|
||||
let mut meter = WeightMeter::max_limit();
|
||||
let limit = Weight::MAX;
|
||||
}: {
|
||||
MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit)
|
||||
}
|
||||
|
||||
// `service_page` without any message processing and without page completion.
|
||||
service_page_base_no_completion {
|
||||
let origin: MessageOriginOf<T> = 0.into();
|
||||
let mut page = PageOf::<T>::default();
|
||||
// Mock the storage such that `is_complete` returns `false` but `peek_first` returns `None`.
|
||||
page.first = 1.into();
|
||||
page.remaining = 1.into();
|
||||
Pages::<T>::insert(&origin, 0, &page);
|
||||
let mut book_state = single_page_book::<T>();
|
||||
let mut meter = WeightMeter::max_limit();
|
||||
let limit = Weight::MAX;
|
||||
}: {
|
||||
MessageQueue::<T>::service_page(&origin, &mut book_state, &mut meter, limit)
|
||||
}
|
||||
|
||||
// Processing a single message from a page.
|
||||
service_page_item {
|
||||
let msg = vec![1u8; MaxMessageLenOf::<T>::get() as usize];
|
||||
let mut page = page::<T>(&msg.clone());
|
||||
let mut book = book_for::<T>(&page);
|
||||
assert!(page.peek_first().is_some(), "There is one message");
|
||||
let mut weight = WeightMeter::max_limit();
|
||||
}: {
|
||||
let status = MessageQueue::<T>::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX);
|
||||
assert_eq!(status, ItemExecutionStatus::Executed(true));
|
||||
} verify {
|
||||
// Check that it was processed.
|
||||
assert_last_event::<T>(Event::Processed {
|
||||
hash: T::Hashing::hash(&msg), origin: 0.into(),
|
||||
weight_used: 1.into_weight(), success: true
|
||||
}.into());
|
||||
let (_, processed, _) = page.peek_index(0).unwrap();
|
||||
assert!(processed);
|
||||
assert_eq!(book.message_count, 0);
|
||||
}
|
||||
|
||||
// Worst case for calling `bump_service_head`.
|
||||
bump_service_head {
|
||||
setup_bump_service_head::<T>(0.into(), 10.into());
|
||||
let mut weight = WeightMeter::max_limit();
|
||||
}: {
|
||||
MessageQueue::<T>::bump_service_head(&mut weight);
|
||||
} verify {
|
||||
assert_eq!(ServiceHead::<T>::get().unwrap(), 10u32.into());
|
||||
assert_eq!(weight.consumed, T::WeightInfo::bump_service_head());
|
||||
}
|
||||
|
||||
reap_page {
|
||||
// Mock the storage to get a *cullable* but not *reapable* page.
|
||||
let origin: MessageOriginOf<T> = 0.into();
|
||||
let mut book = single_page_book::<T>();
|
||||
let (page, msgs) = full_page::<T>();
|
||||
|
||||
for p in 0 .. T::MaxStale::get() * T::MaxStale::get() {
|
||||
if p == 0 {
|
||||
Pages::<T>::insert(&origin, p, &page);
|
||||
}
|
||||
book.end += 1;
|
||||
book.count += 1;
|
||||
book.message_count += msgs as u64;
|
||||
book.size += page.remaining_size.into() as u64;
|
||||
}
|
||||
book.begin = book.end - T::MaxStale::get();
|
||||
BookStateFor::<T>::insert(&origin, &book);
|
||||
assert!(Pages::<T>::contains_key(&origin, 0));
|
||||
|
||||
}: _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0)
|
||||
verify {
|
||||
assert_last_event::<T>(Event::PageReaped{ origin: 0.into(), index: 0 }.into());
|
||||
assert!(!Pages::<T>::contains_key(&origin, 0));
|
||||
}
|
||||
|
||||
// Worst case for `execute_overweight` where the page is removed as completed.
|
||||
//
|
||||
// The worst case occurs when executing the last message in a page of which all are skipped since it is using `peek_index` which has linear complexities.
|
||||
execute_overweight_page_removed {
|
||||
let origin: MessageOriginOf<T> = 0.into();
|
||||
let (mut page, msgs) = full_page::<T>();
|
||||
// Skip all messages.
|
||||
for _ in 1..msgs {
|
||||
page.skip_first(true);
|
||||
}
|
||||
page.skip_first(false);
|
||||
let book = book_for::<T>(&page);
|
||||
Pages::<T>::insert(&origin, 0, &page);
|
||||
BookStateFor::<T>::insert(&origin, &book);
|
||||
}: {
|
||||
MessageQueue::<T>::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap()
|
||||
}
|
||||
verify {
|
||||
assert_last_event::<T>(Event::Processed {
|
||||
hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(),
|
||||
weight_used: Weight::from_parts(1, 1), success: true
|
||||
}.into());
|
||||
assert!(!Pages::<T>::contains_key(&origin, 0), "Page must be removed");
|
||||
}
|
||||
|
||||
// Worst case for `execute_overweight` where the page is updated.
|
||||
execute_overweight_page_updated {
|
||||
let origin: MessageOriginOf<T> = 0.into();
|
||||
let (mut page, msgs) = full_page::<T>();
|
||||
// Skip all messages.
|
||||
for _ in 0..msgs {
|
||||
page.skip_first(false);
|
||||
}
|
||||
let book = book_for::<T>(&page);
|
||||
Pages::<T>::insert(&origin, 0, &page);
|
||||
BookStateFor::<T>::insert(&origin, &book);
|
||||
}: {
|
||||
MessageQueue::<T>::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap()
|
||||
}
|
||||
verify {
|
||||
assert_last_event::<T>(Event::Processed {
|
||||
hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(),
|
||||
weight_used: Weight::from_parts(1, 1), success: true
|
||||
}.into());
|
||||
assert!(Pages::<T>::contains_key(&origin, 0), "Page must be updated");
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(MessageQueue, crate::mock::new_test_ext::<crate::integration_test::Test>(), crate::integration_test::Test);
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Stress tests pallet-message-queue. Defines its own runtime config to use larger constants for
|
||||
//! `HeapSize` and `MaxStale`.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::{
|
||||
mock::{
|
||||
new_test_ext, CountingMessageProcessor, IntoWeight, MockedWeightInfo, NumMessagesProcessed,
|
||||
},
|
||||
*,
|
||||
};
|
||||
|
||||
use crate as pallet_message_queue;
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{ConstU32, ConstU64},
|
||||
};
|
||||
use rand::{rngs::StdRng, Rng, SeedableRng};
|
||||
use rand_distr::Pareto;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
};
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic,
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
|
||||
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub BlockWeights: frame_system::limits::BlockWeights =
|
||||
frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024));
|
||||
}
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = ConstU64<250>;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = ConstU32<16>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const HeapSize: u32 = 32 * 1024;
|
||||
pub const MaxStale: u32 = 32;
|
||||
pub static ServiceWeight: Option<Weight> = Some(Weight::from_parts(100, 100));
|
||||
}
|
||||
|
||||
impl Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = MockedWeightInfo;
|
||||
type MessageProcessor = CountingMessageProcessor;
|
||||
type Size = u32;
|
||||
type QueueChangeHandler = ();
|
||||
type HeapSize = HeapSize;
|
||||
type MaxStale = MaxStale;
|
||||
type ServiceWeight = ServiceWeight;
|
||||
}
|
||||
|
||||
/// Simulates heavy usage by enqueueing and processing large amounts of messages.
|
||||
///
|
||||
/// Best to run with `-r`, `RUST_LOG=info` and `RUSTFLAGS='-Cdebug-assertions=y'`.
|
||||
///
|
||||
/// # Example output
|
||||
///
|
||||
/// ```pre
|
||||
/// Enqueued 1189 messages across 176 queues. Payload 46.97 KiB
|
||||
/// Processing 772 of 1189 messages
|
||||
/// Enqueued 9270 messages across 1559 queues. Payload 131.85 KiB
|
||||
/// Processing 6262 of 9687 messages
|
||||
/// Enqueued 5025 messages across 1225 queues. Payload 100.23 KiB
|
||||
/// Processing 1739 of 8450 messages
|
||||
/// Enqueued 42061 messages across 6357 queues. Payload 536.29 KiB
|
||||
/// Processing 11675 of 48772 messages
|
||||
/// Enqueued 20253 messages across 2420 queues. Payload 288.34 KiB
|
||||
/// Processing 28711 of 57350 messages
|
||||
/// Processing all remaining 28639 messages
|
||||
/// ```
|
||||
#[test]
|
||||
#[ignore] // Only run in the CI.
|
||||
fn stress_test_enqueue_and_service() {
|
||||
let blocks = 20;
|
||||
let max_queues = 10_000;
|
||||
let max_messages_per_queue = 10_000;
|
||||
let max_msg_len = MaxMessageLenOf::<Test>::get();
|
||||
let mut rng = StdRng::seed_from_u64(42);
|
||||
|
||||
new_test_ext::<Test>().execute_with(|| {
|
||||
let mut msgs_remaining = 0;
|
||||
for _ in 0..blocks {
|
||||
// Start by enqueuing a large number of messages.
|
||||
let (enqueued, _) =
|
||||
enqueue_messages(max_queues, max_messages_per_queue, max_msg_len, &mut rng);
|
||||
msgs_remaining += enqueued;
|
||||
|
||||
// Pick a fraction of all messages currently in queue and process them.
|
||||
let processed = rng.gen_range(1..=msgs_remaining);
|
||||
log::info!("Processing {} of all messages {}", processed, msgs_remaining);
|
||||
process_messages(processed); // This also advances the block.
|
||||
msgs_remaining -= processed;
|
||||
}
|
||||
log::info!("Processing all remaining {} messages", msgs_remaining);
|
||||
process_messages(msgs_remaining);
|
||||
post_conditions();
|
||||
});
|
||||
}
|
||||
|
||||
/// Enqueue a random number of random messages into a random number of queues.
|
||||
fn enqueue_messages(
|
||||
max_queues: u32,
|
||||
max_per_queue: u32,
|
||||
max_msg_len: u32,
|
||||
rng: &mut StdRng,
|
||||
) -> (u32, usize) {
|
||||
let num_queues = rng.gen_range(1..max_queues);
|
||||
let mut num_messages = 0;
|
||||
let mut total_msg_len = 0;
|
||||
for origin in 0..num_queues {
|
||||
let num_messages_per_queue =
|
||||
(rng.sample(Pareto::new(1.0, 1.1).unwrap()) as u32).min(max_per_queue);
|
||||
|
||||
for m in 0..num_messages_per_queue {
|
||||
let mut message = format!("{}:{}", &origin, &m).into_bytes();
|
||||
let msg_len = (rng.sample(Pareto::new(1.0, 1.0).unwrap()) as u32)
|
||||
.clamp(message.len() as u32, max_msg_len);
|
||||
message.resize(msg_len as usize, 0);
|
||||
MessageQueue::enqueue_message(
|
||||
BoundedSlice::defensive_truncate_from(&message),
|
||||
origin.into(),
|
||||
);
|
||||
total_msg_len += msg_len;
|
||||
}
|
||||
num_messages += num_messages_per_queue;
|
||||
}
|
||||
log::info!(
|
||||
"Enqueued {} messages across {} queues. Payload {:.2} KiB",
|
||||
num_messages,
|
||||
num_queues,
|
||||
total_msg_len as f64 / 1024.0
|
||||
);
|
||||
(num_messages, total_msg_len as usize)
|
||||
}
|
||||
|
||||
/// Process the number of messages.
|
||||
fn process_messages(num_msgs: u32) {
|
||||
let weight = (num_msgs as u64).into_weight();
|
||||
ServiceWeight::set(Some(weight));
|
||||
let consumed = next_block();
|
||||
|
||||
assert_eq!(consumed, weight, "\n{}", MessageQueue::debug_info());
|
||||
assert_eq!(NumMessagesProcessed::take(), num_msgs as usize);
|
||||
}
|
||||
|
||||
/// Returns the weight consumed by `MessageQueue::on_initialize()`.
|
||||
fn next_block() -> Weight {
|
||||
MessageQueue::on_finalize(System::block_number());
|
||||
System::on_finalize(System::block_number());
|
||||
System::set_block_number(System::block_number() + 1);
|
||||
System::on_initialize(System::block_number());
|
||||
MessageQueue::on_initialize(System::block_number())
|
||||
}
|
||||
|
||||
/// Assert that the pallet is in the expected post state.
|
||||
fn post_conditions() {
|
||||
// All queues are empty.
|
||||
for (_, book) in BookStateFor::<Test>::iter() {
|
||||
assert!(book.end >= book.begin);
|
||||
assert_eq!(book.count, 0);
|
||||
assert_eq!(book.size, 0);
|
||||
assert_eq!(book.message_count, 0);
|
||||
assert!(book.ready_neighbours.is_none());
|
||||
}
|
||||
// No pages remain.
|
||||
assert_eq!(Pages::<Test>::iter().count(), 0);
|
||||
// Service head is gone.
|
||||
assert!(ServiceHead::<Test>::get().is_none());
|
||||
// This still works fine.
|
||||
assert_eq!(MessageQueue::service_queues(Weight::MAX), Weight::zero(), "Nothing left");
|
||||
next_block();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,312 @@
|
||||
// Copyright 2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
pub use super::mock_helpers::*;
|
||||
use super::*;
|
||||
|
||||
use crate as pallet_message_queue;
|
||||
use frame_support::{
|
||||
parameter_types,
|
||||
traits::{ConstU32, ConstU64},
|
||||
};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
testing::Header,
|
||||
traits::{BlakeTwo256, IdentityLookup},
|
||||
};
|
||||
use sp_std::collections::btree_map::BTreeMap;
|
||||
|
||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test where
|
||||
Block = Block,
|
||||
NodeBlock = Block,
|
||||
UncheckedExtrinsic = UncheckedExtrinsic,
|
||||
{
|
||||
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
|
||||
MessageQueue: pallet_message_queue::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
parameter_types! {
|
||||
pub BlockWeights: frame_system::limits::BlockWeights =
|
||||
frame_system::limits::BlockWeights::simple_max(frame_support::weights::Weight::from_ref_time(1024));
|
||||
}
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = ConstU64<250>;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = ();
|
||||
type OnSetCode = ();
|
||||
type MaxConsumers = ConstU32<16>;
|
||||
}
|
||||
parameter_types! {
|
||||
pub const HeapSize: u32 = 24;
|
||||
pub const MaxStale: u32 = 2;
|
||||
pub const ServiceWeight: Option<Weight> = Some(Weight::from_parts(10, 10));
|
||||
}
|
||||
impl Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type WeightInfo = MockedWeightInfo;
|
||||
type MessageProcessor = RecordingMessageProcessor;
|
||||
type Size = u32;
|
||||
type QueueChangeHandler = RecordingQueueChangeHandler;
|
||||
type HeapSize = HeapSize;
|
||||
type MaxStale = MaxStale;
|
||||
type ServiceWeight = ServiceWeight;
|
||||
}
|
||||
|
||||
/// Mocked `WeightInfo` impl with allows to set the weight per call.
|
||||
pub struct MockedWeightInfo;
|
||||
|
||||
parameter_types! {
|
||||
/// Storage for `MockedWeightInfo`, do not use directly.
|
||||
pub static WeightForCall: BTreeMap<String, Weight> = Default::default();
|
||||
}
|
||||
|
||||
/// Set the return value for a function from the `WeightInfo` trait.
|
||||
impl MockedWeightInfo {
|
||||
/// Set the weight of a specific weight function.
|
||||
pub fn set_weight<T: Config>(call_name: &str, weight: Weight) {
|
||||
let mut calls = WeightForCall::get();
|
||||
calls.insert(call_name.into(), weight);
|
||||
WeightForCall::set(calls);
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::weights::WeightInfo for MockedWeightInfo {
|
||||
fn reap_page() -> Weight {
|
||||
WeightForCall::get().get("reap_page").copied().unwrap_or_default()
|
||||
}
|
||||
fn execute_overweight_page_updated() -> Weight {
|
||||
WeightForCall::get()
|
||||
.get("execute_overweight_page_updated")
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn execute_overweight_page_removed() -> Weight {
|
||||
WeightForCall::get()
|
||||
.get("execute_overweight_page_removed")
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn service_page_base_completion() -> Weight {
|
||||
WeightForCall::get()
|
||||
.get("service_page_base_completion")
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn service_page_base_no_completion() -> Weight {
|
||||
WeightForCall::get()
|
||||
.get("service_page_base_no_completion")
|
||||
.copied()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
fn service_queue_base() -> Weight {
|
||||
WeightForCall::get().get("service_queue_base").copied().unwrap_or_default()
|
||||
}
|
||||
fn bump_service_head() -> Weight {
|
||||
WeightForCall::get().get("bump_service_head").copied().unwrap_or_default()
|
||||
}
|
||||
fn service_page_item() -> Weight {
|
||||
WeightForCall::get().get("service_page_item").copied().unwrap_or_default()
|
||||
}
|
||||
fn ready_ring_knit() -> Weight {
|
||||
WeightForCall::get().get("ready_ring_knit").copied().unwrap_or_default()
|
||||
}
|
||||
fn ready_ring_unknit() -> Weight {
|
||||
WeightForCall::get().get("ready_ring_unknit").copied().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static MessagesProcessed: Vec<(Vec<u8>, MessageOrigin)> = vec![];
|
||||
}
|
||||
|
||||
/// A message processor which records all processed messages into [`MessagesProcessed`].
|
||||
pub struct RecordingMessageProcessor;
|
||||
impl ProcessMessage for RecordingMessageProcessor {
|
||||
/// The transport from where a message originates.
|
||||
type Origin = MessageOrigin;
|
||||
|
||||
/// Process the given message, using no more than `weight_limit` in weight to do so.
|
||||
///
|
||||
/// Consumes exactly `n` weight of all components if it starts `weight=n` and `1` otherwise.
|
||||
/// Errors if given the `weight_limit` is insufficient to process the message or if the message
|
||||
/// is `badformat`, `corrupt` or `unsupported` with the respective error.
|
||||
fn process_message(
|
||||
message: &[u8],
|
||||
origin: Self::Origin,
|
||||
weight_limit: Weight,
|
||||
) -> Result<(bool, Weight), ProcessMessageError> {
|
||||
processing_message(message)?;
|
||||
|
||||
let weight = if message.starts_with(&b"weight="[..]) {
|
||||
let mut w: u64 = 0;
|
||||
for &c in &message[7..] {
|
||||
if (b'0'..=b'9').contains(&c) {
|
||||
w = w * 10 + (c - b'0') as u64;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
w
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let weight = Weight::from_parts(weight, weight);
|
||||
|
||||
if weight.all_lte(weight_limit) {
|
||||
let mut m = MessagesProcessed::get();
|
||||
m.push((message.to_vec(), origin));
|
||||
MessagesProcessed::set(m);
|
||||
Ok((true, weight))
|
||||
} else {
|
||||
Err(ProcessMessageError::Overweight(weight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processed a mocked message. Messages that end with `badformat`, `corrupt` or `unsupported` will
|
||||
/// fail with the respective error.
|
||||
fn processing_message(msg: &[u8]) -> Result<(), ProcessMessageError> {
|
||||
let msg = String::from_utf8_lossy(msg);
|
||||
if msg.ends_with("badformat") {
|
||||
Err(ProcessMessageError::BadFormat)
|
||||
} else if msg.ends_with("corrupt") {
|
||||
Err(ProcessMessageError::Corrupt)
|
||||
} else if msg.ends_with("unsupported") {
|
||||
Err(ProcessMessageError::Unsupported)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub static NumMessagesProcessed: usize = 0;
|
||||
pub static NumMessagesErrored: usize = 0;
|
||||
}
|
||||
|
||||
/// Similar to [`RecordingMessageProcessor`] but only counts the number of messages processed and
|
||||
/// does always consume one weight per message.
|
||||
///
|
||||
/// The [`RecordingMessageProcessor`] is a bit too slow for the integration tests.
|
||||
pub struct CountingMessageProcessor;
|
||||
impl ProcessMessage for CountingMessageProcessor {
|
||||
type Origin = MessageOrigin;
|
||||
|
||||
fn process_message(
|
||||
message: &[u8],
|
||||
_origin: Self::Origin,
|
||||
weight_limit: Weight,
|
||||
) -> Result<(bool, Weight), ProcessMessageError> {
|
||||
if let Err(e) = processing_message(message) {
|
||||
NumMessagesErrored::set(NumMessagesErrored::get() + 1);
|
||||
return Err(e)
|
||||
}
|
||||
let weight = Weight::from_parts(1, 1);
|
||||
|
||||
if weight.all_lte(weight_limit) {
|
||||
NumMessagesProcessed::set(NumMessagesProcessed::get() + 1);
|
||||
Ok((true, weight))
|
||||
} else {
|
||||
Err(ProcessMessageError::Overweight(weight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// Storage for `RecordingQueueChangeHandler`, do not use directly.
|
||||
pub static QueueChanges: Vec<(MessageOrigin, u64, u64)> = vec![];
|
||||
}
|
||||
|
||||
/// Records all queue changes into [`QueueChanges`].
|
||||
pub struct RecordingQueueChangeHandler;
|
||||
impl OnQueueChanged<MessageOrigin> for RecordingQueueChangeHandler {
|
||||
fn on_queue_changed(id: MessageOrigin, items_count: u64, items_size: u64) {
|
||||
QueueChanges::mutate(|cs| cs.push((id, items_count, items_size)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new test externalities.
|
||||
///
|
||||
/// Is generic since it is used by the unit test, integration tests and benchmarks.
|
||||
pub fn new_test_ext<T: Config>() -> sp_io::TestExternalities
|
||||
where
|
||||
<T as frame_system::Config>::BlockNumber: From<u32>,
|
||||
{
|
||||
sp_tracing::try_init_simple();
|
||||
WeightForCall::take();
|
||||
QueueChanges::take();
|
||||
NumMessagesErrored::take();
|
||||
let t = frame_system::GenesisConfig::default().build_storage::<T>().unwrap();
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
ext.execute_with(|| frame_system::Pallet::<T>::set_block_number(1.into()));
|
||||
ext
|
||||
}
|
||||
|
||||
/// Set the weight of a specific weight function.
|
||||
pub fn set_weight(name: &str, w: Weight) {
|
||||
MockedWeightInfo::set_weight::<Test>(name, w);
|
||||
}
|
||||
|
||||
/// Assert that exactly these pages are present. Assumes `Here` origin.
|
||||
pub fn assert_pages(indices: &[u32]) {
|
||||
assert_eq!(Pages::<Test>::iter().count(), indices.len());
|
||||
for i in indices {
|
||||
assert!(Pages::<Test>::contains_key(MessageOrigin::Here, i));
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
|
||||
pub fn build_triple_ring() {
|
||||
use MessageOrigin::*;
|
||||
build_ring::<Test>(&[Here, There, Everywhere(0)])
|
||||
}
|
||||
|
||||
/// Shim to get rid of the annoying `::<Test>` everywhere.
|
||||
pub fn assert_ring(queues: &[MessageOrigin]) {
|
||||
super::mock_helpers::assert_ring::<Test>(queues);
|
||||
}
|
||||
|
||||
pub fn knit(queue: &MessageOrigin) {
|
||||
super::mock_helpers::knit::<Test>(queue);
|
||||
}
|
||||
|
||||
pub fn unknit(queue: &MessageOrigin) {
|
||||
super::mock_helpers::unknit::<Test>(queue);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright 2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Polkadot.
|
||||
|
||||
// Polkadot 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.
|
||||
|
||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Std setup helpers for testing and benchmarking.
|
||||
//!
|
||||
//! Cannot be put into mock.rs since benchmarks require no-std and mock.rs is std.
|
||||
|
||||
use crate::*;
|
||||
use frame_support::traits::Defensive;
|
||||
|
||||
/// Converts `Self` into a `Weight` by using `Self` for all components.
|
||||
pub trait IntoWeight {
|
||||
fn into_weight(self) -> Weight;
|
||||
}
|
||||
|
||||
impl IntoWeight for u64 {
|
||||
fn into_weight(self) -> Weight {
|
||||
Weight::from_parts(self, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mocked message origin for testing.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo, Debug)]
|
||||
pub enum MessageOrigin {
|
||||
Here,
|
||||
There,
|
||||
Everywhere(u32),
|
||||
}
|
||||
|
||||
impl From<u32> for MessageOrigin {
|
||||
fn from(i: u32) -> Self {
|
||||
Self::Everywhere(i)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes any message and consumes (1, 1) weight per message.
|
||||
pub struct NoopMessageProcessor;
|
||||
impl ProcessMessage for NoopMessageProcessor {
|
||||
type Origin = MessageOrigin;
|
||||
|
||||
fn process_message(
|
||||
_message: &[u8],
|
||||
_origin: Self::Origin,
|
||||
weight_limit: Weight,
|
||||
) -> Result<(bool, Weight), ProcessMessageError> {
|
||||
let weight = Weight::from_parts(1, 1);
|
||||
|
||||
if weight.all_lte(weight_limit) {
|
||||
Ok((true, weight))
|
||||
} else {
|
||||
Err(ProcessMessageError::Overweight(weight))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a message from the given data.
|
||||
pub fn msg<N: Get<u32>>(x: &'static str) -> BoundedSlice<u8, N> {
|
||||
BoundedSlice::defensive_truncate_from(x.as_bytes())
|
||||
}
|
||||
|
||||
pub fn vmsg(x: &'static str) -> Vec<u8> {
|
||||
x.as_bytes().to_vec()
|
||||
}
|
||||
|
||||
/// Create a page from a single message.
|
||||
pub fn page<T: Config>(msg: &[u8]) -> PageOf<T> {
|
||||
PageOf::<T>::from_message::<T>(msg.try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn single_page_book<T: Config>() -> BookStateOf<T> {
|
||||
BookState { begin: 0, end: 1, count: 1, ready_neighbours: None, message_count: 0, size: 0 }
|
||||
}
|
||||
|
||||
pub fn empty_book<T: Config>() -> BookStateOf<T> {
|
||||
BookState { begin: 0, end: 1, count: 1, ready_neighbours: None, message_count: 0, size: 0 }
|
||||
}
|
||||
|
||||
/// Returns a full page of messages with their index as payload and the number of messages.
|
||||
pub fn full_page<T: Config>() -> (PageOf<T>, usize) {
|
||||
let mut msgs = 0;
|
||||
let mut page = PageOf::<T>::default();
|
||||
for i in 0..u32::MAX {
|
||||
let r = i.using_encoded(|d| page.try_append_message::<T>(d.try_into().unwrap()));
|
||||
if r.is_err() {
|
||||
break
|
||||
} else {
|
||||
msgs += 1;
|
||||
}
|
||||
}
|
||||
assert!(msgs > 0, "page must hold at least one message");
|
||||
(page, msgs)
|
||||
}
|
||||
|
||||
/// Returns a page filled with empty messages and the number of messages.
|
||||
pub fn book_for<T: Config>(page: &PageOf<T>) -> BookStateOf<T> {
|
||||
BookState {
|
||||
count: 1,
|
||||
begin: 0,
|
||||
end: 1,
|
||||
ready_neighbours: None,
|
||||
message_count: page.remaining.into() as u64,
|
||||
size: page.remaining_size.into() as u64,
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert the last event that was emitted.
|
||||
#[cfg(any(feature = "std", feature = "runtime-benchmarks", test))]
|
||||
pub fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
|
||||
assert!(
|
||||
!frame_system::Pallet::<T>::block_number().is_zero(),
|
||||
"The genesis block has n o events"
|
||||
);
|
||||
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
|
||||
}
|
||||
|
||||
/// Provide a setup for `bump_service_head`.
|
||||
pub fn setup_bump_service_head<T: Config>(
|
||||
current: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
|
||||
next: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
|
||||
) {
|
||||
let mut book = single_page_book::<T>();
|
||||
book.ready_neighbours = Some(Neighbours::<MessageOriginOf<T>> { prev: next.clone(), next });
|
||||
ServiceHead::<T>::put(¤t);
|
||||
BookStateFor::<T>::insert(¤t, &book);
|
||||
}
|
||||
|
||||
/// Knit a queue into the ready-ring and write it back to storage.
|
||||
pub fn knit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
|
||||
let mut b = BookStateFor::<T>::get(o);
|
||||
b.ready_neighbours = crate::Pallet::<T>::ready_ring_knit(o).ok().defensive();
|
||||
BookStateFor::<T>::insert(o, b);
|
||||
}
|
||||
|
||||
/// Unknit a queue into the ready-ring and write it back to storage.
|
||||
pub fn unknit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>::Origin) {
|
||||
let mut b = BookStateFor::<T>::get(o);
|
||||
crate::Pallet::<T>::ready_ring_unknit(o, b.ready_neighbours.unwrap());
|
||||
b.ready_neighbours = None;
|
||||
BookStateFor::<T>::insert(o, b);
|
||||
}
|
||||
|
||||
/// Build a ring with three queues: `Here`, `There` and `Everywhere(0)`.
|
||||
pub fn build_ring<T: Config>(
|
||||
queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
|
||||
) {
|
||||
for queue in queues {
|
||||
BookStateFor::<T>::insert(queue, empty_book::<T>());
|
||||
}
|
||||
for queue in queues {
|
||||
knit::<T>(queue);
|
||||
}
|
||||
assert_ring::<T>(queues);
|
||||
}
|
||||
|
||||
/// Check that the Ready Ring consists of `queues` in that exact order.
|
||||
///
|
||||
/// Also check that all backlinks are valid and that the first element is the service head.
|
||||
pub fn assert_ring<T: Config>(
|
||||
queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
|
||||
) {
|
||||
for (i, origin) in queues.iter().enumerate() {
|
||||
let book = BookStateFor::<T>::get(origin);
|
||||
assert_eq!(
|
||||
book.ready_neighbours,
|
||||
Some(Neighbours {
|
||||
prev: queues[(i + queues.len() - 1) % queues.len()].clone(),
|
||||
next: queues[(i + 1) % queues.len()].clone(),
|
||||
})
|
||||
);
|
||||
}
|
||||
assert_eq!(ServiceHead::<T>::get(), queues.first().cloned());
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,216 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Autogenerated weights for pallet_message_queue
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2022-12-08, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// /home/benchbot/cargo_target_dir/production/substrate
|
||||
// benchmark
|
||||
// pallet
|
||||
// --steps=50
|
||||
// --repeat=20
|
||||
// --extrinsic=*
|
||||
// --execution=wasm
|
||||
// --wasm-execution=compiled
|
||||
// --heap-pages=4096
|
||||
// --json-file=/var/lib/gitlab-runner/builds/zyw4fam_/0/parity/mirrors/substrate/.git/.artifacts/bench.json
|
||||
// --pallet=pallet_message_queue
|
||||
// --chain=dev
|
||||
// --header=./HEADER-APACHE2
|
||||
// --output=./frame/message-queue/src/weights.rs
|
||||
// --template=./.maintain/frame-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for pallet_message_queue.
|
||||
pub trait WeightInfo {
|
||||
fn ready_ring_knit() -> Weight;
|
||||
fn ready_ring_unknit() -> Weight;
|
||||
fn service_queue_base() -> Weight;
|
||||
fn service_page_base_completion() -> Weight;
|
||||
fn service_page_base_no_completion() -> Weight;
|
||||
fn service_page_item() -> Weight;
|
||||
fn bump_service_head() -> Weight;
|
||||
fn reap_page() -> Weight;
|
||||
fn execute_overweight_page_removed() -> Weight;
|
||||
fn execute_overweight_page_updated() -> Weight;
|
||||
}
|
||||
|
||||
/// Weights for pallet_message_queue using the Substrate node and recommended hardware.
|
||||
pub struct SubstrateWeight<T>(PhantomData<T>);
|
||||
impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
fn ready_ring_knit() -> Weight {
|
||||
// Minimum execution time: 12_330 nanoseconds.
|
||||
Weight::from_ref_time(12_711_000)
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
fn ready_ring_unknit() -> Weight {
|
||||
// Minimum execution time: 12_322 nanoseconds.
|
||||
Weight::from_ref_time(12_560_000)
|
||||
.saturating_add(T::DbWeight::get().reads(3))
|
||||
.saturating_add(T::DbWeight::get().writes(3))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
fn service_queue_base() -> Weight {
|
||||
// Minimum execution time: 4_652 nanoseconds.
|
||||
Weight::from_ref_time(4_848_000)
|
||||
.saturating_add(T::DbWeight::get().reads(1))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn service_page_base_completion() -> Weight {
|
||||
// Minimum execution time: 7_115 nanoseconds.
|
||||
Weight::from_ref_time(7_407_000)
|
||||
.saturating_add(T::DbWeight::get().reads(1))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn service_page_base_no_completion() -> Weight {
|
||||
// Minimum execution time: 6_974 nanoseconds.
|
||||
Weight::from_ref_time(7_200_000)
|
||||
.saturating_add(T::DbWeight::get().reads(1))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
fn service_page_item() -> Weight {
|
||||
// Minimum execution time: 79_657 nanoseconds.
|
||||
Weight::from_ref_time(80_050_000)
|
||||
}
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:0)
|
||||
fn bump_service_head() -> Weight {
|
||||
// Minimum execution time: 7_598 nanoseconds.
|
||||
Weight::from_ref_time(8_118_000)
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn reap_page() -> Weight {
|
||||
// Minimum execution time: 60_562 nanoseconds.
|
||||
Weight::from_ref_time(61_430_000)
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn execute_overweight_page_removed() -> Weight {
|
||||
// Minimum execution time: 74_582 nanoseconds.
|
||||
Weight::from_ref_time(75_445_000)
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn execute_overweight_page_updated() -> Weight {
|
||||
// Minimum execution time: 87_526 nanoseconds.
|
||||
Weight::from_ref_time(88_055_000)
|
||||
.saturating_add(T::DbWeight::get().reads(2))
|
||||
.saturating_add(T::DbWeight::get().writes(2))
|
||||
}
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:0)
|
||||
// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
fn ready_ring_knit() -> Weight {
|
||||
// Minimum execution time: 12_330 nanoseconds.
|
||||
Weight::from_ref_time(12_711_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(3))
|
||||
.saturating_add(RocksDbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:2 w:2)
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
fn ready_ring_unknit() -> Weight {
|
||||
// Minimum execution time: 12_322 nanoseconds.
|
||||
Weight::from_ref_time(12_560_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(3))
|
||||
.saturating_add(RocksDbWeight::get().writes(3))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
fn service_queue_base() -> Weight {
|
||||
// Minimum execution time: 4_652 nanoseconds.
|
||||
Weight::from_ref_time(4_848_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(1))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn service_page_base_completion() -> Weight {
|
||||
// Minimum execution time: 7_115 nanoseconds.
|
||||
Weight::from_ref_time(7_407_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(1))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn service_page_base_no_completion() -> Weight {
|
||||
// Minimum execution time: 6_974 nanoseconds.
|
||||
Weight::from_ref_time(7_200_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(1))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
fn service_page_item() -> Weight {
|
||||
// Minimum execution time: 79_657 nanoseconds.
|
||||
Weight::from_ref_time(80_050_000)
|
||||
}
|
||||
// Storage: MessageQueue ServiceHead (r:1 w:1)
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:0)
|
||||
fn bump_service_head() -> Weight {
|
||||
// Minimum execution time: 7_598 nanoseconds.
|
||||
Weight::from_ref_time(8_118_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn reap_page() -> Weight {
|
||||
// Minimum execution time: 60_562 nanoseconds.
|
||||
Weight::from_ref_time(61_430_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn execute_overweight_page_removed() -> Weight {
|
||||
// Minimum execution time: 74_582 nanoseconds.
|
||||
Weight::from_ref_time(75_445_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(2))
|
||||
}
|
||||
// Storage: MessageQueue BookStateFor (r:1 w:1)
|
||||
// Storage: MessageQueue Pages (r:1 w:1)
|
||||
fn execute_overweight_page_updated() -> Weight {
|
||||
// Minimum execution time: 87_526 nanoseconds.
|
||||
Weight::from_ref_time(88_055_000)
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(2))
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys
|
||||
sp-io = { version = "7.0.0", default-features = false, path = "../../primitives/io" }
|
||||
sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" }
|
||||
sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" }
|
||||
|
||||
[dev-dependencies]
|
||||
pallet-preimage = { version = "4.0.0-dev", path = "../preimage" }
|
||||
@@ -42,5 +43,6 @@ std = [
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
"sp-weights/std",
|
||||
]
|
||||
try-runtime = ["frame-support/try-runtime"]
|
||||
|
||||
@@ -73,7 +73,6 @@ use frame_support::{
|
||||
weights::{Weight, WeightMeter},
|
||||
};
|
||||
use frame_system::{self as system};
|
||||
pub use pallet::*;
|
||||
use scale_info::TypeInfo;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use sp_runtime::{
|
||||
@@ -81,6 +80,8 @@ use sp_runtime::{
|
||||
BoundedVec, RuntimeDebug,
|
||||
};
|
||||
use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*};
|
||||
|
||||
pub use pallet::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
/// Just a simple index for naming period tasks.
|
||||
|
||||
@@ -112,6 +112,12 @@ pub use voting::{
|
||||
mod preimages;
|
||||
pub use preimages::{Bounded, BoundedInline, FetchResult, Hash, QueryPreimage, StorePreimage};
|
||||
|
||||
mod messages;
|
||||
pub use messages::{
|
||||
EnqueueMessage, ExecuteOverweightError, Footprint, ProcessMessage, ProcessMessageError,
|
||||
ServiceQueues,
|
||||
};
|
||||
|
||||
#[cfg(feature = "try-runtime")]
|
||||
mod try_runtime;
|
||||
#[cfg(feature = "try-runtime")]
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Traits for managing message queuing and handling.
|
||||
|
||||
use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
|
||||
use scale_info::TypeInfo;
|
||||
use sp_core::{ConstU32, Get, TypedGet};
|
||||
use sp_runtime::{traits::Convert, BoundedSlice, RuntimeDebug};
|
||||
use sp_std::{fmt::Debug, marker::PhantomData, prelude::*};
|
||||
use sp_weights::Weight;
|
||||
|
||||
/// Errors that can happen when attempting to process a message with
|
||||
/// [`ProcessMessage::process_message()`].
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, TypeInfo, RuntimeDebug)]
|
||||
pub enum ProcessMessageError {
|
||||
/// The message data format is unknown (e.g. unrecognised header)
|
||||
BadFormat,
|
||||
/// The message data is bad (e.g. decoding returns an error).
|
||||
Corrupt,
|
||||
/// The message format is unsupported (e.g. old XCM version).
|
||||
Unsupported,
|
||||
/// Message processing was not attempted because it was not certain that the weight limit
|
||||
/// would be respected. The parameter gives the maximum weight which the message could take
|
||||
/// to process.
|
||||
Overweight(Weight),
|
||||
}
|
||||
|
||||
/// Can process messages from a specific origin.
|
||||
pub trait ProcessMessage {
|
||||
/// The transport from where a message originates.
|
||||
type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug;
|
||||
|
||||
/// Process the given message, using no more than `weight_limit` in weight to do so.
|
||||
fn process_message(
|
||||
message: &[u8],
|
||||
origin: Self::Origin,
|
||||
weight_limit: Weight,
|
||||
) -> Result<(bool, Weight), ProcessMessageError>;
|
||||
}
|
||||
|
||||
/// Errors that can happen when attempting to execute an overweight message with
|
||||
/// [`ServiceQueues::execute_overweight()`].
|
||||
#[derive(Eq, PartialEq, RuntimeDebug)]
|
||||
pub enum ExecuteOverweightError {
|
||||
/// The referenced message was not found.
|
||||
NotFound,
|
||||
/// The available weight was insufficient to execute the message.
|
||||
InsufficientWeight,
|
||||
}
|
||||
|
||||
/// Can service queues and execute overweight messages.
|
||||
pub trait ServiceQueues {
|
||||
/// Addresses a specific overweight message.
|
||||
type OverweightMessageAddress;
|
||||
|
||||
/// Service all message queues in some fair manner.
|
||||
///
|
||||
/// - `weight_limit`: The maximum amount of dynamic weight that this call can use.
|
||||
///
|
||||
/// Returns the dynamic weight used by this call; is never greater than `weight_limit`.
|
||||
fn service_queues(weight_limit: Weight) -> Weight;
|
||||
|
||||
/// Executes a message that could not be executed by [`Self::service_queues()`] because it was
|
||||
/// temporarily overweight.
|
||||
fn execute_overweight(
|
||||
_weight_limit: Weight,
|
||||
_address: Self::OverweightMessageAddress,
|
||||
) -> Result<Weight, ExecuteOverweightError> {
|
||||
Err(ExecuteOverweightError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
/// The resource footprint of a queue.
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, RuntimeDebug)]
|
||||
pub struct Footprint {
|
||||
pub count: u64,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
/// Can enqueue messages for multiple origins.
|
||||
pub trait EnqueueMessage<Origin: MaxEncodedLen> {
|
||||
/// The maximal length any enqueued message may have.
|
||||
type MaxMessageLen: Get<u32>;
|
||||
|
||||
/// Enqueue a single `message` from a specific `origin`.
|
||||
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: Origin);
|
||||
|
||||
/// Enqueue multiple `messages` from a specific `origin`.
|
||||
fn enqueue_messages<'a>(
|
||||
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
|
||||
origin: Origin,
|
||||
);
|
||||
|
||||
/// Any remaining unprocessed messages should happen only lazily, not proactively.
|
||||
fn sweep_queue(origin: Origin);
|
||||
|
||||
/// Return the state footprint of the given queue.
|
||||
fn footprint(origin: Origin) -> Footprint;
|
||||
}
|
||||
|
||||
impl<Origin: MaxEncodedLen> EnqueueMessage<Origin> for () {
|
||||
type MaxMessageLen = ConstU32<0>;
|
||||
fn enqueue_message(_: BoundedSlice<u8, Self::MaxMessageLen>, _: Origin) {}
|
||||
fn enqueue_messages<'a>(
|
||||
_: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
|
||||
_: Origin,
|
||||
) {
|
||||
}
|
||||
fn sweep_queue(_: Origin) {}
|
||||
fn footprint(_: Origin) -> Footprint {
|
||||
Footprint::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform the origin of an [`EnqueueMessage`] via `C::convert`.
|
||||
pub struct TransformOrigin<E, O, N, C>(PhantomData<(E, O, N, C)>);
|
||||
impl<E: EnqueueMessage<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>> EnqueueMessage<N>
|
||||
for TransformOrigin<E, O, N, C>
|
||||
{
|
||||
type MaxMessageLen = E::MaxMessageLen;
|
||||
|
||||
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: N) {
|
||||
E::enqueue_message(message, C::convert(origin));
|
||||
}
|
||||
|
||||
fn enqueue_messages<'a>(
|
||||
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
|
||||
origin: N,
|
||||
) {
|
||||
E::enqueue_messages(messages, C::convert(origin));
|
||||
}
|
||||
|
||||
fn sweep_queue(origin: N) {
|
||||
E::sweep_queue(C::convert(origin));
|
||||
}
|
||||
|
||||
fn footprint(origin: N) -> Footprint {
|
||||
E::footprint(C::convert(origin))
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles incoming messages for a single origin.
|
||||
pub trait HandleMessage {
|
||||
/// The maximal length any enqueued message may have.
|
||||
type MaxMessageLen: Get<u32>;
|
||||
|
||||
/// Enqueue a single `message` with an implied origin.
|
||||
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>);
|
||||
|
||||
/// Enqueue multiple `messages` from an implied origin.
|
||||
fn handle_messages<'a>(
|
||||
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
|
||||
);
|
||||
|
||||
/// Any remaining unprocessed messages should happen only lazily, not proactively.
|
||||
fn sweep_queue();
|
||||
|
||||
/// Return the state footprint of the queue.
|
||||
fn footprint() -> Footprint;
|
||||
}
|
||||
|
||||
/// Adapter type to transform an [`EnqueueMessage`] with an origin into a [`HandleMessage`] impl.
|
||||
pub struct EnqueueWithOrigin<E, O>(PhantomData<(E, O)>);
|
||||
impl<E: EnqueueMessage<O::Type>, O: TypedGet> HandleMessage for EnqueueWithOrigin<E, O>
|
||||
where
|
||||
O::Type: MaxEncodedLen,
|
||||
{
|
||||
type MaxMessageLen = E::MaxMessageLen;
|
||||
|
||||
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>) {
|
||||
E::enqueue_message(message, O::get());
|
||||
}
|
||||
|
||||
fn handle_messages<'a>(
|
||||
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
|
||||
) {
|
||||
E::enqueue_messages(messages, O::get());
|
||||
}
|
||||
|
||||
fn sweep_queue() {
|
||||
E::sweep_queue(O::get());
|
||||
}
|
||||
|
||||
fn footprint() -> Footprint {
|
||||
E::footprint(O::get())
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 278 others
|
||||
and 279 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
|
||||
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
|
||||
(A, B, C, D)
|
||||
(A, B, C, D, E)
|
||||
(A, B, C, D, E, F)
|
||||
and 161 others
|
||||
and 162 others
|
||||
= note: required for `Bar` to implement `StaticTypeInfo`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 278 others
|
||||
and 279 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
+3
-3
@@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 278 others
|
||||
and 279 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `PartialStorageInfoTrait`
|
||||
@@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied
|
||||
(A, B, C, D)
|
||||
(A, B, C, D, E)
|
||||
(A, B, C, D, E, F)
|
||||
and 161 others
|
||||
and 162 others
|
||||
= note: required for `Bar` to implement `StaticTypeInfo`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied
|
||||
<&[(T,)] as EncodeLike<BinaryHeap<LikeT>>>
|
||||
<&[(T,)] as EncodeLike<LinkedList<LikeT>>>
|
||||
<&[T] as EncodeLike<Vec<U>>>
|
||||
and 278 others
|
||||
and 279 others
|
||||
= note: required for `Bar` to implement `FullEncode`
|
||||
= note: required for `Bar` to implement `FullCodec`
|
||||
= note: required for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` to implement `StorageEntryMetadataBuilder`
|
||||
|
||||
@@ -675,6 +675,13 @@ impl<T, S: Get<u32>> BoundedVec<T, S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> BoundedVec<T, S> {
|
||||
/// Return a [`BoundedSlice`] with the content and bound of [`Self`].
|
||||
pub fn as_bounded_slice(&self) -> BoundedSlice<T, S> {
|
||||
BoundedSlice(&self.0[..], PhantomData::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S> Default for BoundedVec<T, S> {
|
||||
fn default() -> Self {
|
||||
// the bound cannot be below 0, which is satisfied by an empty vector
|
||||
|
||||
@@ -71,6 +71,12 @@ impl WeightMeter {
|
||||
time.max(pov)
|
||||
}
|
||||
|
||||
/// Consume some weight and defensively fail if it is over the limit. Saturate in any case.
|
||||
pub fn defensive_saturating_accrue(&mut self, w: Weight) {
|
||||
self.consumed.saturating_accrue(w);
|
||||
debug_assert!(self.consumed.all_lte(self.limit), "Weight counter overflow");
|
||||
}
|
||||
|
||||
/// Consume the given weight after checking that it can be consumed. Otherwise do nothing.
|
||||
pub fn check_accrue(&mut self, w: Weight) -> bool {
|
||||
self.consumed.checked_add(&w).map_or(false, |test| {
|
||||
|
||||
Reference in New Issue
Block a user