From 45cf343edcae4132cb1541d523be6e2022e776bb Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 17:37:34 +0100 Subject: [PATCH] feat: Create stable memory data (#4524) # Motivation The code to store accounts in stable structures is ready. Here it is enabled. Note: This PR does NOT cover migrating between stable structures and the heap. That is for another PR. # Changes - Enable creating state in stable structures - Persist stats (such as number of accounts) across upgrades. # Tests - An e2e test is included # Todos - [ ] Add entry to changelog (if necessary). --- .github/workflows/build.yml | 32 ++++++++++++++++++- rs/backend/src/accounts_store.rs | 1 + .../accounts_in_unbounded_stable_btree_map.rs | 1 - .../schema/label_serialization.rs | 1 - rs/backend/src/main.rs | 20 ++++++++++-- rs/backend/src/state.rs | 2 +- rs/backend/src/state/partitions/schemas.rs | 5 +-- 7 files changed, 51 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9067cf12424..ab0bbba231e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -152,6 +152,36 @@ jobs: with: name: test-upgrade-map-dfx.log path: test-upgrade-map-dfx.log + test-upgrade-stable: + needs: build + runs-on: ubuntu-20.04 + timeout-minutes: 40 + steps: + - name: Checkout nns-dapp + uses: actions/checkout@v4 + - name: Get nns-dapp_test + uses: actions/download-artifact@v4 + with: + name: out + path: out + - name: Install ic-wasm + uses: ./.github/actions/install_ic_wasm + - name: Install dfx + uses: dfinity/setup-dfx@main + - name: Install tools + run: | + sudo apt-get update -yy && sudo apt-get install -yy moreutils && command -v sponge + cargo binstall --no-confirm "idl2json_cli@$(jq -r .defaults.build.config.IDL2JSON_VERSION dfx.json)" && idl2json --version + - name: Start dfx + run: dfx start --clean --background &>test-upgrade-stable-dfx.log + - name: Downgrade nns-dapp to prod and upgrade back again + run: ./scripts/nns-dapp/migration-test --schema1 AccountsInStableMemory --schema2 AccountsInStableMemory --accounts 1000 --chunk 100 + - name: Upload dfx logs + if: failure() + uses: actions/upload-artifact@v3 + with: + name: test-upgrade-stable-dfx.log + path: test-upgrade-stable-dfx.log test-test-account-api: needs: build runs-on: ubuntu-20.04 @@ -495,7 +525,7 @@ jobs: ) | tee -a $GITHUB_STEP_SUMMARY (( wasm_size <= max_size )) || { echo "The WASM is too large" ; exit 1 ; } build-pass: - needs: ["build", "test-playwright-e2e-shard-1-of-2", "test-playwright-e2e-shard-2-of-2", "test-rest", "network_independent_wasm", "aggregator_test", "assets", "test-downgrade-upgrade", "test-test-account-api"] + needs: ["build", "test-playwright-e2e-shard-1-of-2", "test-playwright-e2e-shard-2-of-2", "test-rest", "network_independent_wasm", "aggregator_test", "assets", "test-downgrade-upgrade", "test-test-account-api", "test-upgrade-map", "test-upgrade-stable"] if: ${{ always() }} runs-on: ubuntu-20.04 steps: diff --git a/rs/backend/src/accounts_store.rs b/rs/backend/src/accounts_store.rs index f4f51a53f6a..bf3a56980bf 100644 --- a/rs/backend/src/accounts_store.rs +++ b/rs/backend/src/accounts_store.rs @@ -1554,6 +1554,7 @@ impl StableState for AccountsStore { &self.multi_part_transactions_processor, &self.last_ledger_sync_timestamp_nanos, &self.neurons_topped_up_count, + Some(&self.accounts_db_stats), )) .into_bytes() .unwrap() diff --git a/rs/backend/src/accounts_store/schema/accounts_in_unbounded_stable_btree_map.rs b/rs/backend/src/accounts_store/schema/accounts_in_unbounded_stable_btree_map.rs index 2b85a6ce660..2d3d06dac8e 100644 --- a/rs/backend/src/accounts_store/schema/accounts_in_unbounded_stable_btree_map.rs +++ b/rs/backend/src/accounts_store/schema/accounts_in_unbounded_stable_btree_map.rs @@ -28,7 +28,6 @@ where M: Memory, { /// Creates a new, empty database. - #[cfg(test)] pub fn new(memory: M) -> Self { Self { accounts: StableBTreeMap::new(memory), diff --git a/rs/backend/src/accounts_store/schema/label_serialization.rs b/rs/backend/src/accounts_store/schema/label_serialization.rs index a193d84ae4d..ac1115abc01 100644 --- a/rs/backend/src/accounts_store/schema/label_serialization.rs +++ b/rs/backend/src/accounts_store/schema/label_serialization.rs @@ -48,7 +48,6 @@ impl TryFrom for SchemaLabel { fn try_from(value: u32) -> Result { match value { 0 => Ok(Self::Map), - #[cfg(test)] 1 => Ok(Self::AccountsInStableMemory), other => Err(SchemaLabelError::InvalidLabel(other)), } diff --git a/rs/backend/src/main.rs b/rs/backend/src/main.rs index a64144d5cc2..7bef7161829 100644 --- a/rs/backend/src/main.rs +++ b/rs/backend/src/main.rs @@ -5,11 +5,12 @@ use crate::accounts_store::{ RegisterHardwareWalletRequest, RegisterHardwareWalletResponse, RenameCanisterRequest, RenameCanisterResponse, RenameSubAccountRequest, RenameSubAccountResponse, }; -use crate::arguments::{set_canister_arguments, CanisterArguments}; +use crate::arguments::{set_canister_arguments, CanisterArguments, CANISTER_ARGUMENTS}; use crate::assets::{hash_bytes, insert_asset, insert_tar_xz, Asset}; use crate::perf::PerformanceCount; use crate::periodic_tasks_runner::run_periodic_tasks; use crate::state::{StableState, State, STATE}; + pub use candid::{CandidType, Deserialize}; use dfn_candid::{candid, candid_one}; use dfn_core::{over, over_async}; @@ -40,11 +41,23 @@ type Cycles = u128; #[init] fn init(args: Option) { - println!("init with args: {args:#?}"); + println!("START init with args: {args:#?}"); set_canister_arguments(args); perf::record_instruction_count("init after set_canister_arguments"); + CANISTER_ARGUMENTS.with(|args| { + let args = args.borrow(); + let schema = args.schema.unwrap_or_default(); + let stable_memory = DefaultMemoryImpl::default(); + let state = State::new(schema, stable_memory); + STATE.with(|s| { + s.replace(state); + println!("init state after: {s:?}"); + }); + }); + // Legacy: assets::init_assets(); perf::record_instruction_count("init stop"); + println!("END init with args"); } /// Redundant function, never called but required as this is `main.rs`. @@ -71,7 +84,7 @@ fn pre_upgrade() { #[post_upgrade] fn post_upgrade(args: Option) { - println!("post_upgrade with args: {args:#?}"); + println!("START post_upgrade with args: {args:#?}"); // Saving the instruction counter now will not have the desired effect // as the storage is about to be wiped out and replaced with stable memory. let counter_before = PerformanceCount::new("post_upgrade start"); @@ -86,6 +99,7 @@ fn post_upgrade(args: Option) { perf::record_instruction_count("post_upgrade after set_canister_arguments"); assets::init_assets(); perf::record_instruction_count("post_upgrade stop"); + println!("END post-upgrade"); } #[export_name = "canister_query http_request"] diff --git a/rs/backend/src/state.rs b/rs/backend/src/state.rs index 7f8b9b3a6b8..99ac2173c22 100644 --- a/rs/backend/src/state.rs +++ b/rs/backend/src/state.rs @@ -113,7 +113,7 @@ thread_local! { impl State { /// Creates new state with the specified schema. - #[cfg(test)] + #[must_use] pub fn new(schema: SchemaLabel, memory: DefaultMemoryImpl) -> Self { match schema { SchemaLabel::Map => { diff --git a/rs/backend/src/state/partitions/schemas.rs b/rs/backend/src/state/partitions/schemas.rs index 6a492b6935e..7a4f26d136e 100644 --- a/rs/backend/src/state/partitions/schemas.rs +++ b/rs/backend/src/state/partitions/schemas.rs @@ -1,8 +1,6 @@ //! Sets up memory for a given schema. -#[cfg(test)] use super::DefaultMemoryImpl; use super::{PartitionType, Partitions}; -#[cfg(test)] use crate::accounts_store::schema::SchemaLabelBytes; use crate::state::SchemaLabel; use ic_cdk::println; @@ -13,7 +11,6 @@ impl Partitions { /// Writes the schema label to the metadata partition. /// /// Note: This MUST be called by every constructor. - #[cfg(test)] fn set_schema_label(&self, schema: SchemaLabel) { let schema_label_bytes = SchemaLabelBytes::from(schema); println!("Set schema label bytes to: {:?}", schema_label_bytes); @@ -36,7 +33,7 @@ impl Partitions { /// Gets the memory partitioned appropriately for the given schema. /// /// If a schema uses raw memory, the memory is returned. - #[cfg(test)] + #[must_use] pub fn new_with_schema(memory: DefaultMemoryImpl, schema: SchemaLabel) -> Partitions { match schema { SchemaLabel::Map => panic!("Map schema does not use partitions"),