From eb2850beb1706bc2520f3f28441969d39a1ea0d7 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 26 Jun 2026 10:32:40 +0100 Subject: [PATCH] feat(dashboard-agent-db): run migrations over a direct (non-pooler) connection A transaction-mode pooler can't run the migrator (no advisory locks, no multi-statement DDL), so the agent datastore now resolves its migration connection from a dedicated DASHBOARD_AGENT_DIRECT_URL, falling back to the pooled DASHBOARD_AGENT_DATABASE_URL, then the main DIRECT_URL / DATABASE_URL for the single-database fallback. The application keeps using the pooled DASHBOARD_AGENT_DATABASE_URL. Only the migration entry points (drizzle.config.ts, migrate.mjs, migrate-status.mjs) changed; the runtime client is untouched. --- internal-packages/dashboard-agent-db/README.md | 13 ++++++++----- .../dashboard-agent-db/drizzle.config.ts | 8 ++++++-- .../dashboard-agent-db/migrate-status.mjs | 9 +++++++-- internal-packages/dashboard-agent-db/migrate.mjs | 14 ++++++++++---- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/internal-packages/dashboard-agent-db/README.md b/internal-packages/dashboard-agent-db/README.md index ff1bfbd0db4..fe33a538b5f 100644 --- a/internal-packages/dashboard-agent-db/README.md +++ b/internal-packages/dashboard-agent-db/README.md @@ -4,10 +4,13 @@ The conversation datastore for the in-dashboard agent, isolated from the main Prisma database. Drizzle (postgres-js) over a dedicated `trigger_dashboard_agent` Postgres schema. -- **Cloud:** a separate PlanetScale Postgres database (`DASHBOARD_AGENT_DATABASE_URL`), - reached over a standard pooled connection. -- **OSS / self-host:** falls back to the main `DATABASE_URL`; the tables live in - the dedicated `trigger_dashboard_agent` schema, isolated from Prisma's `public`. +- **Cloud:** a separate PlanetScale Postgres database. The app connects over a + pooled connection (`DASHBOARD_AGENT_DATABASE_URL`); migrations run over a direct + (non-pooler) connection (`DASHBOARD_AGENT_DIRECT_URL`), since a transaction-mode + pooler can't run the migrator. +- **OSS / self-host:** falls back to the main `DATABASE_URL` (and `DIRECT_URL` for + migrations); the tables live in the dedicated `trigger_dashboard_agent` schema, + isolated from Prisma's `public`. The schema is **foreign-key-free** — it references main entities (`organizationId`, `userId`) by id only, because in cloud it lives in a different database. @@ -36,7 +39,7 @@ of truth. ```bash pnpm run db:generate # generate SQL migration from src/schema.ts (offline) -pnpm run db:migrate # apply migrations (needs DASHBOARD_AGENT_DATABASE_URL or DATABASE_URL) +pnpm run db:migrate # apply migrations (direct url: DASHBOARD_AGENT_DIRECT_URL, falling back to DASHBOARD_AGENT_DATABASE_URL / DIRECT_URL / DATABASE_URL) ``` drizzle-kit is scoped to the `trigger_dashboard_agent` schema (`schemaFilter`), so diff --git a/internal-packages/dashboard-agent-db/drizzle.config.ts b/internal-packages/dashboard-agent-db/drizzle.config.ts index ddaddb9fd8f..8713b17047e 100644 --- a/internal-packages/dashboard-agent-db/drizzle.config.ts +++ b/internal-packages/dashboard-agent-db/drizzle.config.ts @@ -1,9 +1,13 @@ import { defineConfig } from "drizzle-kit"; -// Cloud points at the dedicated PlanetScale database; OSS falls back to the main -// DATABASE_URL (tables still land in the trigger_dashboard_agent schema). +// Migrations need a direct (non-pooler) connection; a transaction-mode pooler +// can't run the migrator. Prefer the agent's direct url, then its pooled url, +// then the main DIRECT_URL/DATABASE_URL (OSS single-database fallback; tables +// still land in the trigger_dashboard_agent schema). const url = + process.env.DASHBOARD_AGENT_DIRECT_URL ?? process.env.DASHBOARD_AGENT_DATABASE_URL ?? + process.env.DIRECT_URL ?? process.env.DATABASE_URL ?? "postgres://placeholder"; // generate is offline; a real url is only needed for migrate/studio diff --git a/internal-packages/dashboard-agent-db/migrate-status.mjs b/internal-packages/dashboard-agent-db/migrate-status.mjs index 8d8208fdb2e..431876aaf34 100644 --- a/internal-packages/dashboard-agent-db/migrate-status.mjs +++ b/internal-packages/dashboard-agent-db/migrate-status.mjs @@ -8,11 +8,16 @@ import postgres from "postgres"; const MIGRATIONS_SCHEMA = "drizzle"; const MIGRATIONS_TABLE = "__dashboard_agent_migrations"; -const connectionString = process.env.DASHBOARD_AGENT_DATABASE_URL ?? process.env.DATABASE_URL; +// Match migrate.mjs: a direct (non-pooler) connection, same precedence. +const connectionString = + process.env.DASHBOARD_AGENT_DIRECT_URL ?? + process.env.DASHBOARD_AGENT_DATABASE_URL ?? + process.env.DIRECT_URL ?? + process.env.DATABASE_URL; if (!connectionString) { console.error( - "[dashboard-agent-db] DASHBOARD_AGENT_DATABASE_URL / DATABASE_URL not set; cannot check status." + "[dashboard-agent-db] No database url set (DASHBOARD_AGENT_DIRECT_URL / DASHBOARD_AGENT_DATABASE_URL / DIRECT_URL / DATABASE_URL); cannot check status." ); process.exit(2); } diff --git a/internal-packages/dashboard-agent-db/migrate.mjs b/internal-packages/dashboard-agent-db/migrate.mjs index d840707ddd5..ba4e4c6c803 100644 --- a/internal-packages/dashboard-agent-db/migrate.mjs +++ b/internal-packages/dashboard-agent-db/migrate.mjs @@ -10,13 +10,19 @@ import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; -// Cloud points at the dedicated dashboard-agent database; OSS falls back to the -// main DATABASE_URL (tables still land in the `trigger_dashboard_agent` schema). -const connectionString = process.env.DASHBOARD_AGENT_DATABASE_URL ?? process.env.DATABASE_URL; +// Migrations need a direct (non-pooler) connection; a transaction-mode pooler +// can't run the migrator. Prefer the agent's direct url, then its pooled url, +// then the main DIRECT_URL/DATABASE_URL (OSS single-database fallback; tables +// still land in the `trigger_dashboard_agent` schema). +const connectionString = + process.env.DASHBOARD_AGENT_DIRECT_URL ?? + process.env.DASHBOARD_AGENT_DATABASE_URL ?? + process.env.DIRECT_URL ?? + process.env.DATABASE_URL; if (!connectionString) { console.error( - "[dashboard-agent-db] DASHBOARD_AGENT_DATABASE_URL / DATABASE_URL not set; cannot migrate." + "[dashboard-agent-db] No database url set (DASHBOARD_AGENT_DIRECT_URL / DASHBOARD_AGENT_DATABASE_URL / DIRECT_URL / DATABASE_URL); cannot migrate." ); process.exit(1); }