diff --git a/apps/cloud/src/api.ts b/apps/cloud/src/api.ts index da5dcaf7e9..084b6716fa 100644 --- a/apps/cloud/src/api.ts +++ b/apps/cloud/src/api.ts @@ -10,7 +10,7 @@ import { HttpRouter, HttpServer, } from "@effect/platform"; -import { Effect, Layer, Scope } from "effect"; +import { Effect, Layer } from "effect"; import { setCookie } from "@tanstack/react-start/server"; import { addGroup } from "@executor/api"; @@ -69,20 +69,19 @@ const SharedServices = Layer.mergeAll( HttpServer.layerContext, ); -const sharedServicesScope = Effect.runSync(Scope.make()); -const SharedServicesMemoized = Effect.runSync( - Layer.memoize(SharedServices).pipe( - Scope.extend(sharedServicesScope), - ), -); - -const publicApiHandler = HttpApiBuilder.toWebHandler( - PublicCloudApiLive.pipe( - Layer.provideMerge(SharedServicesMemoized), - Layer.provideMerge(HttpRouter.setRouterConfig({ maxParamLength: 1000 })), - ), - { middleware: HttpMiddleware.logger }, -); +let _publicApiHandler: ReturnType | null = null; +const getPublicApiHandler = () => { + if (!_publicApiHandler) { + _publicApiHandler = HttpApiBuilder.toWebHandler( + PublicCloudApiLive.pipe( + Layer.provideMerge(SharedServices), + Layer.provideMerge(HttpRouter.setRouterConfig({ maxParamLength: 1000 })), + ), + { middleware: HttpMiddleware.logger }, + ); + } + return _publicApiHandler; +}; const parseCookie = (cookieHeader: string | null, name: string): string | null => { if (!cookieHeader) return null; @@ -187,7 +186,7 @@ const createProtectedHandler = ( Layer.provideMerge(HttpApiBuilder.middlewareOpenApi()), Layer.provideMerge(ProtectedCloudApiLive), Layer.provideMerge(requestServices), - Layer.provideMerge(SharedServicesMemoized), + Layer.provideMerge(SharedServices), Layer.provideMerge(HttpRouter.setRouterConfig({ maxParamLength: 1000 })), ), { middleware: HttpMiddleware.logger }, @@ -210,7 +209,7 @@ export const handleApiRequest = async (request: Request): Promise => { const pathname = new URL(request.url).pathname; if (isPublicPath(pathname)) { - return publicApiHandler.handler(request); + return getPublicApiHandler().handler(request); } const requestProgram = Effect.gen(function* () { @@ -248,7 +247,7 @@ export const handleApiRequest = async (request: Request): Promise => { return Effect.runPromise( requestProgram.pipe( - Effect.provide(SharedServicesMemoized), + Effect.provide(SharedServices), Effect.scoped, ), ); diff --git a/apps/cloud/src/services/db.ts b/apps/cloud/src/services/db.ts index 8abadbdd1a..5b1d62b6a6 100644 --- a/apps/cloud/src/services/db.ts +++ b/apps/cloud/src/services/db.ts @@ -44,13 +44,15 @@ const resolveConnectionString = resolveHyperdriveUrl.pipe( const acquirePostgres = (connectionString: string) => Effect.tryPromise(async () => { const { drizzle } = await import("drizzle-orm/node-postgres"); - const { Pool } = await import("pg"); - const pool = new Pool({ connectionString }); - return { db: drizzle(pool, { schema }) as DrizzleDb, pool }; + const { Client } = await import("pg"); + // Use Client (not Pool) — Hyperdrive manages connection pooling externally. + const client = new Client({ connectionString }); + await client.connect(); + return { db: drizzle(client, { schema }) as DrizzleDb, client }; }); -const releasePostgres = ({ pool }: { pool: { end: () => Promise } }) => - Effect.promise(() => pool.end()).pipe(Effect.orElseSucceed(() => undefined)); +const releasePostgres = ({ client }: { client: { end: () => Promise } }) => + Effect.promise(() => client.end()).pipe(Effect.orElseSucceed(() => undefined)); // --------------------------------------------------------------------------- // Service