Bug: Resource.instantiate() nodes persist in WASM after consumer disconnects
Version: @skipruntime/core@0.0.19
Problem
When a client disconnects from an SSE stream backed by a Resource, the reactive graph nodes created by Resource.instantiate() remain in WASM memory. There is no automatic cleanup, and the required manual cleanup method (serviceBroker.deleteUUID()) is not documented.
Without explicit cleanup, WASM arrayBuffers memory grows unbounded with each new SSE connection.
Reproduction
// Server-side: creating a resource instance per SSE client
app.get("/stream/:resourceName", async (req, reply) => {
const uuid = await serviceBroker.getStreamUUID(
req.params.resourceName,
{ userId: req.user.id }
);
// Client connects to the Skip streaming server
reply.redirect(`http://localhost:8080/streams/${uuid}`);
// When the client disconnects...
// The reactive graph nodes for this UUID remain in WASM memory forever.
// No automatic cleanup happens.
});
After many connect/disconnect cycles:
# Memory grows steadily:
# t=0: arrayBuffers: 12 MB
# t=1h: arrayBuffers: 85 MB
# t=4h: arrayBuffers: 310 MB
# t=12h: OOM kill
Current workaround
Manually calling serviceBroker.deleteUUID() when the stream consumer disconnects:
app.get("/stream/:resourceName", async (req, reply) => {
const uuid = await serviceBroker.getStreamUUID(resourceName, params);
// Proxy the SSE stream...
const cleanup = () => {
// Free the reactive graph nodes from WASM memory
serviceBroker.deleteUUID(uuid).catch((err) => {
console.warn(`Failed to delete stream ${uuid}: ${err.message}`);
});
};
req.raw.on("close", cleanup);
req.raw.on("error", cleanup);
});
We also built a memory trend tracker that samples process.memoryUsage().arrayBuffers every 60 seconds and alerts when growth exceeds 50 MB/hour — this is how we discovered the leak in the first place.
Expected behavior
One of:
-
Automatic cleanup: When the SSE stream consumer disconnects (the HTTP connection closes), the runtime should detect this and release the associated resource instance and its reactive graph nodes from WASM memory.
-
Documented manual cleanup: If automatic cleanup isn't feasible, deleteUUID() should be prominently documented as a required cleanup step, with examples showing the pattern for SSE stream lifecycle management.
Currently, deleteUUID() is not mentioned in any documentation or examples, making this a hidden memory leak that only surfaces under sustained production traffic.
Bug:
Resource.instantiate()nodes persist in WASM after consumer disconnectsVersion:
@skipruntime/core@0.0.19Problem
When a client disconnects from an SSE stream backed by a
Resource, the reactive graph nodes created byResource.instantiate()remain in WASM memory. There is no automatic cleanup, and the required manual cleanup method (serviceBroker.deleteUUID()) is not documented.Without explicit cleanup, WASM
arrayBuffersmemory grows unbounded with each new SSE connection.Reproduction
After many connect/disconnect cycles:
Current workaround
Manually calling
serviceBroker.deleteUUID()when the stream consumer disconnects:We also built a memory trend tracker that samples
process.memoryUsage().arrayBuffersevery 60 seconds and alerts when growth exceeds 50 MB/hour — this is how we discovered the leak in the first place.Expected behavior
One of:
Automatic cleanup: When the SSE stream consumer disconnects (the HTTP connection closes), the runtime should detect this and release the associated resource instance and its reactive graph nodes from WASM memory.
Documented manual cleanup: If automatic cleanup isn't feasible,
deleteUUID()should be prominently documented as a required cleanup step, with examples showing the pattern for SSE stream lifecycle management.Currently,
deleteUUID()is not mentioned in any documentation or examples, making this a hidden memory leak that only surfaces under sustained production traffic.