From 9763d379d08539b5e0525ff94faf8a5be7268866 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Oct 2025 23:02:09 -0700 Subject: [PATCH 1/4] fix: enable database connection pooling in production --- packages/db/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/db/index.ts b/packages/db/index.ts index d53999e083..3b52d72d3e 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -26,4 +26,4 @@ declare global { } export const db = globalThis.database || drizzleClient -if (process.env.NODE_ENV !== 'production') globalThis.database = db +globalThis.database = db From 1c189acb667f6c3e245bd4188e5149f68af78836 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Oct 2025 23:28:19 -0700 Subject: [PATCH 2/4] debug: add diagnostic endpoints to test NODE_ENV and database pooling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added two diagnostic endpoints: - /api/debug/env: Shows NODE_ENV and globalThis.database status - /api/debug/db-test: Measures database query performance 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/sim/app/api/debug/db-test/route.ts | 52 +++++++++++++++++++++++++ apps/sim/app/api/debug/env/route.ts | 17 ++++++++ 2 files changed, 69 insertions(+) create mode 100644 apps/sim/app/api/debug/db-test/route.ts create mode 100644 apps/sim/app/api/debug/env/route.ts diff --git a/apps/sim/app/api/debug/db-test/route.ts b/apps/sim/app/api/debug/db-test/route.ts new file mode 100644 index 0000000000..5de5aa574f --- /dev/null +++ b/apps/sim/app/api/debug/db-test/route.ts @@ -0,0 +1,52 @@ +import { db } from '@sim/db' +import { sql } from 'drizzle-orm' +import { NextResponse } from 'next/server' + +export const dynamic = 'force-dynamic' + +export async function GET() { + const measurements: Record = {} + + try { + // Test 1: Simple query + const t1 = performance.now() + await db.execute(sql`SELECT 1`) + measurements.simpleQuery = performance.now() - t1 + + // Test 2: Check if globalThis caching is working + const t2 = performance.now() + const dbRef1 = globalThis.database + measurements.checkGlobalThis = performance.now() - t2 + + // Test 3: Another simple query + const t3 = performance.now() + await db.execute(sql`SELECT 1`) + measurements.simpleQuery2 = performance.now() - t3 + + // Test 4: Check connection + const t4 = performance.now() + await db.execute(sql`SELECT current_database(), current_user, version()`) + measurements.connectionInfo = performance.now() - t4 + + return NextResponse.json({ + success: true, + NODE_ENV: process.env.NODE_ENV, + hasCachedDatabase: !!globalThis.database, + isSameDbInstance: dbRef1 === globalThis.database, + measurements, + timestamp: new Date().toISOString(), + }) + } catch (error: any) { + return NextResponse.json( + { + success: false, + error: error.message, + NODE_ENV: process.env.NODE_ENV, + hasCachedDatabase: !!globalThis.database, + measurements, + timestamp: new Date().toISOString(), + }, + { status: 500 } + ) + } +} diff --git a/apps/sim/app/api/debug/env/route.ts b/apps/sim/app/api/debug/env/route.ts new file mode 100644 index 0000000000..827ee8002a --- /dev/null +++ b/apps/sim/app/api/debug/env/route.ts @@ -0,0 +1,17 @@ +import { NextResponse } from 'next/server' + +export const dynamic = 'force-dynamic' + +export async function GET() { + return NextResponse.json({ + NODE_ENV: process.env.NODE_ENV, + nodeVersion: process.version, + platform: process.platform, + arch: process.arch, + uptime: process.uptime(), + memoryUsage: process.memoryUsage(), + // Check if globalThis.database exists + hasCachedDatabase: !!globalThis.database, + timestamp: new Date().toISOString(), + }) +} From ddac374fa4c2c8dba7b8040b068b7f7d70cc3644 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 6 Oct 2025 23:46:17 -0700 Subject: [PATCH 3/4] test: add connection testing endpoint to diagnose production delay --- apps/sim/app/api/test-connection/route.ts | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 apps/sim/app/api/test-connection/route.ts diff --git a/apps/sim/app/api/test-connection/route.ts b/apps/sim/app/api/test-connection/route.ts new file mode 100644 index 0000000000..63104ae3b1 --- /dev/null +++ b/apps/sim/app/api/test-connection/route.ts @@ -0,0 +1,78 @@ +import { NextResponse } from 'next/server' +import postgres from 'postgres' + +export const dynamic = 'force-dynamic' +export const runtime = 'nodejs' + +let requestCount = 0 +let globalPgClient: ReturnType | undefined + +export async function GET() { + const startTime = performance.now() + requestCount++ + + const measurements: Record = {} + + // Test 1: Check if globalThis persists + measurements.requestCount = requestCount + measurements.globalThisTest = (globalThis as any).testCounter || 0 + ;(globalThis as any).testCounter = ((globalThis as any).testCounter || 0) + 1 + + // Test 2: Check if module-level variable persists + measurements.moduleLevelPersists = globalPgClient !== undefined + + // Test 3: Create new postgres client + const t1 = performance.now() + const connectionString = process.env.DATABASE_URL! + const pgClient = postgres(connectionString, { + max: 80, + idle_timeout: 20, + connect_timeout: 30, + prepare: false, + onnotice: () => {}, + }) + measurements.createClientTime = performance.now() - t1 + + // Test 4: Execute query with new client + const t2 = performance.now() + try { + await pgClient`SELECT 1` + measurements.firstQueryTime = performance.now() - t2 + } catch (e: any) { + measurements.firstQueryError = e.message + } + + // Test 5: Execute second query + const t3 = performance.now() + try { + await pgClient`SELECT 1` + measurements.secondQueryTime = performance.now() - t3 + } catch (e: any) { + measurements.secondQueryError = e.message + } + + // Test 6: Use global client if exists + if (globalPgClient) { + const t4 = performance.now() + try { + await globalPgClient`SELECT 1` + measurements.globalClientQueryTime = performance.now() - t4 + } catch (e: any) { + measurements.globalClientError = e.message + } + } + + // Store for next request + if (!globalPgClient) { + globalPgClient = pgClient + } else { + // End the new client to avoid connection leak + await pgClient.end() + } + + measurements.totalTime = performance.now() - startTime + measurements.NODE_ENV = process.env.NODE_ENV + measurements.timestamp = new Date().toISOString() + + return NextResponse.json(measurements) +} From 088ca310066eefef23b872a65050dff27fc8e83d Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Oct 2025 10:25:24 -0700 Subject: [PATCH 4/4] redeuce num of concurrent connections --- apps/sim/app/api/debug/db-test/route.ts | 52 ------------- apps/sim/app/api/debug/env/route.ts | 17 ---- apps/sim/app/api/test-connection/route.ts | 78 ------------------- apps/sim/socket-server/database/operations.ts | 3 +- apps/sim/socket-server/rooms/manager.ts | 2 +- packages/db/index.ts | 12 +-- 6 files changed, 4 insertions(+), 160 deletions(-) delete mode 100644 apps/sim/app/api/debug/db-test/route.ts delete mode 100644 apps/sim/app/api/debug/env/route.ts delete mode 100644 apps/sim/app/api/test-connection/route.ts diff --git a/apps/sim/app/api/debug/db-test/route.ts b/apps/sim/app/api/debug/db-test/route.ts deleted file mode 100644 index 5de5aa574f..0000000000 --- a/apps/sim/app/api/debug/db-test/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { db } from '@sim/db' -import { sql } from 'drizzle-orm' -import { NextResponse } from 'next/server' - -export const dynamic = 'force-dynamic' - -export async function GET() { - const measurements: Record = {} - - try { - // Test 1: Simple query - const t1 = performance.now() - await db.execute(sql`SELECT 1`) - measurements.simpleQuery = performance.now() - t1 - - // Test 2: Check if globalThis caching is working - const t2 = performance.now() - const dbRef1 = globalThis.database - measurements.checkGlobalThis = performance.now() - t2 - - // Test 3: Another simple query - const t3 = performance.now() - await db.execute(sql`SELECT 1`) - measurements.simpleQuery2 = performance.now() - t3 - - // Test 4: Check connection - const t4 = performance.now() - await db.execute(sql`SELECT current_database(), current_user, version()`) - measurements.connectionInfo = performance.now() - t4 - - return NextResponse.json({ - success: true, - NODE_ENV: process.env.NODE_ENV, - hasCachedDatabase: !!globalThis.database, - isSameDbInstance: dbRef1 === globalThis.database, - measurements, - timestamp: new Date().toISOString(), - }) - } catch (error: any) { - return NextResponse.json( - { - success: false, - error: error.message, - NODE_ENV: process.env.NODE_ENV, - hasCachedDatabase: !!globalThis.database, - measurements, - timestamp: new Date().toISOString(), - }, - { status: 500 } - ) - } -} diff --git a/apps/sim/app/api/debug/env/route.ts b/apps/sim/app/api/debug/env/route.ts deleted file mode 100644 index 827ee8002a..0000000000 --- a/apps/sim/app/api/debug/env/route.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { NextResponse } from 'next/server' - -export const dynamic = 'force-dynamic' - -export async function GET() { - return NextResponse.json({ - NODE_ENV: process.env.NODE_ENV, - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - uptime: process.uptime(), - memoryUsage: process.memoryUsage(), - // Check if globalThis.database exists - hasCachedDatabase: !!globalThis.database, - timestamp: new Date().toISOString(), - }) -} diff --git a/apps/sim/app/api/test-connection/route.ts b/apps/sim/app/api/test-connection/route.ts deleted file mode 100644 index 63104ae3b1..0000000000 --- a/apps/sim/app/api/test-connection/route.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { NextResponse } from 'next/server' -import postgres from 'postgres' - -export const dynamic = 'force-dynamic' -export const runtime = 'nodejs' - -let requestCount = 0 -let globalPgClient: ReturnType | undefined - -export async function GET() { - const startTime = performance.now() - requestCount++ - - const measurements: Record = {} - - // Test 1: Check if globalThis persists - measurements.requestCount = requestCount - measurements.globalThisTest = (globalThis as any).testCounter || 0 - ;(globalThis as any).testCounter = ((globalThis as any).testCounter || 0) + 1 - - // Test 2: Check if module-level variable persists - measurements.moduleLevelPersists = globalPgClient !== undefined - - // Test 3: Create new postgres client - const t1 = performance.now() - const connectionString = process.env.DATABASE_URL! - const pgClient = postgres(connectionString, { - max: 80, - idle_timeout: 20, - connect_timeout: 30, - prepare: false, - onnotice: () => {}, - }) - measurements.createClientTime = performance.now() - t1 - - // Test 4: Execute query with new client - const t2 = performance.now() - try { - await pgClient`SELECT 1` - measurements.firstQueryTime = performance.now() - t2 - } catch (e: any) { - measurements.firstQueryError = e.message - } - - // Test 5: Execute second query - const t3 = performance.now() - try { - await pgClient`SELECT 1` - measurements.secondQueryTime = performance.now() - t3 - } catch (e: any) { - measurements.secondQueryError = e.message - } - - // Test 6: Use global client if exists - if (globalPgClient) { - const t4 = performance.now() - try { - await globalPgClient`SELECT 1` - measurements.globalClientQueryTime = performance.now() - t4 - } catch (e: any) { - measurements.globalClientError = e.message - } - } - - // Store for next request - if (!globalPgClient) { - globalPgClient = pgClient - } else { - // End the new client to avoid connection leak - await pgClient.end() - } - - measurements.totalTime = performance.now() - startTime - measurements.NODE_ENV = process.env.NODE_ENV - measurements.timestamp = new Date().toISOString() - - return NextResponse.json(measurements) -} diff --git a/apps/sim/socket-server/database/operations.ts b/apps/sim/socket-server/database/operations.ts index 388d6610a3..09b25ced12 100644 --- a/apps/sim/socket-server/database/operations.ts +++ b/apps/sim/socket-server/database/operations.ts @@ -15,9 +15,8 @@ const socketDb = drizzle( prepare: false, idle_timeout: 10, connect_timeout: 20, - max: 25, + max: 15, onnotice: () => {}, - debug: false, }), { schema } ) diff --git a/apps/sim/socket-server/rooms/manager.ts b/apps/sim/socket-server/rooms/manager.ts index b36ac85a77..0e890761cd 100644 --- a/apps/sim/socket-server/rooms/manager.ts +++ b/apps/sim/socket-server/rooms/manager.ts @@ -13,7 +13,7 @@ const db = drizzle( prepare: false, idle_timeout: 15, connect_timeout: 20, - max: 5, + max: 3, onnotice: () => {}, }), { schema } diff --git a/packages/db/index.ts b/packages/db/index.ts index 3b52d72d3e..7549eacbf7 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -14,16 +14,8 @@ const postgresClient = postgres(connectionString, { prepare: false, idle_timeout: 20, connect_timeout: 30, - max: 80, + max: 30, onnotice: () => {}, }) -const drizzleClient = drizzle(postgresClient, { schema }) - -declare global { - // eslint-disable-next-line no-var - var database: PostgresJsDatabase | undefined -} - -export const db = globalThis.database || drizzleClient -globalThis.database = db +export const db = drizzle(postgresClient, { schema })