Updating workers to newer versions of Rust components
As described in the general Workers page, each worker runs on a specific version of the component it is based on, but it is possible to update a worker to a different version of the same component.
Automatic update
The automatic update mode has no Rust-specific details and works as it is described in the general Workers page.
Manual snapshot-based update
Sometimes the change between two component versions is so large that the only solution to migrate worker's state to a new version is to manually implement this state transfer.
This is done by implementing a save-snapshot
function in the old component and a load-snapshot
function in the new component. The snapshot is an array of bytes and it is the responsibility of the user to ensure that the snapshot is compatible between the two versions.
These functions are defined in the golem:api
WIT package and must be explicitly exported from the component's world
.
Implementing the save snapshot function
The save-snapshot
function must be explicitly exported from the component's world
in it's WIT
file:
package golem:demo;
interface api {
// ...
}
world update-test-v3 {
export golem:api/save-snapshot@1.1.0;
export api;
}
This requires manually adding all the WIT dependencies Golem provides to the component's wit/deps
subdirectory, and importing some of them in the Cargo.toml
file.
Step 1: copy all the WIT dependencies
First copy the whole contents of https://github.com/golemcloud/golem-wit/tree/main/wit/deps (opens in a new tab) to the component's wit/deps
directory.
Step 2: add the relevant dependencies to the Cargo.toml
Add the following dependencies to the Cargo.toml
file:
[package.metadata.component.target.dependencies]
"golem:api" = { path = "wit/deps/golem" }
"golem:rpc" = { path = "wit/deps/wasm-rpc" }
"wasi:clocks" = { path = "wit/deps/clocks" }
"wasi:io" = { path = "wit/deps/io" }
"wasi:http" = { path = "wit/deps/http" }
"wasi:random" = { path = "wit/deps/random" }
"wasi:cli" = { path = "wit/deps/cli" }
"wasi:filesystem" = { path = "wit/deps/filesystem" }
"wasi:sockets" = { path = "wit/deps/sockets" }
Step 3: export the save-snapshot
function
Add the following line to the component's world in its main WIT
file:
export golem:api/save-snapshot@1.1.0;
Step 4: implement the save-snapshot
function in Rust
An example implementation that just writes out an empty array of bytes:
struct Component;
use crate::bindings::exports::golem::api::save_snapshot;
impl save_snapshot::Guest for Component {
fn save() -> Vec<u8> {
let mut result = Vec::new();
// ...
result
}
}
Implementing the load snapshot function
Implementing the load-snapshot
function in the new component requires the same prerequisites as the save-snapshot
function.
Once the WIT dependencies are set up, export the load-snapshot
function in the following way:
export golem:api/load-snapshot@1.1.0;
and then implement it in Rust:
use crate::bindings::exports::golem::api::{load_snapshot};
struct Component;
impl load_snapshot::Guest for Component {
fn load(_bytes: Vec<u8>) -> Result<(), String> {
Err("Invalid snapshot".to_string())
}
}
Note that the load-snapshot
function can fail, indicating that it cannot load a previously saved snapshot. When the upgrade logic detects this, it reverts the worker to the previous version.
If the snapshot can be loaded, the load-snapshot
function must set up the worker's global state and return with Ok(())
.