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

(fix) Fix storage usage bug with migration #4274

Merged
merged 45 commits into from
May 13, 2021

Conversation

EgorKulikov
Copy link
Contributor

@EgorKulikov EgorKulikov commented May 4, 2021

On some further though we are actually able to migrate this inside a single block

Test plan

Sanity test in migrations.rs
Integration test in process_blocks

core/primitives/src/runtime/migration_data.rs Outdated Show resolved Hide resolved
core/primitives/src/views.rs Outdated Show resolved Hide resolved
neard/src/runtime/mod.rs Outdated Show resolved Hide resolved
core/primitives/src/types.rs Outdated Show resolved Hide resolved
EgorKulikov and others added 3 commits May 5, 2021 11:50
@EgorKulikov EgorKulikov linked an issue May 5, 2021 that may be closed by this pull request
core/primitives/src/types.rs Outdated Show resolved Hide resolved
neard/src/runtime/mod.rs Outdated Show resolved Hide resolved
runtime/runtime/src/lib.rs Outdated Show resolved Hide resolved
runtime/runtime/src/lib.rs Outdated Show resolved Hide resolved
Copy link
Collaborator

@frol frol left a comment

Choose a reason for hiding this comment

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

I don't see what prevents this migration from being applied on every single epoch start. Do I miss something?

Another corner case I have in mind is: what if the very first block on the epoch won't be produced? Will this still work fine? I am not very familiar with the block production code.

chain/rosetta-rpc/src/adapters/mod.rs Outdated Show resolved Hide resolved
#[derive(Default)]
pub struct MigrationData {
#[cfg(feature = "protocol_feature_fix_storage_usage")]
pub storage_usage_delta: Vec<(AccountId, u64)>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer to have a struct with named arguments as u64 is not really descriptive

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we use named structure in json this will lead to additional 70kb of data included executable

@EgorKulikov
Copy link
Contributor Author

I don't see what prevents this migration from being applied on every single epoch start. Do I miss something?

migration_data in ApplyState in only Some when protocol version changes, not just epoch

@EgorKulikov
Copy link
Contributor Author

Another corner case I have in mind is: what if the very first block on the epoch won't be produced? Will this still work fine? I am not very familiar with the block production code.

It follows same logic as changes to validators at the start of an epoch

Copy link
Collaborator

@frol frol left a comment

Choose a reason for hiding this comment

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

All my concerns are clarified.

@EgorKulikov This implementation is much cleaner, thank you for re-implementing it to this maintainable state! 🚀

@@ -152,6 +152,9 @@ pub enum StateChangeCause {
/// State change that happens when we update validator accounts. Not associated with with any
/// specific transaction or receipt.
ValidatorAccountsUpdate,
/// State change that is happens due to migration that happens in first block of an epoch
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

Suggested change
/// State change that is happens due to migration that happens in first block of an epoch
/// State change that happens due to state-correction migration that happens in the first block of an epoch

Ideally, reference this PR as an example for the reader, so they don't need to do git-blame to look up what kind of "state-corrections" happen.

const GAS_USED_FOR_STORAGE_USAGE_DELTA_MIGRATION: Gas = 490_000_000_000_000;

pub fn load_migration_data(chain_id: &String) -> MigrationData {
#[cfg(not(feature = "protocol_feature_fix_storage_usage"))]
Copy link
Contributor

Choose a reason for hiding this comment

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

Separate blocks under #[cfg(feature = "protocol_feature_fix_storage_usage")] and #[cfg(not(feature = "protocol_feature_fix_storage_usage"))] could be more readable.

Some(mut account) => {
// Storage usage is saved in state, hence it is nowhere close to max value
// of u64, and maximal delta is 4196, se we can add here without checking
// for overflow
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe checked add would be just easier than long comment why it's not needed :)?

Copy link
Contributor

@olonho olonho left a comment

Choose a reason for hiding this comment

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

LGTM, just few nits.

@@ -2267,6 +2275,135 @@ fn test_epoch_protocol_version_change() {
assert_eq!(protocol_version, PROTOCOL_VERSION + 1);
}

#[test]
#[cfg(feature = "protocol_feature_fix_storage_usage")]
Copy link
Collaborator

Choose a reason for hiding this comment

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

It is better to group the tests for some protocol feature in one mod so that the imports are cleaner, e.g.

#[cfg(test)]
mod protocol_feature_fix_storage_usage_tests {
   ....
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will do, though I have to notice that there is a lot of different kinds of tests in this file and just one mod

assert_eq!(account_test0.storage_usage(), 182);
}
}
{
Copy link
Collaborator

Choose a reason for hiding this comment

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

If it is a different test, please actually make it separate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are kinda the same, just with different chain ids

Copy link
Collaborator

Choose a reason for hiding this comment

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

Then it makes sense to refactor the test to avoid code duplication. Something like

fn test_with_chain_id(chain_id: &str) {
  ....
}

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 did something like that, not sure it is more understandable though

Comment on lines 2293 to 2318
let (encoded_chunk, merkle_paths, receipts) =
create_chunk_on_height(&mut env.clients[0], i);

let mut chain_store =
ChainStore::new(env.clients[0].chain.store().owned_store(), genesis_height);
env.clients[0]
.shards_mgr
.distribute_encoded_chunk(
encoded_chunk.clone(),
merkle_paths.clone(),
receipts.clone(),
&mut chain_store,
)
.unwrap();

let mut block = env.clients[0].produce_block(i).unwrap().unwrap();

let validator_signer = InMemoryValidatorSigner::from_seed(
&"test0".to_string(),
KeyType::ED25519,
&"test0".to_string(),
);

block.mut_header().get_mut().inner_rest.latest_protocol_version =
ProtocolFeature::FixStorageUsage.protocol_version();
block.mut_header().resign(&validator_signer);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you explain what is done here? This looks the same if you were to call TestEnv::produce_block

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe. I started with test_epoch_protocol_version_change as a template for my test. I'll look if this can be refactored

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 need to update protocol version, so no, I cannot just use TestEnv::produce_block

runtime/runtime/src/lib.rs Show resolved Hide resolved
chain/client/tests/process_blocks.rs Show resolved Hide resolved
core/primitives/src/version.rs Outdated Show resolved Hide resolved
EgorKulikov and others added 2 commits May 13, 2021 15:35
Co-authored-by: Bowen Wang <bowenwang1996@users.noreply.github.com>
@near-bulldozer near-bulldozer bot merged commit 473e71b into master May 13, 2021
@near-bulldozer near-bulldozer bot deleted the migration-fix-storage-usage branch May 13, 2021 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Recalculate storage usage based on the actual state
6 participants