-
Notifications
You must be signed in to change notification settings - Fork 2.7k
migrations: VersionedRuntimeUpgrade #14311
Changes from all commits
0885a4f
9c0072f
2f30d00
451de26
d8bc4d7
53f6170
617c864
1b9c68d
acc9b0c
c3638c5
f5cab56
bd42fbe
0aae7e8
71f7c6c
7919778
44d65b0
87ecff5
284a403
53bf2f1
d34405a
46f8bfb
3c31046
5bdb5b8
65fd376
9941c7c
6fd6c10
446b19e
2b23890
53473ee
e78ff5a
27f35d1
7f8add6
52b920e
a16eba7
c8e0538
2417754
a72c9b7
63542cf
547feec
8c2050c
ae484c7
ab880cb
605f63d
8a9e31c
e5d4207
888f5eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,8 +15,6 @@ | |
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
|
|
||
| #[cfg(feature = "try-runtime")] | ||
| use crate::storage::unhashed::contains_prefixed_key; | ||
| use crate::{ | ||
| traits::{GetStorageVersion, NoStorageVersionSet, PalletInfoAccess, StorageVersion}, | ||
| weights::{RuntimeDbWeight, Weight}, | ||
|
|
@@ -25,9 +23,148 @@ use impl_trait_for_tuples::impl_for_tuples; | |
| use sp_core::Get; | ||
| use sp_io::{hashing::twox_128, storage::clear_prefix, KillStorageResult}; | ||
| use sp_std::marker::PhantomData; | ||
|
|
||
| #[cfg(feature = "try-runtime")] | ||
| use sp_std::vec::Vec; | ||
|
|
||
| #[cfg(feature = "experimental")] | ||
| use crate::traits::OnRuntimeUpgrade; | ||
|
|
||
| /// Make it easier to write versioned runtime upgrades. | ||
| /// | ||
| /// [`VersionedRuntimeUpgrade`] allows developers to write migrations without worrying about | ||
| /// checking and setting storage versions. Instead, the developer wraps their migration in this | ||
| /// struct which takes care of version handling using best practices. | ||
| /// | ||
| /// It takes 5 type parameters: | ||
| /// - `From`: The version being upgraded from. | ||
| /// - `To`: The version being upgraded to. | ||
| /// - `Inner`: An implementation of `OnRuntimeUpgrade`. | ||
| /// - `Pallet`: The Pallet being upgraded. | ||
| /// - `Weight`: The runtime's RuntimeDbWeight implementation. | ||
| /// | ||
| /// When a [`VersionedRuntimeUpgrade`] `on_runtime_upgrade`, `pre_upgrade`, or `post_upgrade` | ||
| /// method is called, the on-chain version of the pallet is compared to `From`. If they match, the | ||
| /// `Inner` equivalent is called and the pallets on-chain version is set to `To` after the | ||
| /// migration. Otherwise, a warning is logged notifying the developer that the upgrade was a noop | ||
| /// and should probably be removed. | ||
| /// | ||
| /// ### Examples | ||
| /// ```ignore | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. possibly not to difficult to make this not be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are you thinking I'd use the dummy pallet and upgrades from my test file? I'd rather not use a migration from a real pallet since it creates a dependency that could break. I'm also curious what the benefit/s are of making the comment compile |
||
| /// // In file defining migrations | ||
| /// pub struct VersionUncheckedMigrateV5ToV6<T>(sp_std::marker::PhantomData<T>); | ||
| /// impl<T: Config> OnRuntimeUpgrade for VersionUncheckedMigrateV5ToV6<T> { | ||
| /// // OnRuntimeUpgrade implementation... | ||
| /// } | ||
| /// | ||
| /// pub type VersionCheckedMigrateV5ToV6<Runtime, Pallet, DbWeight> = | ||
| /// VersionedRuntimeUpgrade<5, 6, VersionUncheckedMigrateV5ToV6<Runtime>, Pallet, DbWeight>; | ||
| /// | ||
| /// // Migrations tuple to pass to the Executive pallet: | ||
| /// pub type Migrations = ( | ||
| /// // other migrations... | ||
| /// VersionCheckedMigrateV5ToV6<Runtime, Balances, RuntimeDbWeight>, | ||
| /// // other migrations... | ||
| /// ); | ||
| /// ``` | ||
| #[cfg(feature = "experimental")] | ||
| pub struct VersionedRuntimeUpgrade<const FROM: u16, const TO: u16, Inner, Pallet, Weight> { | ||
| _marker: PhantomData<(Inner, Pallet, Weight)>, | ||
| } | ||
|
|
||
| /// A helper enum to wrap the pre_upgrade bytes like an Option before passing them to post_upgrade. | ||
| /// This enum is used rather than an Option to make the API clearer to the developer. | ||
| #[cfg(feature = "experimental")] | ||
| #[derive(codec::Encode, codec::Decode)] | ||
| pub enum VersionedPostUpgradeData { | ||
| /// The migration ran, inner vec contains pre_upgrade data. | ||
| MigrationExecuted(Vec<u8>), | ||
| /// This migration is a noop, do not run post_upgrade checks. | ||
| Noop, | ||
| } | ||
|
|
||
| /// Implementation of the `OnRuntimeUpgrade` trait for `VersionedRuntimeUpgrade`. | ||
| /// | ||
| /// Its main function is to perform the runtime upgrade in `on_runtime_upgrade` only if the on-chain | ||
| /// version of the pallets storage matches `From`, and after the upgrade set the on-chain storage to | ||
| /// `To`. If the versions do not match, it writes a log notifying the developer that the migration | ||
| /// is a noop. | ||
| #[cfg(feature = "experimental")] | ||
| impl< | ||
| const FROM: u16, | ||
| const TO: u16, | ||
| Inner: OnRuntimeUpgrade, | ||
| Pallet: GetStorageVersion<CurrentStorageVersion = StorageVersion> + PalletInfoAccess, | ||
| DbWeight: Get<RuntimeDbWeight>, | ||
|
Comment on lines
+97
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For readability purposes, I would consider moving Then, in the runtime, when you are wrapping things in Possibly not worth it, especially as I am still critical of the fundamental approach.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure how to obtain
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm able to remove the before (inside migrations file): pub type VersionCheckedMigrateV5ToV6<Runtime, Pallet, DbWeight> = VersionedRuntimeUpgrade<
ConstU16<5>,
ConstU16<6>,
VersionUncheckedMigrateV5ToV6<Runtime>,
Pallet,
DbWeight,
>;after (inside migrations file): pub type VersionCheckedMigrateV5ToV6<Runtime, Pallet> = VersionedRuntimeUpgrade<
ConstU16<5>,
ConstU16<6>,
VersionUncheckedMigrateV5ToV6<Runtime>,
Pallet,
<Runtime as frame_system::Config>::DbWeight,
>;Then in the runtime file, you no longer need to pass the DbWeight: parachains_configuration::migration::v6::VersionCheckedMigrateV5ToV6<
Runtime,
Configuration,
>,Let me know if this approach is also OK, it seems to keep things clean in the runtime file without needing to create an extra trait, but I'm a noob with associated types so definitely may be missing something, please let me know if I am. |
||
| > OnRuntimeUpgrade for VersionedRuntimeUpgrade<FROM, TO, Inner, Pallet, DbWeight> | ||
| { | ||
| /// Executes pre_upgrade if the migration will run, and wraps the pre_upgrade bytes in | ||
| /// [`VersionedPostUpgradeData`] before passing them to post_upgrade, so it knows whether the | ||
| /// migration ran or not. | ||
| #[cfg(feature = "try-runtime")] | ||
| fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> { | ||
| use codec::Encode; | ||
| let on_chain_version = Pallet::on_chain_storage_version(); | ||
| if on_chain_version == FROM { | ||
| Ok(VersionedPostUpgradeData::MigrationExecuted(Inner::pre_upgrade()?).encode()) | ||
| } else { | ||
| Ok(VersionedPostUpgradeData::Noop.encode()) | ||
| } | ||
| } | ||
|
|
||
| /// Executes the versioned runtime upgrade. | ||
| /// | ||
| /// First checks if the pallets on-chain storage version matches the version of this upgrade. If | ||
| /// it matches, it calls `Inner::on_runtime_upgrade`, updates the on-chain version, and returns | ||
| /// the weight. If it does not match, it writes a log notifying the developer that the migration | ||
| /// is a noop. | ||
| fn on_runtime_upgrade() -> Weight { | ||
| let on_chain_version = Pallet::on_chain_storage_version(); | ||
| if on_chain_version == FROM { | ||
| log::info!( | ||
| "Running {} VersionedOnRuntimeUpgrade: version {:?} to {:?}.", | ||
| Pallet::name(), | ||
| FROM, | ||
| TO | ||
| ); | ||
|
|
||
| // Execute the migration | ||
| let weight = Inner::on_runtime_upgrade(); | ||
|
|
||
| // Update the on-chain version | ||
| StorageVersion::new(TO).put::<Pallet>(); | ||
|
|
||
| weight.saturating_add(DbWeight::get().reads_writes(1, 1)) | ||
| } else { | ||
| log::warn!( | ||
| "{} VersionedOnRuntimeUpgrade for version {:?} skipped because current on-chain version is {:?}.", | ||
| Pallet::name(), | ||
| FROM, | ||
| on_chain_version | ||
| ); | ||
| DbWeight::get().reads(1) | ||
| } | ||
| } | ||
|
|
||
| /// Executes `Inner::post_upgrade` if the migration just ran. | ||
| /// | ||
| /// pre_upgrade passes [`VersionedPostUpgradeData::MigrationExecuted`] to post_upgrade if | ||
| /// the migration ran, and [`VersionedPostUpgradeData::Noop`] otherwise. | ||
| #[cfg(feature = "try-runtime")] | ||
| fn post_upgrade( | ||
| versioned_post_upgrade_data_bytes: Vec<u8>, | ||
| ) -> Result<(), sp_runtime::TryRuntimeError> { | ||
| use codec::DecodeAll; | ||
| match <VersionedPostUpgradeData>::decode_all(&mut &versioned_post_upgrade_data_bytes[..]) | ||
| .map_err(|_| "VersionedRuntimeUpgrade post_upgrade failed to decode PreUpgradeData")? | ||
| { | ||
| VersionedPostUpgradeData::MigrationExecuted(inner_bytes) => | ||
| Inner::post_upgrade(inner_bytes), | ||
| VersionedPostUpgradeData::Noop => Ok(()), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Can store the current pallet version in storage. | ||
| pub trait StoreCurrentStorageVersion<T: GetStorageVersion + PalletInfoAccess> { | ||
| /// Write the current storage version to the storage. | ||
|
|
@@ -185,6 +322,8 @@ impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits | |
|
|
||
| #[cfg(feature = "try-runtime")] | ||
| fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> { | ||
| use crate::storage::unhashed::contains_prefixed_key; | ||
|
|
||
| let hashed_prefix = twox_128(P::get().as_bytes()); | ||
| match contains_prefixed_key(&hashed_prefix) { | ||
| true => log::info!("Found {} keys pre-removal 👀", P::get()), | ||
|
|
@@ -198,6 +337,8 @@ impl<P: Get<&'static str>, DbWeight: Get<RuntimeDbWeight>> frame_support::traits | |
|
|
||
| #[cfg(feature = "try-runtime")] | ||
| fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> { | ||
| use crate::storage::unhashed::contains_prefixed_key; | ||
|
|
||
| let hashed_prefix = twox_128(P::get().as_bytes()); | ||
| match contains_prefixed_key(&hashed_prefix) { | ||
| true => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.