Hi everyone,
This post is about a new rust library for canisters: canister-tools.
The main features of this library make it easy to handle upgrades of global-data-variables in the main-heap-memory and at the same time simple to create serialized snapshots of the global-data, download the snapshots in chunks, and can upload a serialized snapshot in chunks and then load it onto the global variable(s) giving maximum controll over the global-heap-data.
The library works with the memory-manager’s virtual-memories feature of the ic-stable-structures library so each global variable is registered with a unique memory-id and is serialized it it’s own corresponding memory-id during upgrades. This makes it so that a canister can use stable-structures like a StableBTreeMap at the same time as holding global-variables in the main-heap.
This library is compatible with global-variables in a thread_local!
with a RefCell
as is the custom when writing rust canisters.
Usage of the library is as follows:
#[derive(Default, serde::Serialize, serde::Deserialize)]
struct Data {
...
}
thread_local! {
static DATA: RefCell<Data> = RefCell::new(Data::default());
}
const DATA_MEMORY_ID: MemoryId = MemoryId::new(0);
#[init]
fn init() {
canister_tools::init(&DATA, DATA_MEMORY_ID);
}
#[pre_upgrade]
fn pre_upgrade() {
canister_tools::pre_upgrade();
}
#[post_upgrade]
fn post_upgrade() {
canister_tools::post_upgrade(&DATA, DATA_MEMORY_ID, None::<fn(Data) -> Data>);
}
When updating the global-variable struct fields or type during an upgrade, use the optional old_as_new convert function to deserialize as the old struct, then convert to the new struct using the function, then load onto the global-variable. Or pass None if the type stays the same.
Take a look at the docs for the candid file of the controller methods that can be used to create snapshots, download snapshots, and even upload snapshots onto the global-variables in the heap.
By default, types that implement serde’s Serialize and Deserialize traits are compatible out-of-the-box and use the bincode serialization format. The library is compatible with a different or custom serialization format by implementing the canister_tools::Serializable trait on the types.
I’m glad to help people use this library, let me know if there are questions.
:Levi.