GridGain Developers Hub

WebAssembly Compute Jobs

You can run your code compiled to WebAssembly with GridGain 9 Compute API. By default, WASM code will be compiled into JVM bytecode for faster execution. You can configure this behavior in the compute configuration.

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.

Runtime and configuration

By default, WebAssembly code is compiled to JVM bytecode at runtime for faster execution (Chicory runtime).

You can control this behavior with the compute.wasm.enableCompiler setting in the compute configuration.

Instance Caching

To reduce the startup overhead of module loading and initialization, especially noticeable with some runtimes (for example, Go), GridGain 9 caches WebAssembly instances.

  • Instances are cached per deployment unit and file name, avoiding repeated loading and initialization.

  • Cached instances are automatically cleared when the corresponding deployment unit is undeployed.

Configuration Example

Add the following snippet to your gridgain-config.conf to define module memory, cache size and TTL parameters.

compute {
  wasm {
    enableCompiler = true
    moduleMaxMemory = 8m
    moduleCache {
      enabled = true
      maxSize = 10
      expireAfterAccessSeconds = 5
      expireAfterWriteSeconds = 6
    }
  }
}

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: