Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Move type Migrations to Config #14309

Merged
merged 12 commits into from
Jun 9, 2023

Conversation

juangirini
Copy link
Contributor

@juangirini juangirini commented Jun 6, 2023

This PR removes the hardcoded migration sequence from migrations.rs and places it within Config so it can be configured in the runtime.

How to update the code

Developers need to update their runtime configuration with their pending migrations (or () if none). For example:

use pallet_contracts::migration::{v10, v11, v9};

// ...

impl pallet_contracts::Config for Runtime {

// ...

   type Migrations = (v9::Migration<Runtime>, v10::Migration<Runtime>, v11::Migration<Runtime>);

This is a breaking change as it adds a new type to the Config, there's another breaking PR #14020 that changes it, so it would be a good idea to have them in the same release.


cumulus companion: paritytech/cumulus#2701

@juangirini juangirini added A3-in_progress Pull request is in progress. No review needed at this stage. C1-low PR touches the given topic and has a low impact on builders. D2-breaksapi B1-note_worthy Changes should be noted in the release notes F3-breaks_API This PR changes public API; next release should be major. T4-smart_contracts This PR/Issue is related to smart contracts. labels Jun 6, 2023
@juangirini juangirini requested a review from pgherveou June 6, 2023 11:31
@juangirini juangirini requested a review from athei as a code owner June 6, 2023 11:31
@juangirini juangirini self-assigned this Jun 6, 2023
@juangirini juangirini requested a review from a team June 6, 2023 11:31
@juangirini juangirini added the T1-runtime This PR/Issue is related to the topic “runtime”. label Jun 6, 2023
@juangirini juangirini requested a review from agryaznov June 6, 2023 11:35
@juangirini juangirini added A0-please_review Pull request needs code review. and removed A3-in_progress Pull request is in progress. No review needed at this stage. labels Jun 6, 2023
Copy link
Contributor

@pgherveou pgherveou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, left a few comments

frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
frame/contracts/src/lib.rs Outdated Show resolved Hide resolved
frame/contracts/src/migration.rs Outdated Show resolved Hide resolved
@pgherveou
Copy link
Contributor

you will have to fix cumulus too

@juangirini juangirini removed the T4-smart_contracts This PR/Issue is related to smart contracts. label Jun 6, 2023
@juangirini juangirini requested a review from pgherveou June 6, 2023 12:04
@pgherveou
Copy link
Contributor

pgherveou commented Jun 6, 2023

while we are making this change we might also want to be more strict in is_upgrade_supported here, and only allow a migration sequence that goes from current + 1 -> target. This should ensure that users don't include unneeded migration step in their wasm runtime

- in_storage >= first_supported && target == high
+ in_storage == first_supported && target == high

(+ fixing the test that touch this)

@juangirini juangirini requested a review from pgherveou June 6, 2023 15:30
frame/contracts/src/migration.rs Outdated Show resolved Hide resolved
frame/contracts/src/migration.rs Show resolved Hide resolved
@@ -1240,6 +1241,7 @@ impl pallet_contracts::Config for Runtime {
type MaxStorageKeyLen = ConstU32<128>;
type UnsafeUnstableInterface = ConstBool<false>;
type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>;
type Migrations = (NoopMigration<1>, NoopMigration<2>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can add a quick comments to specify that these noop migrations are used for benchmarking the migration framework.

Copy link
Contributor

@agryaznov agryaznov Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the things used for tests & benches should not appear here. It's better to keep them where they were, behind a feature flag. That's being said the docs giving an example how to configure the Migrations seq in runtime Config, would be great to have.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A () should suffice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these NoopMigrations are used by benchmarks, so we should keep them here

#[pov_mode = Measured]
migrate {
StorageVersion::new(0).put::<Pallet<T>>();
<Migration::<T> as frame_support::traits::OnRuntimeUpgrade>::on_runtime_upgrade();
let origin: RawOrigin<<T as frame_system::Config>::AccountId> = RawOrigin::Signed(whitelisted_caller());
}: {
<Contracts<T>>::migrate(origin.into(), Weight::MAX).unwrap()
} verify {
assert_eq!(StorageVersion::get::<Pallet<T>>(), 1);
}

Copy link
Member

@ggwpez ggwpez Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then how does this look like in an actual runtime?
Could be as ugly as this...

Copy link
Contributor

@pgherveou pgherveou Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the things used for tests & benches should not appear here. It's better to keep them where they were, behind a feature flag. That's being said the docs giving an example how to configure the Migrations seq in runtime Config, would be great to have.

@agryaznov I think that's because that is part of the config, we can't avoid having it here. Compile flags worked before because the type was internal to the pallet.

Copy link
Contributor Author

@juangirini juangirini Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then how does this look like in an actual runtime? Could be as ugly as this...

We could do something as ugly as that or as ugly as this (which was the initial approach)

/// Performs all necessary migrations based on `StorageVersion`.
#[cfg(not(feature = "runtime-benchmarks"))]
pub struct Migration<T: Config, M: MigrateSequence = T::Migrations>(PhantomData<(T, M)>);

/// Custom migration for running runtime-benchmarks and tests.
#[cfg(feature = "runtime-benchmarks")]
pub struct Migration<T: Config, M: MigrateSequence = (NoopMigration<1>, NoopMigration<2>)>(
	PhantomData<(T, M)>,
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how it is setup in @ggwpez 's link at least it makes it clear that these noopMigration are intended for benchmarks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have applied PG's suggested fix #14309 (comment)

@bkchr
Copy link
Member

bkchr commented Jun 6, 2023

@juangirini I don't get the need for this pr. Migrations can already be specified in the runtime lib.rs, we do this all the time. Like here:

type Migrations = (
pallet_nomination_pools::migration::v2::MigrateToV2<Runtime>,
pallet_alliance::migration::Migration<Runtime>,
pallet_contracts::Migration<Runtime>,
);

@juangirini
Copy link
Contributor Author

juangirini commented Jun 6, 2023

@juangirini I don't get the need for this pr. Migrations can already be specified in the runtime lib.rs, we do this all the time. Like here:

type Migrations = (
pallet_nomination_pools::migration::v2::MigrateToV2<Runtime>,
pallet_alliance::migration::Migration<Runtime>,
pallet_contracts::Migration<Runtime>,
);

@bkchr The idea here is to remove the hardcoded Migrations from here:

// In benchmark and tests we use noop migrations, to test and bench the migration framework itself.
#[cfg(not(any(feature = "runtime-benchmarks", test)))]
type Migrations<T> = (v9::Migration<T>, v10::Migration<T>, v11::Migration<T>);

so they can be fine tuned in the runtime.

Why do we need this?
As we are moving away from ReserveableCurrency in pallet_contracts #14020 (and similarly elsewhere paritytech/polkadot-sdk#226), we won't have T::Currency to perform this migration for instance, and therefore this migration will need to be generic over Currency where Currency would be the 'old' one before introducing the fungible traits.

Then we could have something like

impl pallet_contracts::Config for Runtime {

// ...

   type Migrations = (v9::Migration<Runtime>, v10::Migration<Runtime, Currency>, v11::Migration<Runtime>);   

This solution indeed creates the developers the need to maintain the Migrations type.


Alternatively, we could let live T::Currency and T::Fungible for a while and keep the migration as it is. The issue with this solution is that we would then need to let T::Currency live there for the sake of migrations, and at some point later remove it. Creating two breaking changes, one when introducing T::Fungible and another one when removing T::Currency.

@bkchr
Copy link
Member

bkchr commented Jun 6, 2023

Everything you said is already possible with the way we are doing runtime migrations in the lib.rs

@pgherveou
Copy link
Contributor

pgherveou commented Jun 6, 2023

Everything you said is already possible with the way we are doing runtime migrations in the lib.rs

@bkchr, unlike other pallets, we are using a new multi-block migration framework. The PR #14045 has more details on how it works, but essentially on_runtime_upgrade instead of doing the migration we set a custom storage MigrationInProgress. This storage defines a cursor for the current migration step. If it exists, a migration is in progress and it's value hold a cursor for the current migration step. These migration steps are executed on_idle or when a new migration dispatchable is invoked.

We need this new configuration trait to customize the migration steps in the runtime and use it in the pallet in these 2 places. The frame team is working on a similar solution that might not require such informations in the Config but until then, I believe that is the best way for us to configure our multi-block migrations.

Here are the relevant bits of codes discussed:

fn on_idle(_block: T::BlockNumber, remaining_weight: Weight) -> Weight {
use migration::MigrateResult::*;
let (result, weight) = Migration::<T>::migrate(remaining_weight);

#[pallet::call_index(9)]
#[pallet::weight(T::WeightInfo::migrate().saturating_add(*weight_limit))]
pub fn migrate(origin: OriginFor<T>, weight_limit: Weight) -> DispatchResultWithPostInfo {
use migration::MigrateResult::*;
ensure_signed(origin)?;
let weight_limit = weight_limit.saturating_add(T::WeightInfo::migrate());
let (result, weight) = Migration::<T>::migrate(weight_limit);

pub(crate) fn migrate(weight_limit: Weight) -> (MigrateResult, Weight) {
let name = <Pallet<T>>::name();
let mut weight_left = weight_limit;
if weight_left.checked_reduce(T::WeightInfo::migrate()).is_none() {
return (MigrateResult::NoMigrationPerformed, Weight::zero())
}
MigrationInProgress::<T>::mutate_exists(|progress| {
let Some(cursor_before) = progress.as_mut() else {
return (MigrateResult::NoMigrationInProgress, T::WeightInfo::migration_noop())
};
// if a migration is running it is always upgrading to the next version
let storage_version = <Pallet<T>>::on_chain_storage_version();
let in_progress_version = storage_version + 1;
log::info!(
target: LOG_TARGET,
"{name}: Migrating from {:?} to {:?},",
storage_version,
in_progress_version,
);
let result = match T::Migrations::steps(
in_progress_version,
cursor_before.as_ref(),
&mut weight_left,
) {

@juangirini
Copy link
Contributor Author

@pg thanks for the comment, I was not explicit about the fact that this pallet is using a custom MBM.

frame/contracts/src/migration.rs Outdated Show resolved Hide resolved
frame/contracts/src/migration.rs Show resolved Hide resolved
@@ -1240,6 +1241,7 @@ impl pallet_contracts::Config for Runtime {
type MaxStorageKeyLen = ConstU32<128>;
type UnsafeUnstableInterface = ConstBool<false>;
type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>;
type Migrations = (NoopMigration<1>, NoopMigration<2>);
Copy link
Contributor

@agryaznov agryaznov Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the things used for tests & benches should not appear here. It's better to keep them where they were, behind a feature flag. That's being said the docs giving an example how to configure the Migrations seq in runtime Config, would be great to have.

frame/contracts/src/migration.rs Outdated Show resolved Hide resolved
@juangirini
Copy link
Contributor Author

I think the things used for tests & benches should not appear here. It's better to keep them where they were, behind a feature flag. That's being said the docs giving an example how to configure the Migrations seq in runtime Config, would be great to have.

@agryaznov as commented here #14309 (comment) this has been resolved. Even though the feature gated Migrations already serves as an example now, I have added a docs example

@juangirini juangirini requested a review from agryaznov June 7, 2023 12:54
@juangirini
Copy link
Contributor Author

bot merge

@paritytech-processbot
Copy link

Error: Github API says paritytech/cumulus#2701 is not mergeable

@juangirini
Copy link
Contributor Author

bot merge

@paritytech-processbot paritytech-processbot bot merged commit ff3be27 into master Jun 9, 2023
@paritytech-processbot paritytech-processbot bot deleted the jg/remove-default-migrate-sequence branch June 9, 2023 11:04
agryaznov pushed a commit that referenced this pull request Jun 9, 2023
* move migrate sequence to config

* remove commented out code

* Update frame/contracts/src/lib.rs

Co-authored-by: PG Herveou <[email protected]>

* remove Migrations generic

* make runtime use noop migrations

* restrict is_upgrade_supported

* undo is_upgrade_supported change

* Update bin/node/runtime/src/lib.rs

Co-authored-by: PG Herveou <[email protected]>

* add rust doc example for `Migrations`

* feature gate NoopMigration

* fix example code

* improve example

---------

Co-authored-by: PG Herveou <[email protected]>
nathanwhit pushed a commit to nathanwhit/substrate that referenced this pull request Jul 19, 2023
* move migrate sequence to config

* remove commented out code

* Update frame/contracts/src/lib.rs

Co-authored-by: PG Herveou <[email protected]>

* remove Migrations generic

* make runtime use noop migrations

* restrict is_upgrade_supported

* undo is_upgrade_supported change

* Update bin/node/runtime/src/lib.rs

Co-authored-by: PG Herveou <[email protected]>

* add rust doc example for `Migrations`

* feature gate NoopMigration

* fix example code

* improve example

---------

Co-authored-by: PG Herveou <[email protected]>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A0-please_review Pull request needs code review. B1-note_worthy Changes should be noted in the release notes C1-low PR touches the given topic and has a low impact on builders. D2-breaksapi F3-breaks_API This PR changes public API; next release should be major. T1-runtime This PR/Issue is related to the topic “runtime”.
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

5 participants