GridGain Developers Hub

WebAssembly Compute Jobs

You can run your code compiled to WebAssembly with GridGain 9 Compute API.

Use JobExecutorType.WASM_EMBEDDED to load WebAssembly binaries as deployment units and execute the code on cluster nodes.

Before submitting your compute job, ensure that the required code is deployed to the nodes where it will execute.

Build WebAssembly Module

The following sections explain how to build and deploy modules in Rust, Go, and WebAssembly Text Format (Wat).

Rust

  1. Make sure Rust and Cargo are installed.

  2. Install wasm-pack:

    cargo install wasm-pack
  3. Create a new Rust library project:

    cargo new --lib hello-wasm
  4. Replace the contents of Cargo.toml with:

    [package]
    name = "hello-wasm"
    version = "0.1.0"
    edition = "2024"
    
    [lib]
    crate-type = ["cdylib", "rlib"]
    
    [dependencies]
    wasm-bindgen = "0.2.84"
  5. Replace the contents of src/lib.rs with:

    use wasm_bindgen::prelude::*;
    
    #[wasm_bindgen]
    pub fn hello(arg: String) -> String {
        format!("Hello from Rust, {}!", arg)
    }
  6. Build the project with wasm-pack build. This will create a pkg directory with the compiled Wasm module and JavaScript bindings. We only need one .wasm file from this directory: pkg/hello_wasm_bg.wasm.

  7. Deploy the .wasm file as part of a Deployment Unit to your cluster.

  8. Run your Rust job using Compute API:

    public class ComputeRustJobExample {
    
        private static final DeploymentUnit DEPLOYMENT_UNIT = new DeploymentUnit("unit-rust", "1.0.0");
    
        private static final String HELLO_FUNC = "rust_wasm_bindgen_bg.wasm:hello:string:rust_wasm_bindgen";
    
        private static final JobExecutionOptions JOB_EXECUTION_OPTIONS = JobExecutionOptions.builder()
                .executorType(JobExecutorType.WASM_EMBEDDED)
                .build();
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
            System.out.println("\nConnecting to server...");
    
            try (IgniteClient client = IgniteClient.builder()
                    .addresses("127.0.0.1:10800")
                    .build()
            ) {
    
                IgniteCompute compute = client.compute();
    
    
                JobDescriptor<String, String> helloDesc = JobDescriptor.<String, String>builder(HELLO_FUNC)
                        .units(DEPLOYMENT_UNIT)
                        .options(JOB_EXECUTION_OPTIONS)
                        .build();
    
                JobTarget target = JobTarget.anyNode(client.cluster().nodes());
    
                String helloRes = compute.execute(target, helloDesc, "World");
                System.out.println("Rust job result: " + helloRes);
    
            }
        }
    }

    For more details, refer to Wasm documentation for Rust:

Go

  1. Make sure Go is installed.

  2. Create a new directory for your project:

    mkdir hello-wasm && cd hello-wasm
  3. Initialize a new Go module:

    go mod init hello-wasm
  4. Create a new file hello.go with the following contents:

    package main
    
    import (
        "fmt"
        "unsafe"
    )
    
    // Store allocated buffers to prevent GC from collecting them.
    var buffersSlice = make([][]byte, 0)
    
    func main() {}
    
    //go:wasmexport addOne
    func addOne(x int32) int32 {
        return x + 1
    }
    
    //go:wasmexport hello
    func hello(arg uint64) uint64 {
        if arg == 0 {
            return 0
        }
    
        argAddr, argLen := unpackPtr(arg)
        argBytes := unsafe.Slice((*byte)(unsafe.Pointer(argAddr)), argLen)
        argStr := string(argBytes)
    
        resStr := fmt.Sprintf("Hello from Go, %s!", argStr)
        resBytes := []byte(resStr)
    
        resPtr := uint64(uintptr(unsafe.Pointer(&resBytes[0])))
        resLen := uint64(len(resBytes))
    
        return packPtr(resLen, resPtr)
    }
    
    //go:wasmexport alloc
    func alloc(size int32) int32 {
        buffer := make([]byte, size)
        buffersSlice = append(buffersSlice, buffer)
        addr := int32(uintptr(unsafe.Pointer(&buffer[0])))
    
        return addr
    }
    
    //go:wasmexport dealloc
    func dealloc(address uint64, size uint32) {
        // No-op.
    }
    
    func packPtr(resLen uint64, resPtr uint64) uint64 {
        return (resLen << 32) | resPtr
    }
    
    func unpackPtr(arg uint64) (uintptr, uintptr) {
        argAddr := uintptr(arg & 0xFFFFFFFF)
        argLen := uintptr(arg >> 32)
        return argAddr, argLen
    }
  5. Build the project:

    GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o go_hello.wasm
  6. Deploy the .wasm file as part of a Deployment Unit to your cluster.

  7. Run your Go job using Compute API:

    public class ComputeGoJobExample {
    
        private static final DeploymentUnit DEPLOYMENT_UNIT = new DeploymentUnit("unit-go", "1.0.0");
    
        private static final String HELLO_FUNC = "go-test-wasi.wasm:hello:string:go_wasi_reactor";
    
        private static final JobExecutionOptions JOB_EXECUTION_OPTIONS = JobExecutionOptions.builder()
                .executorType(JobExecutorType.WASM_EMBEDDED)
                .build();
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
            System.out.println("\nConnecting to server...");
    
            try (IgniteClient client = IgniteClient.builder()
                    .addresses("127.0.0.1:10800")
                    .build()
            ) {
    
                IgniteCompute compute = client.compute();
    
    
                JobDescriptor<String, String> helloDesc = JobDescriptor.<String, String>builder(HELLO_FUNC)
                        .units(DEPLOYMENT_UNIT)
                        .options(JOB_EXECUTION_OPTIONS)
                        .build();
    
                JobTarget target = JobTarget.anyNode(client.cluster().nodes());
    
                String helloRes = compute.execute(target, helloDesc, "World");
                System.out.println("Go job result: " + helloRes);
    
            }
        }
    }

    For more details, refer to Wasm documentation for Go:

WebAssembly Text Format

  1. Go to https://webassembly.github.io/wabt/demo/wat2wasm/ and paste the following code into the left pane:

    (module
        (memory (export "memory") 10)
        (func (export "addOne") (param i32) (result i32)
            local.get 0
            i32.const 1 ;; Value to add
            i32.add
        )
    )
  2. Click the Download button.

  3. Deploy the downloaded .wasm file as part of a Deployment Unit to the cluster.

  4. Invoke the functions with Compute API:

    public class ComputeWasmJobExample {
    
        private static final DeploymentUnit DEPLOYMENT_UNIT = new DeploymentUnit("unit-wasm", "1.0.0");
    
        private static final String HELLO_FUNC = "test-module.wasm:addOne:int32";
    
        private static final JobExecutionOptions JOB_EXECUTION_OPTIONS = JobExecutionOptions.builder()
                .executorType(JobExecutorType.WASM_EMBEDDED)
                .build();
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
            System.out.println("\nConnecting to server...");
    
            try (IgniteClient client = IgniteClient.builder()
                    .addresses("127.0.0.1:10800")
                    .build()
            ) {
    
                IgniteCompute compute = client.compute();
    
    
                JobDescriptor<Integer, Integer> helloDesc = JobDescriptor.<Integer, Integer>builder(HELLO_FUNC)
                        .units(DEPLOYMENT_UNIT)
                        .options(JOB_EXECUTION_OPTIONS)
                        .build();
    
                JobTarget target = JobTarget.anyNode(client.cluster().nodes());
    
                Integer helloRes = compute.execute(target, helloDesc, 42);
                System.out.println("WASM job result: " + helloRes);
    
            }
        }
    }

    For more details, refer to Wasm documentation for WebAssembly: