Skip to content

Add full Go Support#4526

Open
djgilcrease wants to merge 3 commits intoclockworklabs:masterfrom
digitalxero:go-sdk
Open

Add full Go Support#4526
djgilcrease wants to merge 3 commits intoclockworklabs:masterfrom
digitalxero:go-sdk

Conversation

@djgilcrease
Copy link

@djgilcrease djgilcrease commented Mar 3, 2026

Description of Changes

Architecture decisions

  • Code generation over reflection — Go's reflect package is not available in WASM. The stdb-gen tool (https://gitlab.com/digitalxero/stdb-gen) reads //stdb: directives and generates all serialization, table accessors, and module definitions at compile time.
  • //go:wasmexport — Requires Go 1.24+. Exports are in server/runtime/exports.go behind //go:build wasip1.
  • Panic-based error handling in server — Insert/Delete/Update operations panic on error (matching Rust SDK convention), caught by recover() in the WASM call reducer wrapper.
  • WASI stubs — Go's wasip1 target requires WASI imports. Minimal stubs redirect stdout/stderr to the host logger and stub out clock/args/environ.

Summary

This PR adds first-class Go language support to SpacetimeDB, including a complete server-side WASM module SDK, a WebSocket client SDK, Rust-side codegen for Go bindings, CLI build toolchain integration, project templates, and a comprehensive test suite covering all 66 integration tests.

Highlights

  • Server SDK (sdks/go/server/) — Write SpacetimeDB modules in Go, compiled to WASM via GOOS=wasip1 GOARCH=wasm with //go:wasmexport (Go 1.24+). Supports tables, reducers, procedures, views, event tables, scheduled reducers, lifecycle hooks, row-level security, and HTTP from procedures.
  • Client SDK (sdks/go/client/) — Channel-based WebSocket client with BSATN protocol v2, brotli compression, client-side table cache (xsync lock-free maps), subscriptions, and reducer callbacks.
  • BSATN library (sdks/go/bsatn/) — Complete binary serialization matching the SpacetimeDB wire format (LE integers, u32-prefixed strings/arrays, u8 sum tags, sequential product fields).
  • Code generator (stdb-gen) — Replaces reflection with compile-time code generation via //stdb: directives. Generates type-safe table accessors, BSATN serialization, index methods, and module definition.
  • Rust codegen (crates/codegen/src/go.rs) — Implements the Lang trait for generating Go client bindings from module schemas.
  • CLI integration (crates/cli/src/tasks/go.rs) — spacetime build and spacetime init support for Go modules.
  • WASI stubs (crates/core/src/host/wasmtime/wasi_stubs.rs) — Minimal WASI Preview 1 shims (fd_write for stdout/stderr, clock, args, environ) to support Go's wasip1 runtime.
  • Templatesbasic-go and chat-console-go starter projects for spacetime init.
  • All 66 integration tests pass — 50 core SDK tests, 7 procedure tests, 6 view tests, 3 event table tests.

Templates (templates/)

  • basic-go/ — Minimal starter with a Person table and add/say_hello reducers
  • chat-console-go/ — Chat app with User/Message tables, connect/disconnect lifecycle, and a terminal client

API and ABI breaking changes

None

Expected complexity level and risk

2 - does require go knowlage, but it fully integrates with the existing integration tests and benchmarks
This should be relatively isolated from existing code, except for the updates to the cli to add go language support.

Rust changes (crates/) — 18 files, ~1,900 lines

  • crates/codegen/src/go.rs (1,133 lines) — Go client binding codegen implementing Lang trait
  • crates/cli/src/tasks/go.rs (123 lines) — Go module build task (stdb-gen → go generate → go build)
  • crates/cli/src/subcommands/init.rs — Go template support for spacetime init
  • crates/cli/src/subcommands/generate.rs — Go language option for spacetime generate
  • crates/core/src/host/wasmtime/wasi_stubs.rs (272 lines) — WASI Preview 1 stubs for Go WASM modules
  • crates/testing/ — Go module build support in test harness
  • crates/smoketests/ — Go module smoke test

Testing

  • All 50 core Go SDK integration tests pass (cargo test -p spacetimedb-sdk -- go::)
  • All 7 Go procedure tests pass (cargo test -p spacetimedb-sdk -- go_procedures::)
  • All 6 Go view tests pass (cargo test -p spacetimedb-sdk -- go_view::)
  • All 3 Go event table tests pass (cargo test -p spacetimedb-sdk -- go_event_table_tests::)
  • Go unit tests pass (cd sdks/go && go test ./...)
  • Go benchmarks run and are competitive with Rust module performance
  • Smoke test verifies Go toolchain detection
  • CI workflow validates on PR (This will also need to be updated to tag changes to sdks/go with sdks/go/v#.#.# so that go clients/servers will be able to properly import the code.

Test modules (modules/) — 46 files, ~4,500 lines

Module Coverage
sdk-test-go/ 73 tables, 200+ reducers — full parity with Rust sdk-test module
sdk-test-connect-disconnect-go/ Connect/disconnect lifecycle callbacks
sdk-test-procedure-go/ 8 procedures (return values, transactions, panics, HTTP, scheduled, UUID v7)
sdk-test-view-go/ 4 views (auth/anonymous, Option/Vec returns)
sdk-test-event-table-go/ Event tables (emit, multiple events, non-persistence)
benchmarks-go/ 3 suites: circles (game physics), ia_loop (AI state), synthetic (index strategies)
module-test-go/ Comprehensive: 11 tables, 15 reducers, 5 procedures, 1 view, scoped types
perf-test-go/ Multi-column index performance

Benchmark Results: Rust vs Go

All benchmarks run on the same machine. Circles and IA Loop use debug mode; Index Scans use release mode.
Go modules built with: GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared

Circles Benchmark (Debug mode)

Operation Rows Rust Go Go/Rust
insert_bulk_food 50 459µs 517µs 1.13x
insert_bulk_entity 50 207µs 198µs 0.96x
insert_bulk_circle 500 1.302ms 979µs 0.75x
cross_join_circle_food 25,000 7.799ms 9.993ms 1.28x
cross_join_all 1,250,000 331.984ms 263.614ms 0.79x

IA Loop Benchmark (Debug mode)

Operation Rows Rust Go Go/Rust
insert_bulk_position 20,000 48.872ms 25.778ms 0.53x
insert_bulk_velocity 10,000 19.615ms 10.891ms 0.56x
update_position_all 20,000 81.697ms 42.038ms 0.51x
update_position_with_velocity 10,000 63.440ms 85.632ms 1.35x
insert_world 5,000 71.591ms 43.645ms 0.61x
game_loop_enemy_ia 100 184.563ms 88.021ms 0.48x

Index Scans (Release mode, 1.2M rows)

Index Rust Go Go/Rust
{id} (unique PK) 27.6µs 55.4µs 2.01x
{chunk} (btree) 75.5µs 54.0µs 0.72x
{x, z, dimension} (3-col btree) 17.1µs 47.5µs 2.78x
{x, z} (2-col prefix) 90.7µs 61.6µs 0.68x
load_location_table (1.2M inserts) 1.961s 2.629s 1.34x

Key Takeaways

  • Go wins most bulk insert/update workloads (often ~2x faster in debug mode)
  • Go wins the massive cross_join_all (1.25M rows) by 21%
  • Mixed results on index scans — Go wins on btree range scans, Rust wins on unique PK lookups and multi-column exact matches
  • Values bolded where Go is faster (ratio < 1.0)

@CLAassistant
Copy link

CLAassistant commented Mar 3, 2026

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants