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-stubgencan 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 {
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>;
}
}
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 helpstubgen 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 usedThe parameters have the following meaning:
--source-wit-rootmust point to the directory of the WIT specification of the worker to be called remotely. This is usually awitsubdirectory in the worker's component's source code.--dest-crate-rootis 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.--worldcan 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-versionsets the generated Rust crate's version to the given value.--wasm-rpc-path-overrideis useful when working on thewasm-rpccrate 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-rootmust point to the directory of the WIT specification of the worker to be called remotely. This is usually awitsubdirectory in the worker's component's source code.--dest-wasmis the target WASM file to be generated.--dest-wit-rootis the target directory where the stub's WIT file and its dependencies will be generated into.--worldcan 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-versionsets the generated Rust crate's version to the given value.--wasm-rpc-path-overrideis useful when working on thewasm-rpccrate 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 dependenciesThis 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-rootmust point to the directory of the WIT specification of the generated stub. This is thewitdirectory in the output ofstubgen generateor the directory passed by--dest-wit-roottostubgen build.--dest-wit-rootis thewitdirectory of the Golem project that will use the generated stub for remote worker calls.--overwriteis an optional flag to force overwriting the destination WIT files if they already exist.--update-cargo-tomlis an optional flag to update theCargo.tomlfile in the parent directory ofdest-wit-rootwith the copied dependencies. While the command in general works with any guest language, this extra flag enables support for Rust projects built bycargo-componentwhere the dependencies are listed in theCargo.tomlfile.
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 generatedThe 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-wasmis the WASM file of the caller component (the one depending on the stub).--stub-wasmis the WASM file of the generated stub. Multiple stubs can be listed.--dest-wasmis 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 usedIf 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:
--targetsis a list of subprojects to be called via RPC. These are the projects that the tool will generate stubs for.--callersis a list of subprojects using (any of) the generated stubs for calling remote workers.--wasm-rpc-path-overrideis useful when working on thewasm-rpccrate 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.