Skip to content

Commit

Permalink
feat: Support wasm_memory_persistence for canister upgrades (#3890)
Browse files Browse the repository at this point in the history
  • Loading branch information
luc-blaeser authored and rikonor committed Feb 3, 2025
1 parent e80551c commit c6aab05
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

# UNRELEASED

### feat: Support for Motoko's enhanced orthogonal persistence.

Support Motoko's enhanced orthogonal persistence by automatically setting the canister upgrade option `wasm_memory_persistence` based on the Wasm metadata.

### feat: PocketIC state

`dfx start --pocketic` no longer requires `--clean`, and can persist replica state between runs.
Expand Down
3 changes: 3 additions & 0 deletions e2e/assets/wasm_memory_persistence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`classical-actor.wasm` is built from `actor.mo` with `moc -o classical-actor.wasm actor.mo`.
`enhanced-actor.wasm` is built from `actor.mo` with `moc -o enhanced-actor.wasm --enhanced-orthogonal-persistence actor.mo`.
`actor.did` is obtained by `moc --idl actor.mo`.
3 changes: 3 additions & 0 deletions e2e/assets/wasm_memory_persistence/actor.did
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
service : {
getVersion: () -> (nat) query;
}
12 changes: 12 additions & 0 deletions e2e/assets/wasm_memory_persistence/actor.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Prim "mo:prim";

actor {
stable var version = 0;

version += 1;
Prim.debugPrint("Deployed actor version " # debug_show (version));

public query func getVersion() : async Nat {
return version;
};
};
Binary file added e2e/assets/wasm_memory_persistence/actor.wasm
Binary file not shown.
Binary file not shown.
9 changes: 9 additions & 0 deletions e2e/assets/wasm_memory_persistence/dfx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"canisters": {
"test": {
"type": "custom",
"candid": "actor.did",
"wasm": "classical-actor.wasm"
}
}
}
Binary file not shown.
66 changes: 66 additions & 0 deletions e2e/tests-dfx/wasm_memory_persistence.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env bats

load ../utils/_

setup() {
standard_setup

dfx_new test
}

teardown() {
dfx_stop

standard_teardown
}

@test "migrate Motoko from classical persistence to classical persistence" {
install_asset wasm_memory_persistence
dfx_start
dfx deploy
dfx deploy --upgrade-unchanged
assert_command dfx canister call test getVersion '()'
assert_match "(2 : nat)"
}

@test "migrate Motoko from classical persistence to enhanced orthogonal persistence" {
install_asset wasm_memory_persistence
dfx_start
dfx deploy
jq '.canisters.test.wasm="enhanced-actor.wasm"' dfx.json | sponge dfx.json
dfx deploy
assert_command dfx canister call test getVersion '()'
assert_match "(2 : nat)"
}

@test "migrate Motoko from enhanced orthogonal persistence to enhanced orthogonal persistence" {
install_asset wasm_memory_persistence
dfx_start
jq '.canisters.test.wasm="enhanced-actor.wasm"' dfx.json | sponge dfx.json
dfx deploy
jq '.canisters.test.wasm="enhanced-actor.wasm"' dfx.json | sponge dfx.json
dfx deploy --upgrade-unchanged
assert_command dfx canister call test getVersion '()'
assert_match "(2 : nat)"
}

@test "failing Motoko downgrade from enhanced orthogonal persistence to classical persistence" {
install_asset wasm_memory_persistence
dfx_start
jq '.canisters.test.wasm="enhanced-actor.wasm"' dfx.json | sponge dfx.json
dfx deploy
jq '.canisters.test.wasm="classical-actor.wasm"' dfx.json | sponge dfx.json
assert_command_fail dfx deploy
assert_match "The \`wasm_memory_persistence: opt Keep\` upgrade option requires that the new canister module supports enhanced orthogonal persistence."
}

@test "re-install Motoko enhanced orthogonal persistence with classical persistence" {
install_asset wasm_memory_persistence
dfx_start
jq '.canisters.test.wasm="enhanced-actor.wasm"' dfx.json | sponge dfx.json
dfx deploy
jq '.canisters.test.wasm="classical-actor.wasm"' dfx.json | sponge dfx.json
echo yes | dfx canister install test --mode=reinstall
assert_command dfx canister call test getVersion '()'
assert_match "(1 : nat)"
}
13 changes: 11 additions & 2 deletions src/dfx/src/lib/operations/canister/install_canister.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ use dfx_core::config::model::network_descriptor::NetworkDescriptor;
use dfx_core::identity::CallSender;
use fn_error_context::context;
use ic_agent::Agent;
use ic_utils::interfaces::management_canister::builders::InstallMode;
use ic_utils::interfaces::management_canister::builders::{
CanisterUpgradeOptions, InstallMode, WasmMemoryPersistence,
};
use ic_utils::interfaces::ManagementCanister;
use ic_utils::Argument;
use itertools::Itertools;
Expand Down Expand Up @@ -63,9 +65,16 @@ pub async fn install_canister(
"Previously installed module hash: {:?}",
installed_module_hash.as_ref().map(hex::encode)
);
let wasm_memory_persistence =
read_module_metadata(agent, canister_id, "enhanced-orthogonal-persistence")
.await
.map(|_| WasmMemoryPersistence::Keep);
let mode = mode.unwrap_or_else(|| {
if installed_module_hash.is_some() {
InstallMode::Upgrade(None)
InstallMode::Upgrade(Some(CanisterUpgradeOptions {
wasm_memory_persistence,
skip_pre_upgrade: None,
}))
} else {
InstallMode::Install
}
Expand Down

0 comments on commit c6aab05

Please sign in to comment.