-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
feat: migrate Prisma from 6.14.0 to 7.7.0 with driver adapters #4469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ed41f0a
023c3fd
93aa053
8b684e1
737ad56
c97cbcc
aa90db9
f5ce2bc
8c986db
82f198f
9a3e8d0
e101f8e
d01d438
aafb736
7fa3a16
4e6461a
4b5db51
5365936
d35bf04
5f4c41a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/core": patch | ||
| --- | ||
|
|
||
| Fix: ConsoleInterceptor now delegates to original console methods to preserve log chain when other interceptors (like Sentry) are present. (#2900) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/cli-v3": patch | ||
| --- | ||
|
|
||
| Fix: Native build server failed with Docker Hub rate limits. Added support for checking checking `DOCKER_USERNAME` and `DOCKER_PASSWORD` in environment variables and logging into Docker Hub before building. (#2911) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/cli-v3": patch | ||
| --- | ||
|
|
||
| Fix: Ignore engine checks during deployment install phase to prevent failure on build server when Node version mismatch exists. (#2913) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/cli-v3": patch | ||
| --- | ||
|
|
||
| Fix: `trigger.dev dev` command left orphaned worker processes when exited via Ctrl+C (SIGINT). Added signal handlers to ensure proper cleanup of child processes and lockfiles. (#2909) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/cli-v3": patch | ||
| --- | ||
|
|
||
| Fix Sentry OOM: Allow disabling `source-map-support` via `TRIGGER_SOURCE_MAPS=false`. Also supports `node` for native source maps. (#2920) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: fix | ||
| --- | ||
|
|
||
| Runs and sessions replication services now auto-recover from stream errors (e.g. after a Postgres failover) instead of silently leaving replication stopped. Behaviour is configurable per service — reconnect (default), exit so a process supervisor can restart the host, or log. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ import { | |
| type PrismaTransactionClient, | ||
| type PrismaTransactionOptions, | ||
| } from "@trigger.dev/database"; | ||
| import { PrismaPg } from "@prisma/adapter-pg"; | ||
| import { createHash } from "node:crypto"; | ||
| import invariant from "tiny-invariant"; | ||
| import { z } from "zod"; | ||
| import { env } from "./env.server"; | ||
|
|
@@ -127,21 +129,30 @@ function getClient() { | |
| const { DATABASE_URL } = process.env; | ||
| invariant(typeof DATABASE_URL === "string", "DATABASE_URL env var not set"); | ||
|
|
||
| const databaseUrl = extendQueryParams(DATABASE_URL, { | ||
| connection_limit: env.DATABASE_CONNECTION_LIMIT.toString(), | ||
| pool_timeout: env.DATABASE_POOL_TIMEOUT.toString(), | ||
| connection_timeout: env.DATABASE_CONNECTION_TIMEOUT.toString(), | ||
| application_name: env.SERVICE_NAME, | ||
| }); | ||
| const databaseUrl = new URL(DATABASE_URL); | ||
|
|
||
| // Set application_name as a query param on the connection string (pg understands this) | ||
| databaseUrl.searchParams.set("application_name", env.SERVICE_NAME); | ||
|
|
||
| console.log(`🔌 setting up prisma client to ${redactUrlSecrets(databaseUrl)}`); | ||
|
|
||
| const client = new PrismaClient({ | ||
| datasources: { | ||
| db: { | ||
| url: databaseUrl.href, | ||
| }, | ||
| const adapter = new PrismaPg( | ||
| { | ||
| connectionString: databaseUrl.href, | ||
| max: env.DATABASE_CONNECTION_LIMIT, | ||
| idleTimeoutMillis: env.DATABASE_CONNECTION_TIMEOUT * 1000, | ||
| connectionTimeoutMillis: env.DATABASE_CONNECTION_TIMEOUT * 1000, | ||
| }, | ||
| { | ||
| // Generate deterministic prepared statement names from query SQL so PostgreSQL | ||
| // can reuse cached query plans. Without this, every query uses an anonymous | ||
| // prepared statement that PG must parse and plan from scratch each time. | ||
| statementNameGenerator: (query) => `p_${createHash("sha256").update(query.sql).digest("hex").slice(0, 16)}`, | ||
| } | ||
| ); | ||
|
Comment on lines
+146
to
+152
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 Deterministic prepared-statement names rely on SQL text alone
Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| const client = new PrismaClient({ | ||
| adapter, | ||
| log: [ | ||
| // events | ||
| { | ||
|
|
@@ -251,21 +262,25 @@ function getReplicaClient() { | |
| return; | ||
| } | ||
|
|
||
| const replicaUrl = extendQueryParams(env.DATABASE_READ_REPLICA_URL, { | ||
| connection_limit: env.DATABASE_CONNECTION_LIMIT.toString(), | ||
| pool_timeout: env.DATABASE_POOL_TIMEOUT.toString(), | ||
| connection_timeout: env.DATABASE_CONNECTION_TIMEOUT.toString(), | ||
| application_name: env.SERVICE_NAME, | ||
| }); | ||
| const replicaUrl = new URL(env.DATABASE_READ_REPLICA_URL); | ||
| replicaUrl.searchParams.set("application_name", env.SERVICE_NAME); | ||
|
|
||
| console.log(`🔌 setting up read replica connection to ${redactUrlSecrets(replicaUrl)}`); | ||
|
|
||
| const replicaClient = new PrismaClient({ | ||
| datasources: { | ||
| db: { | ||
| url: replicaUrl.href, | ||
| }, | ||
| const adapter = new PrismaPg( | ||
| { | ||
| connectionString: replicaUrl.href, | ||
| max: env.DATABASE_CONNECTION_LIMIT, | ||
| idleTimeoutMillis: env.DATABASE_CONNECTION_TIMEOUT * 1000, | ||
| connectionTimeoutMillis: env.DATABASE_CONNECTION_TIMEOUT * 1000, | ||
| }, | ||
| { | ||
| statementNameGenerator: (query) => `p_${createHash("sha256").update(query.sql).digest("hex").slice(0, 16)}`, | ||
| } | ||
| ); | ||
|
|
||
| const replicaClient = new PrismaClient({ | ||
| adapter, | ||
| log: [ | ||
| // events | ||
| { | ||
|
|
@@ -368,19 +383,6 @@ function getReplicaClient() { | |
| return replicaClient; | ||
| } | ||
|
|
||
| function extendQueryParams(hrefOrUrl: string | URL, queryParams: Record<string, string>) { | ||
| const url = new URL(hrefOrUrl); | ||
| const query = url.searchParams; | ||
|
|
||
| for (const [key, val] of Object.entries(queryParams)) { | ||
| query.set(key, val); | ||
| } | ||
|
|
||
| url.search = query.toString(); | ||
|
|
||
| return url; | ||
| } | ||
|
|
||
| function redactUrlSecrets(hrefOrUrl: string | URL) { | ||
| const url = new URL(hrefOrUrl); | ||
| url.password = ""; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,4 @@ | ||
| import { LoaderFunctionArgs } from "@remix-run/server-runtime"; | ||
| import { prisma } from "~/db.server"; | ||
| import { metricsRegister } from "~/metrics.server"; | ||
|
|
||
| export async function loader({ request }: LoaderFunctionArgs) { | ||
|
|
@@ -13,14 +12,9 @@ export async function loader({ request }: LoaderFunctionArgs) { | |
| } | ||
| } | ||
|
|
||
| // We need to remove empty lines from the prisma metrics, grafana doesn't like them | ||
| const prismaMetrics = (await prisma.$metrics.prometheus()).replace(/^\s*[\r\n]/gm, ""); | ||
| const coreMetrics = await metricsRegister.metrics(); | ||
|
|
||
| // Order matters, core metrics end with `# EOF`, prisma metrics don't | ||
| const metrics = prismaMetrics + coreMetrics; | ||
|
|
||
| return new Response(metrics, { | ||
| return new Response(coreMetrics, { | ||
| headers: { | ||
| "Content-Type": metricsRegister.contentType, | ||
| }, | ||
|
Comment on lines
15
to
20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔍 Prisma pool/metric observability removed with no replacement Dropping the (Refers to lines 15-21) Was this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Database pool wait timeout setting is silently ignored after the connection change
The configured pool wait limit is no longer applied anywhere (its use was removed when building the connection settings at
apps/webapp/app/db.server.ts:139-152), and the connect timeout value is additionally reused as the idle-connection timeout, so two documented settings now behave differently than configured.Impact: Operators who tuned the pool wait or connection timeouts will get different behaviour than their configuration says, including connections being closed while idle sooner than before.
Mapping details
Old code appended
connection_limit,pool_timeout,connection_timeoutquery params to the connection string. The new adapter config only setsmax,idleTimeoutMillisandconnectionTimeoutMillis, all derived fromDATABASE_CONNECTION_LIMIT/DATABASE_CONNECTION_TIMEOUT.env.DATABASE_POOL_TIMEOUT(default 60s,apps/webapp/app/env.server.ts:103) is now unused; pool-acquire waits are governed byconnectionTimeoutMillis(20s default) instead.idleTimeoutMillis: env.DATABASE_CONNECTION_TIMEOUT * 1000reuses a connect timeout as an idle timeout — semantically unrelated knobs, so raising the connect timeout now also keeps idle connections open longer (and vice versa).The same pattern is duplicated for the read replica at
apps/webapp/app/db.server.ts:270-280.Prompt for agents
Was this helpful? React with 👍 or 👎 to provide feedback.