mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-16 22:54:12 +00:00
fix(serverless): bootstrap TinyGo runtime in persistent WS instances (#240/#249)
InstantiatePersistent passed WithStartFunctions() with no args, explicitly disabling both wasi entry points. The intent was to skip main(); the side effect was leaving the TinyGo runtime uninitialized. The first call to any export traps via wasmExportCheckRun and managed-memory ops panic. Every persistent WS function was effectively dead since plan #06 landed. Earlier patch in this thread restored the call but only handled wasi-reactor builds (_initialize). AnChat's rpc-router is a wasi command build (`_start` export only, no `_initialize`) — wasm-objdump confirms — so the reactor-only fix still left it broken. This fix tries `_initialize` first, falls back to `_start`, and bounds whichever runs with a 5s timeout so a buggy main() can't hang instantiation forever. Logs the chosen hook at Debug, warns when neither is exported. Still pass WithStartFunctions() (no args) so wazero doesn't auto-call `_start` during InstantiateModule — we want full control over which hook runs and the timeout that bounds it. VERSION bumped to 0.122.22.
This commit is contained in:
parent
62a8fbf2df
commit
6a0043a244
@ -306,8 +306,47 @@ func (e *Engine) InstantiatePersistent(ctx context.Context, fn *Function, invCtx
|
||||
hf.SetInvocationContext(invCtx)
|
||||
}
|
||||
|
||||
// Disable WASI _start by passing zero start functions. The TinyGo
|
||||
// runtime's main() may still be present but will never be invoked.
|
||||
// Persistent-instance runtime-init policy. TinyGo emits one of two
|
||||
// start hooks depending on the build target:
|
||||
//
|
||||
// - wasi-reactor target → exports `_initialize` only
|
||||
// - wasi (command) target → exports `_start` only
|
||||
//
|
||||
// Both hooks run the runtime's initAll (heap, GC, package init).
|
||||
// `_start` additionally calls `main()` — fine when main is an
|
||||
// empty stub (which is the convention for persistent WS functions
|
||||
// since the gateway drives lifecycle via ws_open / ws_frame /
|
||||
// ws_close, NOT main()).
|
||||
//
|
||||
// Without one of them being called, TinyGo's runtime stays in an
|
||||
// uninitialized state and the very first export call traps via
|
||||
// `wasmExportCheckRun` — managed-memory operations (allocs,
|
||||
// hashmap ops) panic immediately.
|
||||
//
|
||||
// History of this code path (bugs #240/#249 follow-ups):
|
||||
// - Original code: `WithStartFunctions()` with NO args
|
||||
// (explicitly disable both). Intent was to skip main(); side
|
||||
// effect was breaking TinyGo init. Persistent WS dead since
|
||||
// plan #06 landed.
|
||||
// - First fix: call `_initialize` manually. Worked for
|
||||
// wasi-reactor builds. Still broken for wasi (command) builds
|
||||
// like AnChat's rpc-router which only exports `_start`.
|
||||
// - This fix: try `_initialize` first; fall back to `_start`
|
||||
// if reactor hook isn't exported. Bounded by a 5s timeout so
|
||||
// a runaway main() can't hang instantiation forever.
|
||||
//
|
||||
// AnChat's wasm-objdump output that pinned this:
|
||||
// Export[15]:
|
||||
// - func[127] <_start> → "_start"
|
||||
// - func[414] <main.oramaAlloc#wasmexport> → "orama_alloc"
|
||||
// - func[416] <main.wsOpen#wasmexport> → "ws_open"
|
||||
// ...
|
||||
// (no `_initialize`)
|
||||
//
|
||||
// We still pass `WithStartFunctions()` (no args) so wazero doesn't
|
||||
// auto-call `_start` during InstantiateModule — we want full
|
||||
// control over which hook runs and to bound it with our own
|
||||
// timeout.
|
||||
moduleConfig := wazero.NewModuleConfig().
|
||||
WithName(fn.Name + "-" + invCtx.WSClientID).
|
||||
WithStartFunctions().
|
||||
@ -323,6 +362,44 @@ func (e *Engine) InstantiatePersistent(ctx context.Context, fn *Function, invCtx
|
||||
}
|
||||
return nil, fmt.Errorf("InstantiatePersistent: instantiate: %w", err)
|
||||
}
|
||||
|
||||
// Bootstrap the wasm runtime. Try reactor hook first (no main()),
|
||||
// then command hook (assumes main() is an empty stub per
|
||||
// persistent-function convention). Bounded by a short timeout so
|
||||
// a buggy main() can't hang every connection.
|
||||
const initTimeout = 5 * time.Second
|
||||
initCtx, initCancel := context.WithTimeout(ctx, initTimeout)
|
||||
defer initCancel()
|
||||
|
||||
var initName string
|
||||
var initFn api.Function
|
||||
if hook := instance.ExportedFunction("_initialize"); hook != nil {
|
||||
initName, initFn = "_initialize", hook
|
||||
} else if hook := instance.ExportedFunction("_start"); hook != nil {
|
||||
initName, initFn = "_start", hook
|
||||
}
|
||||
if initFn != nil {
|
||||
if _, callErr := initFn.Call(initCtx); callErr != nil {
|
||||
_ = instance.Close(ctx)
|
||||
if hf, ok := e.hostServices.(contextAwareHostServices); ok {
|
||||
hf.ClearContext()
|
||||
}
|
||||
return nil, fmt.Errorf("InstantiatePersistent: %s: %w", initName, callErr)
|
||||
}
|
||||
e.logger.Debug("persistent instance bootstrapped",
|
||||
zap.String("function", fn.Name),
|
||||
zap.String("client_id", invCtx.WSClientID),
|
||||
zap.String("init_hook", initName))
|
||||
} else {
|
||||
// Neither hook exported. The module may still work if it has
|
||||
// no managed-memory operations — but that's rare in TinyGo.
|
||||
// Log a warning so a function author who hits this can
|
||||
// diagnose without filing a ticket.
|
||||
e.logger.Warn("persistent module exports no _initialize or _start; runtime may be uninitialized",
|
||||
zap.String("function", fn.Name),
|
||||
zap.String("client_id", invCtx.WSClientID))
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@debros/orama",
|
||||
"version": "0.122.21",
|
||||
"version": "0.122.22",
|
||||
"description": "TypeScript SDK for Orama Network - Database, PubSub, Cache, Storage, Vault, and more",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user