Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pallet-migrations Initial Implementation #527

Merged
merged 84 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 67 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
9ecdc74
Add initial migrations pallet sketch
notlesh Jun 21, 2021
e4f15ce
Sketch out Migration impls
notlesh Jun 25, 2021
ca3620e
Make it build
notlesh Jun 25, 2021
cdc042a
Squelch warnings
notlesh Jun 25, 2021
c4204b4
Sketch out process_runtime_upgrades
notlesh Jun 25, 2021
0cc885d
Leave note for reviewers
notlesh Jun 25, 2021
95308a1
Add &self to Migrations trait fns
notlesh Jun 28, 2021
93b96ce
Make it compile
notlesh Jun 28, 2021
275531c
Refactor migrations design to use stepping instead of one-shot
notlesh Jun 28, 2021
d5abf1f
Fix typo/bug
notlesh Jun 28, 2021
d25ffc1
Track overall migration doneness
notlesh Jun 28, 2021
0c9ce74
Optimize when progress remains unchanged
notlesh Jun 28, 2021
23837f8
Resolve compiler warnings
notlesh Jun 29, 2021
85ecaa1
Incremental progress on mock
notlesh Jun 29, 2021
f22ce8d
Mock is getting close
notlesh Jun 29, 2021
d8d2c3c
Make mock build
notlesh Jun 29, 2021
bfd24f6
Plumb genesis building in mock
notlesh Jun 30, 2021
9404fbe
Baby's first tests
notlesh Jun 30, 2021
edee830
Fix events
notlesh Jun 30, 2021
d13a471
Use Vec<u8> instead of String
notlesh Jun 30, 2021
986433e
Make MigrationsList part of pallet config; plumb through Moonbase run…
notlesh Jul 1, 2021
92047ee
Appease the compiler
notlesh Jul 1, 2021
b885fc5
Fix up CommonMigrations list
notlesh Jul 1, 2021
aefb302
Remove comment
notlesh Jul 1, 2021
f9f8a2a
Cargo fmt
notlesh Jul 1, 2021
bc2e538
Per-test MigrationsList
notlesh Jul 1, 2021
19a9912
Attempt at a glue
notlesh Jul 3, 2021
1f70d01
Fix FIXME
notlesh Jul 3, 2021
6e581f3
Getting close
notlesh Jul 3, 2021
2ea1e3b
Sort out lifetimes
notlesh Jul 3, 2021
4afc532
Simplify FnMut arguments/storage
notlesh Jul 6, 2021
85ed484
Clean up, fix FIXMEs
notlesh Jul 6, 2021
e377caf
It works
notlesh Jul 7, 2021
95d6b95
Implement Migrations::on_initialize
notlesh Jul 7, 2021
d5da28a
Resolve compilation warnings, add comments about how mock glue works
notlesh Jul 7, 2021
2c0bfe5
Move migration event impl
notlesh Jul 7, 2021
67bede2
Let tests manage ExtBuilder ... execute_with()
notlesh Jul 8, 2021
f924112
Test that migrations are only run once
notlesh Jul 8, 2021
5022dcc
Remove TODO/comment; events are not cheap and should be used conserva…
notlesh Jul 8, 2021
8a925ac
Merge branch 'master' into notlesh-migration-sketch
notlesh Jul 19, 2021
a2177dd
Post merge-master fixes
notlesh Jul 19, 2021
01727f8
Remove cruft
notlesh Jul 19, 2021
8ed861f
Track some db reads and writes and charge accordingly
notlesh Jul 19, 2021
0785e48
cargo fmt
notlesh Jul 20, 2021
8c444e7
Add failing test about one-migration-at-a-time
notlesh Jul 21, 2021
2e12fd2
Don't start next migration until current is done
notlesh Jul 21, 2021
9a9bfd4
Add notes from meeting
notlesh Jul 22, 2021
2744571
Allow multi-block migrations to be disabled
notlesh Jul 22, 2021
e1f49c6
Add failing test about overweight migrations
notlesh Jul 22, 2021
0684874
Explicitly embrace allowing overweight migrations
notlesh Jul 22, 2021
c19a4a1
cargo fmt
notlesh Jul 22, 2021
7d57a4e
Clean up / add comments
notlesh Jul 26, 2021
3f87f79
Derive block weight from Config (still needs improvement)
notlesh Jul 26, 2021
595e865
Merge branch 'master' into notlesh-migration-sketch
notlesh Jul 26, 2021
8f6e9e5
cargo fmt
notlesh Jul 26, 2021
a4e2acc
Configure all runtimes to include pallet-migrations
notlesh Jul 26, 2021
41e9d0b
Add pallet-migrations genesis to specs
notlesh Jul 26, 2021
0a3ae7e
Update pallets/migrations/src/lib.rs
notlesh Jul 29, 2021
26ef7db
Update pallets/migrations/src/lib.rs
notlesh Jul 29, 2021
e6543e6
First pass at ripping out multi-block migration support
notlesh Aug 13, 2021
01bc09f
Incremental work @ removing multi-block migration support
notlesh Aug 13, 2021
d2e5759
Make migration tests compile (not passing yet)
notlesh Aug 13, 2021
38ee3a4
Clean up runtime to reflect removal of multi-block migrations
notlesh Aug 17, 2021
30033ee
You know your tests are good when they catch a critical refactor mistake
notlesh Aug 17, 2021
dfb6f42
Fix test logic to reflect no multi-block migrations
notlesh Aug 17, 2021
f691977
cargo fmt
notlesh Aug 17, 2021
dc42819
Remove phantomdata field from pallet_migrations::GenesisConfig (#701)
4meta5 Aug 18, 2021
f31807a
Better log statement
notlesh Aug 18, 2021
607e0ee
Use ValueQuery instead of OptionQuery
notlesh Aug 18, 2021
3e9c26b
Merge branch 'master' into notlesh-migration-sketch
notlesh Aug 18, 2021
91f92f7
Update Cargo.lock
notlesh Aug 18, 2021
c0e6167
Manually add back version = 3
notlesh Aug 18, 2021
d9e561b
Make some deps dev-dependencies
notlesh Aug 18, 2021
3722eaf
Merge branch 'master' into notlesh-migration-sketch
notlesh Sep 8, 2021
a585e3c
Fix branch
notlesh Sep 8, 2021
bdafb76
Use hotfix branch in Migrations
notlesh Sep 8, 2021
9d27292
Clean up from merge
notlesh Sep 8, 2021
e346883
cargo fmt
notlesh Sep 8, 2021
3cb8bb4
Remove prior hack in test
notlesh Sep 8, 2021
99ca061
cargo.lock
JoshOrndorff Sep 16, 2021
62eb821
Merge branch 'master' into notlesh-migration-sketch
JoshOrndorff Sep 16, 2021
cf735f9
fix warning
JoshOrndorff Sep 16, 2021
fd0a165
minor cleaning in cargo.toml
JoshOrndorff Sep 16, 2021
3bcfce5
clean up cargo.ttoml
JoshOrndorff Sep 20, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
'node',
'node/cli',
'node/service',
'pallets/migrations',
'bin/utils/moonkey',
]
exclude = [
Expand Down
1 change: 1 addition & 0 deletions node/service/src/chain_spec/moonbase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ pub fn testnet_genesis(
.collect(),
},
treasury: Default::default(),
migrations: Default::default(),
}
}

Expand Down
1 change: 1 addition & 0 deletions node/service/src/chain_spec/moonbeam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub fn testnet_genesis(
.collect(),
},
treasury: Default::default(),
migrations: Default::default(),
}
}

Expand Down
1 change: 1 addition & 0 deletions node/service/src/chain_spec/moonriver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub fn testnet_genesis(
.collect(),
},
treasury: Default::default(),
migrations: Default::default(),
}
}

Expand Down
1 change: 1 addition & 0 deletions node/service/src/chain_spec/moonshadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ pub fn testnet_genesis(
.collect(),
},
treasury: Default::default(),
migrations: Default::default(),
}
}

Expand Down
29 changes: 29 additions & 0 deletions pallets/migrations/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[package]
name = "pallet-migrations"
version = "0.1.0"
authors = ["PureStake"]
edition = "2018"
description = "migrations management pallet"

[dependencies]
frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
log = "0.4"
sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
parity-scale-codec = { version = "2.0.0", default-features = false }
environmental = { version = "1.1.2", default-features = false }
sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.8", default-features = false }
notlesh marked this conversation as resolved.
Show resolved Hide resolved

[features]
default = ["std"]
std = [
"frame-support/std",
"frame-system/std",
"sp-std/std",
"sp-runtime/std",
"sp-io/std",
"sp-core/std",
"environmental/std",
]
192 changes: 192 additions & 0 deletions pallets/migrations/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Copyright 2019-2020 PureStake Inc.
// This file is part of Moonbeam.

// Moonbeam 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.

// Moonbeam 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 Moonbeam. If not, see <http://www.gnu.org/licenses/>.

//! # Migration Pallet

#![allow(non_camel_case_types)]
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;

use frame_support::{pallet, weights::Weight};

pub use pallet::*;

#[cfg(test)]
#[macro_use]
extern crate environmental;

/// A Migration that must happen on-chain upon a runtime-upgrade
pub trait Migration {
/// A human-readable name for this migration. Also used as storage key.
fn friendly_name(&self) -> &str;

/// Perform the required migration and return the weight consumed.
///
/// Currently there is no way to migrate across blocks, so this method must (1) perform its full
/// migration and (2) not produce a block that has gone over-weight. Not meeting these strict
/// constraints will lead to a bricked chain upon a runtime upgrade because the parachain will
/// not be able to produce a block that the relay chain will accept.
fn migrate(&self, available_weight: Weight) -> Weight;
}

#[pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sp_std::prelude::*;

/// Pallet for migrations
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);

/// Configuration trait of this pallet.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Overarching event type
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// The list of migrations that will be performed
type MigrationsList: Get<Vec<Box<dyn Migration>>>;
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with an earlier suggestion to change this trait bound to Migration and allow it to accept multiple migrations by implementing Migration for tuple instead of passing them in as a vec.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree. We can do it in a followup though.

}

#[pallet::event]
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
pub enum Event<T: Config> {
RuntimeUpgradeStarted(),
RuntimeUpgradeCompleted(Weight),
MigrationStarted(Vec<u8>),
MigrationCompleted(Vec<u8>, Weight),
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
/// on_runtime_upgrade is expected to be called exactly once after a runtime upgrade.
/// We use this as a chance to flag that we are now in upgrade-mode and begin our
/// migrations.
fn on_runtime_upgrade() -> Weight {
log::warn!("Performing on_runtime_upgrade");

let mut weight: Weight = 0u64.into();
// TODO: derive a suitable value here, which is probably something < max_block
let available_weight: Weight = T::BlockWeights::get().max_block;
Comment on lines +87 to +88
Copy link
Contributor

Choose a reason for hiding this comment

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

yeah, max_block does not account for the rest of on_initialize AFAICT


// start by flagging that we are not fully upgraded
<FullyUpgraded<T>>::put(false);
JoshOrndorff marked this conversation as resolved.
Show resolved Hide resolved
weight += T::DbWeight::get().writes(1);
Self::deposit_event(Event::RuntimeUpgradeStarted());

weight += perform_runtime_upgrades::<T>(available_weight.saturating_sub(weight));

if !<FullyUpgraded<T>>::get() {
log::error!(
"migrations weren't completed in on_runtime_upgrade(), but we're not
configured for multi-block migrations; state is potentially inconsistent!"
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we panic in this case? If you panic the block is immediately invalid. But that may be better than getting one that we suspect is corrupted finalized in the relay chain.

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've generally made the assumption that if we fail during a runtime upgrade and can't produce a block that we'll just end up doing the same thing when we try to produce another block.

One reason to allow the block is that we are better off if we at least continue producing blocks (e.g. we could vote to modify storage, do another runtime upgrade, etc.)

I've definitely gone back and forth on this, though. It's a situation where we can't do anything good...

}

weight
}
}

#[pallet::storage]
#[pallet::getter(fn is_fully_upgraded)]
/// True if all required migrations have completed
type FullyUpgraded<T: Config> = StorageValue<_, bool, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn migration_state)]
/// MigrationState tracks the progress of a migration.
/// Maps name (Vec<u8>) -> whether or not migration has been completed (bool)
type MigrationState<T: Config> = StorageMap<_, Twox64Concat, Vec<u8>, bool, OptionQuery>;
notlesh marked this conversation as resolved.
Show resolved Hide resolved

#[pallet::genesis_config]
#[derive(Default)]
pub struct GenesisConfig {
pub completed_migrations: Vec<Vec<u8>>,
}

#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
fn build(&self) {
for migration_name in &self.completed_migrations {
<MigrationState<T>>::insert(migration_name, true);
}
}
}

fn perform_runtime_upgrades<T: Config>(available_weight: Weight) -> Weight {
let mut weight: Weight = 0u64.into();

for migration in &T::MigrationsList::get() {
let migration_name = migration.friendly_name();
let migration_name_as_bytes = migration_name.as_bytes();
log::trace!("evaluating migration {}", migration_name);

let migration_done = <MigrationState<T>>::get(migration_name_as_bytes).unwrap_or(false);

if !migration_done {
<Pallet<T>>::deposit_event(Event::MigrationStarted(migration_name_as_bytes.into()));

// when we go overweight, leave a warning... there's nothing we can really do about
// this scenario other than hope that the block is actually accepted.
let available_for_step = if available_weight > weight {
available_weight - weight
} else {
log::error!(
"previous migration went overweight;
ignoring and providing migration {} 0 weight.",
migration_name,
);

0u64.into()
};

log::trace!(
"performing migration {}, avail weight: {}",
notlesh marked this conversation as resolved.
Show resolved Hide resolved
migration_name,
available_for_step
);

let consumed_weight = migration.migrate(available_for_step);
4meta5 marked this conversation as resolved.
Show resolved Hide resolved
<Pallet<T>>::deposit_event(Event::MigrationCompleted(
migration_name_as_bytes.into(),
consumed_weight,
));
<MigrationState<T>>::insert(migration_name_as_bytes, true);
4meta5 marked this conversation as resolved.
Show resolved Hide resolved

weight += consumed_weight;
if weight > available_weight {
log::error!(
"Migration {} consumed more weight than it was given! ({} > {})",
migration_name,
consumed_weight,
available_for_step
);
}
}
}

<FullyUpgraded<T>>::put(true);
weight += T::DbWeight::get().writes(1);
<Pallet<T>>::deposit_event(Event::RuntimeUpgradeCompleted(weight));

weight
}
}
Loading