Skip to content

fix(deps): require go-pkg v0.4.1 to stop GC in cabi_realloc - #477

Open
ricochet wants to merge 2 commits into
mainfrom
claude/frosty-nobel-daa8c3
Open

ricochet wants to merge 2 commits into
mainfrom
claude/frosty-nobel-daa8c3

Conversation

@ricochet

@ricochet ricochet commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

What

Moves go.bytecodealliance.org/pkg from v0.4.0 to v0.4.1 in the SDK (component/), the plugin SDK (plugin/), every example and the http-hello-world template.

Why

go-pkg v0.4.0 lets the Go garbage collector start a cycle inside cabi_realloc, the allocator the host calls to copy a value into the component's memory. A GC cycle calls imports (it reads clocks and polls for I/O). The component model forbids imports during cabi_realloc, so the component traps.

It only shows up when one value is large enough to trigger GC:

  • a list<record> of ~40k records traps, while 20k works
  • reading a single 8.8 MiB NATS KV value traps, with gcStart and cabi_realloc in the backtrace

bytecodealliance/go-pkg#13 ("defer GC work while in cabi_realloc"), released in v0.4.1, calls runtime.procPin / procUnpin around the allocation. That defers GC work until the allocation returns. Nothing else changed between v0.4.0 and v0.4.1: one file (wit/runtime/runtime.go), and the go 1.27.1 directive is the same.

Why the examples and template move too

  • They build against the released SDK v0.1.6, which still requires v0.4.0. Go's minimum version selection only picks the fix if the module itself asks for v0.4.1. Anyone who copies an example gets the fix now, without waiting for SDK v0.1.7.

Last time go-wasi moved (c68cdc6), every example and the template moved with it. This PR does the same.

Version tracking and air-gap

  • airgap/versions.env: GO_PKG=v0.4.1, plus a comment saying why it is the minimum.
  • BUILDING.md: version table, and a short "v0.4.1 is the floor" paragraph under go-pkg and go-wasi with the symptoms. For code copied from an earlier example it says to run go get go.bytecodealliance.org/pkg@v0.4.1. Editing go.mod by hand fails the next build on go.sum.
  • airgap/check-versions.sh:
    • New go-pkg (module pins) check. It fails if any go.mod under examples/, templates/ or plugin/ names a go-pkg version other than GO_PKG. Before, only component/go.mod was checked.
    • The SDK, go-pkg and go-wasi module-pin checks now share one check_module_pins helper. On bash 3.2 under set -u, it reports a module with no pins as found: none instead of crashing.
    • The BUILDING.md go-pkg check now matches the table row. The new prose names v0.4.1 on purpose, so a bare version match would pass with a stale table.
  • airgap/Dockerfile: the seed loop now runs go mod download all in component/ too. Before, it seeded only the examples and templates, so the offline in-repo-SDK build had the SDK's own requirements only when some example happened to name the same versions.
  • .github/workflows/airgapped.yaml: plugin/go.mod added to the push paths and the paths-filter, since the check reads it.
  • plugin/README.md: the require line it tells plugin authors to copy.

Not changed

  • No binding regeneration. Generated code refers to go-pkg by import path only, and the SDK bindings header check still passes at 0.63.0.
  • componentize-go stays at v0.4.3 in every module. go mod tidy did not move it.

Testing

  • airgap/check-versions.sh under macOS /bin/bash 3.2: all ok. Three forced failures each report DRIFT as expected:
    • plugin/go.mod set back to v0.4.0
    • the BUILDING.md table row set to v0.4.0 while the prose still says v0.4.1
    • the helper called for a module nothing names (found: none, no set -u crash)
  • shellcheck: no new warnings. The three info-level notes it prints are on unchanged lines.
  • SDK: GOOS=wasip1 GOARCH=wasm go build ./..., go vet (same package filter as CI) and go test ./... pass. Plugin: build and go test ./... pass.
  • wash build passes in all 11 example modules as checked in (released SDK v0.1.6 + go-pkg v0.4.1). It also passes in a go mod tidy'd copy of the template, where tidy keeps v0.4.1.
  • http-p3-streaming and nats-kv-watch were also built the way CI builds them: go mod edit -replace to the in-repo component/, go mod tidy, wash build. Both pass and resolve go-pkg v0.4.1.
  • The built http_p3_streaming.wasm and nats_kv_watch.wasm both contain go.bytecodealliance.org/pkg@v0.4.1. http_p3_streaming.wasm also calls runtime.procPin.
  • make airgap-image passes with the new seed loop. It downloads component/'s modules first, then every example and the template. The image's own offline warm-up builds (http-server and http-p3-streaming under --network=none, GOPROXY=off) pass.
  • Not run: the full offline example build (make airgap-test), and a runtime repro of the 40k-record / 8.8 MiB trap. Local wash is 2.7.0, not the pinned v2.10.3. It only drives the build command here.

Follow-up

Cut SDK v0.1.7 so go.wasmcloud.dev/component requires v0.4.1 itself. After that, users who don't copy an example get the fix too. The pins in the examples become redundant but stay correct.

go.bytecodealliance.org/pkg v0.4.0 lets the Go garbage collector start a
cycle inside cabi_realloc. A GC cycle calls imports (clocks, I/O polling),
which the component model forbids there, so the component traps. It shows
up once a single value is big enough to trigger GC: a list<record> of ~40k
records traps while 20k works, and reading one 8.8 MiB NATS KV value traps
with gcStart and cabi_realloc in the backtrace. v0.4.1
(bytecodealliance/go-pkg#13) pins the M while allocating, deferring GC
work until the allocation returns.

- component/ and plugin/ require v0.4.1.
- Every example and the template pin v0.4.1 too. They build against the
  released SDK v0.1.6, which still names v0.4.0, so MVS needs the pin to
  pick the fix; and the air-gapped image seeds GOMODCACHE from these
  modules alone, so without it the in-repo SDK build would go offline
  short of v0.4.1.
- airgap/versions.env and BUILDING.md record the new pin and why it is
  the floor; check-versions.sh now fails if any module names another
  go-pkg version.

No binding regeneration: generated code names go-pkg by import path only.
componentize-go stays at v0.4.3.

Signed-off-by: Bailey Hayes <bailey@cosmonic.com>
Follow-ups from review of the go-pkg v0.4.1 bump:

- airgap/Dockerfile seeds GOMODCACHE from component/ as well. An in-repo
  SDK build replaces the released SDK with it, so its requirements must
  be cached offline even when no example names the same versions.
- check-versions.sh: the SDK, go-pkg and go-wasi module-pin checks share
  one check_module_pins helper, which also no longer trips `set -u` on
  bash 3.2 when nothing matches. The go-pkg rationale comment now gives
  the real reason (MVS under the released SDK; plugin in lockstep).
- The BUILDING.md go-pkg check matches the table row. The prose names
  the v0.4.1 floor, so a bare version match passed with a stale table.
- airgapped.yaml runs on plugin/go.mod changes, which the check reads.
- BUILDING.md tells users to run `go get ...@v0.4.1` rather than edit
  go.mod by hand, which fails the next build on go.sum.

Signed-off-by: Bailey Hayes <bailey@cosmonic.com>
@ricochet
ricochet requested a review from a team as a code owner November 8, 2026 00:04

This branch has not been deployed

No deployments
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.

1 participant