Worker to Worker communication
Concept
Golem allows creating and invoking workers through its REST API or command-line interface, using the rules defined on the component interface page. These methods are for working with Golem workers from the outside world. However, the public API for calling one worker from another is not very convenient.
Golem instead introduces a new way to do worker-to-worker communication, using a method called WASM RPC. This consists of the following building blocks:
- Golem provides a host resource for workers, which can be imported into any Golem project and used to invoke a remote worker by passing the target worker, the name of the invoked function and its parameters in a dynamic way using a data type called
wit-value
. - A CLI tool called
wasm-rpc-stubgen
can take any Golem worker, and generate a pair of a WIT definition and a WASM component implementing a "stub" for remotely calling that given worker. The generated stub internally uses the above mentioned low-level invocation API, providing a typed interface for working with a remote Golem worker. - The generated stub can be composed with other Golem workers using standard WASM tooling to get a resulting WASM ready to be uploaded as a Golem component, capable of typed worker-to-worker communication.
Details
Low-level interface
The low-level host interface this technique uses is defined in wasm-rpc.wit (opens in a new tab) as the following:
package golem:rpc@0.1.0;
interface types {
use wasi:io/poll@0.2.0.{pollable};
type node-index = s32;
record wit-value {
nodes: list<wit-node>,
}
variant wit-node {
record-value(list<node-index>),
variant-value(tuple<u32, option<node-index>>),
enum-value(u32),
flags-value(list<bool>),
tuple-value(list<node-index>),
list-value(list<node-index>),
option-value(option<node-index>),
result-value(result<option<node-index>, option<node-index>>),
prim-u8(u8),
prim-u16(u16),
prim-u32(u32),
prim-u64(u64),
prim-s8(s8),
prim-s16(s16),
prim-s32(s32),
prim-s64(s64),
prim-float32(float32),
prim-float64(float64),
prim-char(char),
prim-bool(bool),
prim-string(string),
}
record uri {
value: string,
}
variant rpc-error {
protocol-error(string),
denied(string),
not-found(string),
remote-internal-error(string)
}
resource wasm-rpc {
constructor(location: uri);
invoke-and-await: func(function-name: string, function-params: list<wit-value>) -> result<wit-value, rpc-error>;
invoke: func(function-name: string, function-params: list<wit-value>) -> result<_, rpc-error>;
async-invoke-and-await: func(function-name: string, function-params: list<wit-value>) -> future-invoke-result;
}
}
world wit-value {
import types;
}
Worker URI
The worker URI that has to be passed to the remote interface as a parameter identifies a specific Golem worker, consisting of the component ID and the worker name.
It can be constructed by hand using the pattern worker://component-id/worker-name
, and a running worker can get its own URI using the Golem-specific host function golem:api/host.{get-self-uri}
.
Stub generator
The latest version of golem-cli
and golem-cloud-cli
contains a stubgen
subcommand which implements a set of tools to generate and integrate WASM RPC stubs:
WASM RPC stub generator
Usage: golem-cli stubgen [OPTIONS] <COMMAND>
Commands:
generate Generate a Rust RPC stub crate for a WASM component
build Build an RPC stub for a WASM component
add-stub-dependency Adds a generated stub as a dependency to another WASM component
compose Compose a WASM component with a generated stub WASM
initialize-workspace Initializes a Golem-specific cargo-make configuration in a Cargo workspace for automatically generating stubs and composing results
help Print this message or the help of the given subcommand(s)
Options:
-v, --verbose... Increase logging verbosity
-q, --quiet... Decrease logging verbosity
-h, --help Print help
stubgen generate
Generate a Rust RPC stub crate for a WASM component
Usage: golem-cli stubgen generate [OPTIONS] --source-wit-root <SOURCE_WIT_ROOT> --dest-crate-root <DEST_CRATE_ROOT>
Options:
-s, --source-wit-root <SOURCE_WIT_ROOT>
The root directory of the component's WIT definition to be called via RPC
-d, --dest-crate-root <DEST_CRATE_ROOT>
The target path to generate a new stub crate to
-w, --world <WORLD>
The world name to be used in the generated stub crate. If there is only a single world in the source root package, no need to specify
--stub-crate-version <STUB_CRATE_VERSION>
The crate version of the generated stub crate [default: 0.0.1]
--wasm-rpc-path-override <WASM_RPC_PATH_OVERRIDE>
The path to the `wasm-rpc` crate to be used in the generated stub crate. If not specified, the latest version of `wasm-rpc` will be used
The parameters have the following meaning:
--source-wit-root
must point to the directory of the WIT specification of the worker to be called remotely. This is usually awit
subdirectory in the worker's component's source code.--dest-crate-root
is a new directory where the generated RPC stub will be put. The tool's current version does not generate a WASM file directly, instead it outputs a complete Rust crate which can be compiled viacargo component build
(as described on the Rust page) or included in a Rust workspace.--world
can be used to select the exported world from the source WIT, if there are more than one. If there is only one world defined, there is no need to use this parameter.--stub-crate-version
sets the generated Rust crate's version to the given value.--wasm-rpc-path-override
is useful when working on thewasm-rpc
crate itself, to point the dependency to a local path instead of using the published version.
The generated crate will contain a new WIT file - named wit/_stub.wit
- which contains all the exported functions, interfaces and resources from the source worker's WIT specification, but each of them is packaged into an exported resource representing the remote connection to a specific remote worker. The generated Rust code implements these exported resources using the low-level invocation interface defined above.
For example if the original worker's WIT exported an interface and a resource through a function:
package golem:component;
interface api {
resource counter {
constructor(name: string);
inc-by: func(value: u64);
get-value: func() -> u64;
merge-counters: static func(counter1: counter, counter2: counter, name: string) -> counter;
}
get-all-counters: func() -> list<counter>;
}
world my-world {
export api;
}
The generated stub WIT specification for the above interface will be:
package golem:component-stub;
interface stub-my-world {
use golem:rpc/types@0.1.0.{uri};
resource api {
constructor(location: uri);
get-all-counters: func() -> list<counter>;
}
resource counter {
constructor(location: uri, name: string);
inc-by: func(value: u64);
get-value: func() -> u64;
merge-counters: static func(counter1: counter, counter2: counter, name: string) -> counter;
}
}
world wasm-rpc-stub-my-world {
export stub-my-world;
}
Each exported interface and resource becomes a new resource, accepting a remote worker URI as a parameter.
stubgen build
Build an RPC stub for a WASM component
Usage: golem-cli stubgen build [OPTIONS] --source-wit-root <SOURCE_WIT_ROOT> --dest-wasm <DEST_WASM> --dest-wit-root <DEST_WIT_ROOT>
Options:
-s, --source-wit-root <SOURCE_WIT_ROOT>
The root directory of the component's WIT definition to be called via RPC
--dest-wasm <DEST_WASM>
The name of the stub WASM file to be generated
--dest-wit-root <DEST_WIT_ROOT>
The directory name where the generated WIT files should be placed
-w, --world <WORLD>
The world name to be used in the generated stub crate. If there is only a single world in the source root package, no need to specify
--stub-crate-version <STUB_CRATE_VERSION>
The crate version of the generated stub crate [default: 0.0.1]
--wasm-rpc-path-override <WASM_RPC_PATH_OVERRIDE>
The path to the `wasm-rpc` crate to be used in the generated stub crate. If not specified, the latest version of `wasm-rpc` will be used. It needs to be an **absolute path**
Similar to the stubgen generate
subcommand, but instead of generating the source code for a Rust crate, it builds the stub WASM directly.
The parameters have the following meaning:
--source-wit-root
must point to the directory of the WIT specification of the worker to be called remotely. This is usually awit
subdirectory in the worker's component's source code.--dest-wasm
is the target WASM file to be generated.--dest-wit-root
is the target directory where the stub's WIT file and its dependencies will be generated into.--world
can be used to select the exported world from the source WIT, if there are more than one. If there is only one world defined, there is no need to use this parameter.--stub-crate-version
sets the generated Rust crate's version to the given value.--wasm-rpc-path-override
is useful when working on thewasm-rpc
crate itself, to point the dependency to a local path instead of using the published version. It must be an absolute path.
See the stubgen generate
command's description above for more details about the generated WIT interface.
stubgen add-stub-dependency
Adds a generated stub as a dependency to another WASM component
Usage: golem-cli stubgen add-stub-dependency [OPTIONS] --stub-wit-root <STUB_WIT_ROOT> --dest-wit-root <DEST_WIT_ROOT>
Options:
-s, --stub-wit-root <STUB_WIT_ROOT> The WIT root generated by either `generate` or `build` command
-d, --dest-wit-root <DEST_WIT_ROOT> The WIT root of the component where the stub should be added as a dependency
-o, --overwrite This command would not do anything if it detects that it would change an existing WIT file's contents at the destination. With this flag, it can be forced to overwrite those files
-u, --update-cargo-toml Enables updating the Cargo.toml file in the parent directory of `dest-wit-root` with the copied dependencies
This is a follow-up step after generating (and/or building) the RPC stub. It takes the generated stub's WIT specification and integrates it into another project's WIT specification as a dependency, so that other project can use the stub interface for the remote procedure call.
The parameters have the following meaning:
--stub-wit-root
must point to the directory of the WIT specification of the generated stub. This is thewit
directory in the output ofstubgen generate
or the directory passed by--dest-wit-root
tostubgen build
.--dest-wit-root
is thewit
directory of the Golem project that will use the generated stub for remote worker calls.--overwrite
is an optional flag to force overwriting the destination WIT files if they already exist.--update-cargo-toml
is an optional flag to update theCargo.toml
file in the parent directory ofdest-wit-root
with the copied dependencies. While the command in general works with any guest language, this extra flag enables support for Rust projects built bycargo-component
where the dependencies are listed in theCargo.toml
file.
stubgen compose
Compose a WASM component with a generated stub WASM
Usage: golem-cli stubgen compose [OPTIONS] --source-wasm <SOURCE_WASM> --stub-wasm <STUB_WASM> --dest-wasm <DEST_WASM>
Options:
--source-wasm <SOURCE_WASM> The WASM file of the caller component
--stub-wasm <STUB_WASM> The WASM file of the generated stub. Multiple stubs can be listed
--dest-wasm <DEST_WASM> The name of the composed WASM file to be generated
The compose
subcommand is an alternative for using the general purpose wasm-tools compose
command providing a simpler way to compose the generated RPC stubs with the compiled WASM components using these stubs.
Compiling a project that uses a stub as a dependency cannot be directly uploaded to Golem, as its stub dependencies will not be satisfied by the Golem worker executor. It first has to be composed with the compiled stub WASMs. This is the last step to be performed before uploading the WASM to Golem.
The parameters have the following meaning:
--source-wasm
is the WASM file of the caller component (the one depending on the stub).--stub-wasm
is the WASM file of the generated stub. Multiple stubs can be listed.--dest-wasm
is the target WASM file to be generated. This is the file that can be uploaded to Golem.
stubgen initialize-workspace
Initializes a Golem-specific cargo-make configuration in a Cargo workspace for automatically generating stubs and composing results
Usage: golem-cli stubgen initialize-workspace [OPTIONS] --targets <TARGETS> --callers <CALLERS>
Options:
--targets <TARGETS>
List of subprojects to be called via RPC
--callers <CALLERS>
List of subprojects using the generated stubs for calling remote workers
--wasm-rpc-path-override <WASM_RPC_PATH_OVERRIDE>
The path to the `wasm-rpc` crate to be used in the generated stub crate. If not specified, the latest version of `wasm-rpc` will be used
If all the workers involved in worker to worker communication have their source code in a single cargo workspace, then this command can be used to add a cargo-make (opens in a new tab) build specification that performs all the above steps automatically when building the workspace with cargo make build-flow
or cargo make release-build-flow
.
The parameters have the following meaning:
--targets
is a list of subprojects to be called via RPC. These are the projects that the tool will generate stubs for.--callers
is a list of subprojects using (any of) the generated stubs for calling remote workers.--wasm-rpc-path-override
is useful when working on thewasm-rpc
crate itself, to point the dependency to a local path instead of using the published version. It must be an absolute path.
The initialize-workspace
command is supposed to be used only once in a workspace. After it succeeds, cargo make build-flow
and cargo make release-build-flow
can be used to build the workspace, and the generated stubs will be automatically added as dependencies to the caller projects and the compiled WASM files will be composed with the stubs.
Example
Please read the worker to worker communication blog post (opens in a new tab) for a full example of how to use the stub generator calling one worker from another.