-
Notifications
You must be signed in to change notification settings - Fork 2.6k
[Feature] Sequential migration execution for try-runtime #12319
Changes from 17 commits
bfce676
efc3d29
5de382e
3f3fdda
2c4b856
4c455a8
7522aac
2e13236
551f04a
7bd6c17
7676a47
da7dd03
d2efbba
626c5bf
20c0793
98fc5a3
3d2098a
d7102dc
1537c99
6eb790e
d7d6078
57d0f0b
11c2a6b
759b851
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 |
---|---|---|
|
@@ -22,7 +22,7 @@ use impl_trait_for_tuples::impl_for_tuples; | |
use sp_runtime::traits::AtLeast32BitUnsigned; | ||
use sp_std::prelude::*; | ||
|
||
#[cfg(feature = "try-runtime")] | ||
#[cfg(all(feature = "try-runtime", test))] | ||
use codec::{Decode, Encode}; | ||
|
||
/// The block initialization trait. | ||
|
@@ -165,27 +165,44 @@ pub trait OnRuntimeUpgrade { | |
#[cfg_attr(all(feature = "tuples-96", not(feature = "tuples-128")), impl_for_tuples(96))] | ||
#[cfg_attr(feature = "tuples-128", impl_for_tuples(128))] | ||
impl OnRuntimeUpgrade for Tuple { | ||
#[cfg(not(feature = "try-runtime"))] | ||
fn on_runtime_upgrade() -> Weight { | ||
let mut weight = Weight::zero(); | ||
for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* ); | ||
weight | ||
} | ||
|
||
#[cfg(feature = "try-runtime")] | ||
/// We are executing pre- and post-checks sequentially in order to be able to test several | ||
/// consecutive migrations for the same pallet without errors. Therefore pre and post upgrade | ||
/// hooks for tuples are a noop. | ||
fn on_runtime_upgrade() -> Weight { | ||
let mut weight = Weight::zero(); | ||
for_tuples!( #( | ||
let _guard = frame_support::StorageNoopGuard::default(); | ||
// expected unwrap: we want to panic if any checks fail right here right now. | ||
let state = Tuple::pre_upgrade().unwrap(); | ||
drop(_guard); | ||
|
||
weight = weight.saturating_add(Tuple::on_runtime_upgrade()); | ||
|
||
let _guard = frame_support::StorageNoopGuard::default(); | ||
// expected unwrap: we want to panic if any checks fail right here right now. | ||
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. bad indentation (my bad) |
||
Tuple::post_upgrade(state).unwrap(); | ||
ruseinov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
drop(_guard); | ||
)* ); | ||
weight | ||
} | ||
|
||
#[cfg(feature = "try-runtime")] | ||
/// noop | ||
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { | ||
let mut state: Vec<Vec<u8>> = Vec::default(); | ||
for_tuples!( #( state.push(Tuple::pre_upgrade()?); )* ); | ||
Ok(state.encode()) | ||
Ok(Vec::new()) | ||
} | ||
|
||
#[cfg(feature = "try-runtime")] | ||
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { | ||
let state: Vec<Vec<u8>> = Decode::decode(&mut state.as_slice()) | ||
.expect("the state parameter should be the same as pre_upgrade generated"); | ||
let mut state_iter = state.into_iter(); | ||
for_tuples!( #( Tuple::post_upgrade( | ||
state_iter.next().expect("the state parameter should be the same as pre_upgrade generated") | ||
)?; )* ); | ||
/// noop | ||
fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { | ||
Ok(()) | ||
} | ||
} | ||
|
@@ -342,7 +359,9 @@ mod tests { | |
|
||
#[test] | ||
fn on_initialize_and_on_runtime_upgrade_weight_merge_works() { | ||
use sp_io::TestExternalities; | ||
struct Test; | ||
|
||
impl OnInitialize<u8> for Test { | ||
fn on_initialize(_n: u8) -> Weight { | ||
Weight::from_ref_time(10) | ||
|
@@ -354,8 +373,10 @@ mod tests { | |
} | ||
} | ||
|
||
assert_eq!(<(Test, Test)>::on_initialize(0), Weight::from_ref_time(20)); | ||
assert_eq!(<(Test, Test)>::on_runtime_upgrade(), Weight::from_ref_time(40)); | ||
TestExternalities::default().execute_with(|| { | ||
assert_eq!(<(Test, Test)>::on_initialize(0), Weight::from_ref_time(20)); | ||
assert_eq!(<(Test, Test)>::on_runtime_upgrade(), Weight::from_ref_time(40)); | ||
}); | ||
} | ||
|
||
#[test] | ||
|
@@ -417,16 +438,26 @@ mod tests { | |
#[cfg(feature = "try-runtime")] | ||
#[test] | ||
fn on_runtime_upgrade_tuple() { | ||
use frame_support::parameter_types; | ||
use sp_io::TestExternalities; | ||
|
||
struct Test1; | ||
struct Test2; | ||
struct Test3; | ||
|
||
parameter_types! { | ||
static Test1Assertions: u8 = 0; | ||
static Test2Assertions: u8 = 0; | ||
static Test3Assertions: u8 = 0; | ||
} | ||
|
||
impl OnRuntimeUpgrade for Test1 { | ||
fn pre_upgrade() -> Result<Vec<u8>, &'static str> { | ||
Ok("Test1".encode()) | ||
} | ||
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { | ||
let s: String = Decode::decode(&mut state.as_slice()).unwrap(); | ||
Test1Assertions::mutate(|val| *val += 1); | ||
assert_eq!(s, "Test1"); | ||
Ok(()) | ||
} | ||
|
@@ -437,6 +468,7 @@ mod tests { | |
} | ||
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { | ||
let s: u32 = Decode::decode(&mut state.as_slice()).unwrap(); | ||
Test2Assertions::mutate(|val| *val += 1); | ||
assert_eq!(s, 100); | ||
Ok(()) | ||
} | ||
|
@@ -447,60 +479,43 @@ mod tests { | |
} | ||
fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { | ||
let s: bool = Decode::decode(&mut state.as_slice()).unwrap(); | ||
Test3Assertions::mutate(|val| *val += 1); | ||
assert_eq!(s, true); | ||
Ok(()) | ||
} | ||
} | ||
|
||
type TestEmpty = (); | ||
let origin_state = <TestEmpty as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
let states: Vec<Vec<u8>> = Decode::decode(&mut origin_state.as_slice()).unwrap(); | ||
assert!(states.is_empty()); | ||
<TestEmpty as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
|
||
type Test1Tuple = (Test1,); | ||
let origin_state = <Test1Tuple as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
let states: Vec<Vec<u8>> = Decode::decode(&mut origin_state.as_slice()).unwrap(); | ||
assert_eq!(states.len(), 1); | ||
assert_eq!( | ||
<String as Decode>::decode(&mut states[0].as_slice()).unwrap(), | ||
"Test1".to_owned() | ||
); | ||
<Test1Tuple as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
|
||
type Test123 = (Test1, Test2, Test3); | ||
let origin_state = <Test123 as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
let states: Vec<Vec<u8>> = Decode::decode(&mut origin_state.as_slice()).unwrap(); | ||
assert_eq!( | ||
<String as Decode>::decode(&mut states[0].as_slice()).unwrap(), | ||
"Test1".to_owned() | ||
); | ||
assert_eq!(<u32 as Decode>::decode(&mut states[1].as_slice()).unwrap(), 100u32); | ||
assert_eq!(<bool as Decode>::decode(&mut states[2].as_slice()).unwrap(), true); | ||
<Test123 as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
|
||
type Test321 = (Test3, Test2, Test1); | ||
let origin_state = <Test321 as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
let states: Vec<Vec<u8>> = Decode::decode(&mut origin_state.as_slice()).unwrap(); | ||
assert_eq!(<bool as Decode>::decode(&mut states[0].as_slice()).unwrap(), true); | ||
assert_eq!(<u32 as Decode>::decode(&mut states[1].as_slice()).unwrap(), 100u32); | ||
assert_eq!( | ||
<String as Decode>::decode(&mut states[2].as_slice()).unwrap(), | ||
"Test1".to_owned() | ||
); | ||
<Test321 as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
|
||
type TestNested123 = (Test1, (Test2, Test3)); | ||
let origin_state = <TestNested123 as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
let states: Vec<Vec<u8>> = Decode::decode(&mut origin_state.as_slice()).unwrap(); | ||
assert_eq!( | ||
<String as Decode>::decode(&mut states[0].as_slice()).unwrap(), | ||
"Test1".to_owned() | ||
); | ||
// nested state for (Test2, Test3) | ||
let nested_states: Vec<Vec<u8>> = Decode::decode(&mut states[1].as_slice()).unwrap(); | ||
assert_eq!(<u32 as Decode>::decode(&mut nested_states[0].as_slice()).unwrap(), 100u32); | ||
assert_eq!(<bool as Decode>::decode(&mut nested_states[1].as_slice()).unwrap(), true); | ||
<TestNested123 as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
TestExternalities::default().execute_with(|| { | ||
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. This test is not quite needed anymore as we don't combine the outputs anymore. 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 mean, it is nice that we're testing For example, with the new logic, I would test the following:
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 don’t see a reason to introduce those tbh. try-runtime methods are feature-gated, so are the tests. We can’t test feature-gated stuff in a non-feature-gated test. |
||
type TestEmpty = (); | ||
let origin_state = <TestEmpty as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
assert!(origin_state.is_empty()); | ||
<TestEmpty as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
|
||
type Test1Tuple = (Test1,); | ||
let origin_state = <Test1Tuple as OnRuntimeUpgrade>::pre_upgrade().unwrap(); | ||
assert!(origin_state.is_empty()); | ||
<Test1Tuple as OnRuntimeUpgrade>::post_upgrade(origin_state).unwrap(); | ||
assert_eq!(Test1Assertions::get(), 0); | ||
<Test1Tuple as OnRuntimeUpgrade>::on_runtime_upgrade(); | ||
assert_eq!(Test1Assertions::take(), 1); | ||
|
||
type Test123 = (Test1, Test2, Test3); | ||
<Test123 as OnRuntimeUpgrade>::on_runtime_upgrade(); | ||
assert_eq!(Test1Assertions::take(), 1); | ||
assert_eq!(Test2Assertions::take(), 1); | ||
assert_eq!(Test3Assertions::take(), 1); | ||
|
||
type Test321 = (Test3, Test2, Test1); | ||
<Test321 as OnRuntimeUpgrade>::on_runtime_upgrade(); | ||
assert_eq!(Test1Assertions::take(), 1); | ||
assert_eq!(Test2Assertions::take(), 1); | ||
assert_eq!(Test3Assertions::take(), 1); | ||
|
||
type TestNested123 = (Test1, (Test2, Test3)); | ||
<TestNested123 as OnRuntimeUpgrade>::on_runtime_upgrade(); | ||
assert_eq!(Test1Assertions::take(), 1); | ||
assert_eq!(Test2Assertions::take(), 1); | ||
assert_eq!(Test3Assertions::take(), 1); | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to just have some new
on_runtime_upgrade_with_pre_and_post
that returns a result. (the name is for sure shit!)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be fair I'm not sure if this is needed, try-runtime is used for testing. If any of the checks panic - it's they need to be fixed. Seems like extra complexity to come up with another method that does have a Result return type just for the sake of a couple of unwraps.
On the other hand it hurts readability a little bit.
If I'm being honest I just don't want to add yet another method to this interface and make things more confusing for future implementors.
Correct me if I'm wrong, you are proposing to add an extra method on
OnRuntimeUpgrade
whentry-runtime
.The one that's going to be called instead of
on_runtime_upgrade
fortry-runtime
scenarios.Alternatively we could introduce this method and still call it in
on_runtime_upgrade
, but then we'd have to unwrap anyway, so there is no point.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If @bkchr's point is to only prevent
unwrap()
, I disagree.If there's more benefit in the new method, I am all ears.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My point here was to prevent the unwrap. However, if we keep it. I demand a
expect
that provides more context, at least like the index of the tuple to have some idea what failed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've discussed it before and I think the real answer here is
RUST_BACKTRACE=1
, which gives you exactly the failing line. That being said, @ruseinov can you please update the unwraps to expect?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!