From 32a48131b58e953465a399851e2dc3154c41be31 Mon Sep 17 00:00:00 2001 From: Nathan Flurry Date: Thu, 12 Mar 2026 22:16:48 -0700 Subject: [PATCH] Cache app workspace actor handle across requests Every request was calling getOrCreate on the Rivet engine API to resolve the workspace actor, even though it's always the same actor. Cache the handle and invalidate on error so retries re-resolve. This eliminates redundant cross-region round-trips to api.rivet.dev on every request. Co-Authored-By: Claude Opus 4.6 --- foundry/packages/backend/src/index.ts | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/foundry/packages/backend/src/index.ts b/foundry/packages/backend/src/index.ts index bf64122..335bb42 100644 --- a/foundry/packages/backend/src/index.ts +++ b/foundry/packages/backend/src/index.ts @@ -118,15 +118,30 @@ export async function startBackend(options: BackendStartOptions = {}): Promise - await withRetries( + let cachedAppWorkspace: any | null = null; + + const appWorkspace = async () => { + if (cachedAppWorkspace) return cachedAppWorkspace; + const handle = await withRetries( async () => await actorClient.workspace.getOrCreate(workspaceKey(APP_SHELL_WORKSPACE_ID), { createWithInput: APP_SHELL_WORKSPACE_ID, }), ); + cachedAppWorkspace = handle; + return handle; + }; - const appWorkspaceAction = async (run: (workspace: any) => Promise): Promise => await withRetries(async () => await run(await appWorkspace())); + const appWorkspaceAction = async (run: (workspace: any) => Promise): Promise => + await withRetries(async () => { + try { + return await run(await appWorkspace()); + } catch (error) { + // Invalidate cache on connection/actor errors so next retry re-resolves + cachedAppWorkspace = null; + throw error; + } + }); const resolveSessionId = async (c: any): Promise => { const requested = c.req.header("x-foundry-session");