History

End a request's sandbox fibers before its database connection closes (#1799)

executeWithPause forks the sandbox as a daemon so a pause can outlive the caller that observed it. That fiber closes over the executor, and the FumaDB client captures its handle at construction rather than resolving it per operation, so the fiber keeps a live reference to the connection the host opened for the scope that built the engine. On the HTTP executor plane that scope is the request, and its finalizer closes the pool as soon as the response is written. A sandbox still parked at that moment issues its next query against a closed pool. Add ExecutionEngine.shutdown, which interrupts the engine's in-flight sandbox fibers and waits for them to unwind, and run it from the shared execution-stack middleware so it happens before the request scope tears the connection down. Nothing is lost by ending it there: on a per-request engine the pause is already unreachable once the response is written, since a resume lands on a different engine and replays the call. The MCP session Durable Object does not use this middleware and keeps its session-lifetime pauses. shutdown is a required member so a decorator that rebuilds the engine object cannot drop it silently.

Rhys Sullivan committed 2 months ago

Commit 6609fa61 parent ebdfb4415 files · +202 −0Browse files

Showing the changes to apps/cloud/src/api (1 of 15 files). Show all

Unified · Split

apps/cloud/src/api/protected.test.ts+2−0
@@ -20,6 +20,8 @@ const makeBaseEngine = (): ExecutionEngine => · 19 unmodified lines
2020 pausedExecutionCount: () => Effect.succeed(0),
2121 hasPausedExecutions: () => Effect.succeed(false),
2222 getDescription: Effect.succeed("desc"),
23+ // The fake forks nothing, so there is no sandbox fiber to end.
24+ shutdown: Effect.void,
2325 }) as ExecutionEngine;
2426
2527 describe("withExecutionUsageTracking", () => {