referrerpolicy=no-referrer-when-downgrade

pallet_migrations/
benchmarking.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg(feature = "runtime-benchmarks")]
19
20use super::*;
21
22use core::array;
23use frame_benchmarking::{v2::*, BenchmarkError};
24use frame_system::{Pallet as System, RawOrigin};
25use sp_core::Get;
26use sp_crypto_hashing::twox_128;
27use sp_io::{storage, KillStorageResult};
28use sp_runtime::traits::One;
29
30fn assert_has_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
31	frame_system::Pallet::<T>::assert_has_event(generic_event.into());
32}
33
34fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
35	frame_system::Pallet::<T>::assert_last_event(generic_event.into());
36}
37
38#[benchmarks]
39mod benches {
40	use super::*;
41	use frame_support::traits::Hooks;
42
43	#[benchmark]
44	fn onboard_new_mbms() {
45		T::Migrations::set_fail_after(0); // Should not be called anyway.
46		assert!(!Cursor::<T>::exists());
47
48		#[block]
49		{
50			Pallet::<T>::onboard_new_mbms();
51		}
52
53		assert_last_event::<T>(Event::UpgradeStarted { migrations: 1 }.into());
54	}
55
56	#[benchmark]
57	fn progress_mbms_none() {
58		T::Migrations::set_fail_after(0); // Should not be called anyway.
59		assert!(!Cursor::<T>::exists());
60
61		#[block]
62		{
63			Pallet::<T>::progress_mbms(One::one());
64		}
65	}
66
67	/// All migrations completed.
68	#[benchmark]
69	fn exec_migration_completed() -> Result<(), BenchmarkError> {
70		T::Migrations::set_fail_after(0); // Should not be called anyway.
71		assert_eq!(T::Migrations::len(), 1, "Setup failed");
72		let c = ActiveCursor { index: 1, inner_cursor: None, started_at: 0u32.into() };
73		let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get());
74		System::<T>::set_block_number(1u32.into());
75
76		#[block]
77		{
78			Pallet::<T>::exec_migration(c, false, &mut meter);
79		}
80
81		assert_last_event::<T>(Event::UpgradeCompleted {}.into());
82
83		Ok(())
84	}
85
86	/// No migration runs since it is skipped as historic.
87	#[benchmark]
88	fn exec_migration_skipped_historic() -> Result<(), BenchmarkError> {
89		T::Migrations::set_fail_after(0); // Should not be called anyway.
90		assert_eq!(T::Migrations::len(), 1, "Setup failed");
91		let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() };
92
93		let id: IdentifierOf<T> = T::Migrations::nth_id(0).unwrap().try_into().unwrap();
94		Historic::<T>::insert(id, ());
95
96		let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get());
97		System::<T>::set_block_number(1u32.into());
98
99		#[block]
100		{
101			Pallet::<T>::exec_migration(c, false, &mut meter);
102		}
103
104		assert_last_event::<T>(Event::MigrationSkipped { index: 0 }.into());
105
106		Ok(())
107	}
108
109	/// Advance a migration by one step.
110	#[benchmark]
111	fn exec_migration_advance() -> Result<(), BenchmarkError> {
112		T::Migrations::set_success_after(1);
113		assert_eq!(T::Migrations::len(), 1, "Setup failed");
114		let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() };
115		let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get());
116		System::<T>::set_block_number(1u32.into());
117
118		#[block]
119		{
120			Pallet::<T>::exec_migration(c, false, &mut meter);
121		}
122
123		assert_last_event::<T>(Event::MigrationAdvanced { index: 0, took: One::one() }.into());
124
125		Ok(())
126	}
127
128	/// Successfully complete a migration.
129	#[benchmark]
130	fn exec_migration_complete() -> Result<(), BenchmarkError> {
131		T::Migrations::set_success_after(0);
132		assert_eq!(T::Migrations::len(), 1, "Setup failed");
133		let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() };
134		let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get());
135		System::<T>::set_block_number(1u32.into());
136
137		#[block]
138		{
139			Pallet::<T>::exec_migration(c, false, &mut meter);
140		}
141
142		assert_last_event::<T>(Event::MigrationCompleted { index: 0, took: One::one() }.into());
143
144		Ok(())
145	}
146
147	#[benchmark]
148	fn exec_migration_fail() -> Result<(), BenchmarkError> {
149		T::Migrations::set_fail_after(0);
150		assert_eq!(T::Migrations::len(), 1, "Setup failed");
151		let c = ActiveCursor { index: 0, inner_cursor: None, started_at: 0u32.into() };
152		let mut meter = WeightMeter::with_limit(T::MaxServiceWeight::get());
153		System::<T>::set_block_number(1u32.into());
154
155		#[block]
156		{
157			Pallet::<T>::exec_migration(c, false, &mut meter);
158		}
159
160		assert_has_event::<T>(Event::UpgradeFailed {}.into());
161
162		Ok(())
163	}
164
165	#[benchmark]
166	fn on_init_loop() {
167		T::Migrations::set_fail_after(0); // Should not be called anyway.
168		System::<T>::set_block_number(1u32.into());
169		<Pallet<T> as Hooks<BlockNumberFor<T>>>::on_runtime_upgrade();
170
171		#[block]
172		{
173			Pallet::<T>::on_initialize(1u32.into());
174		}
175	}
176
177	#[benchmark]
178	fn force_set_cursor() {
179		#[extrinsic_call]
180		_(RawOrigin::Root, Some(cursor::<T>()));
181	}
182
183	#[benchmark]
184	fn force_set_active_cursor() {
185		#[extrinsic_call]
186		_(RawOrigin::Root, 0, None, None);
187	}
188
189	#[benchmark]
190	fn force_onboard_mbms() {
191		#[extrinsic_call]
192		_(RawOrigin::Root);
193	}
194
195	#[benchmark]
196	fn clear_historic(n: Linear<0, { DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 }>) {
197		let id_max_len = <T as Config>::IdentifierMaxLen::get();
198		assert!(id_max_len >= 4, "Precondition violated");
199
200		for i in 0..DEFAULT_HISTORIC_BATCH_CLEAR_SIZE * 2 {
201			let id = IdentifierOf::<T>::truncate_from(
202				i.encode().into_iter().cycle().take(id_max_len as usize).collect::<Vec<_>>(),
203			);
204
205			Historic::<T>::insert(&id, ());
206		}
207
208		#[extrinsic_call]
209		_(
210			RawOrigin::Root,
211			HistoricCleanupSelector::Wildcard { limit: n.into(), previous_cursor: None },
212		);
213	}
214
215	#[benchmark(skip_meta, pov_mode = Measured)]
216	fn reset_pallet_migration(n: Linear<0, 2048>) -> Result<(), BenchmarkError> {
217		let prefix: [u8; 16] = twox_128(b"__ResetPalletBenchmarkPrefix__");
218
219		for i in 0..n {
220			// we need to avoid allocations here
221			let mut iter = prefix.into_iter().chain(i.to_le_bytes());
222			let key: [u8; 20] = array::from_fn(|_| iter.next().unwrap());
223			// 32 byte will trigger the worst case where the value is
224			// no longer stored inline
225			storage::set(&key, &[0u8; 32]);
226		}
227
228		let result;
229		#[block]
230		{
231			result = storage::clear_prefix(&prefix, None);
232		}
233
234		// It will always reports no keys removed because they are still in the overlay.
235		// However, the benchmarking PoV results are correctly dependent on the amount of
236		// keys removed.
237		match result {
238			KillStorageResult::AllRemoved(_i) => {
239				// during the test the storage is not comitted and `i` will always be 0
240				#[cfg(not(test))]
241				ensure!(_i == n, "Not all keys are removed");
242			},
243			_ => Err("Not all keys were removed")?,
244		}
245
246		Ok(())
247	}
248
249	fn cursor<T: Config>() -> CursorOf<T> {
250		// Note: The weight of a function can depend on the weight of reading the `inner_cursor`.
251		// `Cursor` is a user provided type. Now instead of requiring something like `Cursor:
252		// From<u32>`, we instead rely on the fact that it is MEL and the PoV benchmarking will
253		// therefore already take the MEL bound, even when the cursor in storage is `None`.
254		MigrationCursor::Active(ActiveCursor {
255			index: u32::MAX,
256			inner_cursor: None,
257			started_at: 0u32.into(),
258		})
259	}
260
261	// Implements a test for each benchmark. Execute with:
262	// `cargo test -p pallet-migrations --features runtime-benchmarks`.
263	impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
264}