Skip to content

Commit 24a9151

Browse files
authored
Prisma 6.14.0 upgrade (#2444)
* Initial work on upgrading to 6.14.0 Set the output to node_modules still to make it easier * Use ./generated Prisma folder, update types to fix issues * Docker compose restart Clickhouse * Prisma instrumentation update * Docker * Removed database dockerignore file, add generated prisma client to the top-level one * Delete v3-catalog package.json * Resolved pnpm lock file * Log errors for very slow queries
1 parent 688b108 commit 24a9151

File tree

15 files changed

+315
-129
lines changed

15 files changed

+315
-129
lines changed

.cursor/rules/webapp.mdc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ alwaysApply: false
66

77
The main trigger.dev webapp, which powers it's API and dashboard and makes up the docker image that is produced as an OSS image, is a Remix 2.1.0 app that uses an express server, written in TypeScript. The following subsystems are either included in the webapp or are used by the webapp in another part of the monorepo:
88

9-
- `@trigger.dev/database` exports a Prisma 5.4.1 client that is used extensively in the webapp to access a PostgreSQL instance. The schema file is [schema.prisma](mdc:internal-packages/database/prisma/schema.prisma)
9+
- `@trigger.dev/database` exports a Prisma 6.14.0 client that is used extensively in the webapp to access a PostgreSQL instance. The schema file is [schema.prisma](mdc:internal-packages/database/prisma/schema.prisma)
1010
- `@trigger.dev/core` is a published package and is used to share code between the `@trigger.dev/sdk` and the webapp. It includes functionality but also a load of Zod schemas for data validation. When importing from `@trigger.dev/core` in the webapp, we never import the root `@trigger.dev/core` path, instead we favor one of the subpath exports that you can find in [package.json](mdc:packages/core/package.json)
1111
- `@internal/run-engine` has all the code needed to trigger a run and take it through it's lifecycle to completion.
1212
- `@trigger.dev/redis-worker` is a custom redis based background job/worker system that's used in the webapp and also used inside the run engine.
@@ -31,7 +31,10 @@ We originally the Trigger.dev "Run Engine" not as a single system, but just spre
3131
- The batch trigger API endpoint is [api.v1.tasks.batch.ts](mdc:apps/webapp/app/routes/api.v1.tasks.batch.ts)
3232
- Setup code for the prisma client is in [db.server.ts](mdc:apps/webapp/app/db.server.ts)
3333
- The run engine is configured in [runEngine.server.ts](mdc:apps/webapp/app/v3/runEngine.server.ts)
34-
- All the "services" that are found in app/v3/services/**/*.server.ts
34+
- All the "services" that are found in app/v3/services/\*_/_.server.ts
3535
- The code for the TaskEvent data, which is the otel data sent from tasks to our servers, is in both the [eventRepository.server.ts](mdc:apps/webapp/app/v3/eventRepository.server.ts) and also the [otlpExporter.server.ts](mdc:apps/webapp/app/v3/otlpExporter.server.ts). The otel endpoints which are hit from production and development otel exporters is [otel.v1.logs.ts](mdc:apps/webapp/app/routes/otel.v1.logs.ts) and [otel.v1.traces.ts](mdc:apps/webapp/app/routes/otel.v1.traces.ts)
36-
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/**/*.server.ts
36+
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/\*_/_.server.ts
3737

38+
- All the "services" that are found in app/v3/services/\*_/_.server.ts
39+
- The code for the TaskEvent data, which is the otel data sent from tasks to our servers, is in both the [eventRepository.server.ts](mdc:apps/webapp/app/v3/eventRepository.server.ts) and also the [otlpExporter.server.ts](mdc:apps/webapp/app/v3/otlpExporter.server.ts). The otel endpoints which are hit from production and development otel exporters is [otel.v1.logs.ts](mdc:apps/webapp/app/routes/otel.v1.logs.ts) and [otel.v1.traces.ts](mdc:apps/webapp/app/routes/otel.v1.traces.ts)
40+
- We use "presenters" to move more complex loader code into a class, and you can find those are app/v3/presenters/\*_/_.server.ts

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
**/dist
1717
**/node_modules
1818

19+
**/generated/prisma
20+
1921
apps/webapp/build
2022
apps/webapp/public/build
2123

apps/webapp/app/db.server.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
import {
22
Prisma,
33
PrismaClient,
4-
PrismaClientOrTransaction,
5-
PrismaReplicaClient,
6-
PrismaTransactionClient,
7-
PrismaTransactionOptions,
4+
$transaction as transac,
5+
type PrismaClientOrTransaction,
6+
type PrismaReplicaClient,
7+
type PrismaTransactionClient,
8+
type PrismaTransactionOptions,
89
} from "@trigger.dev/database";
910
import invariant from "tiny-invariant";
1011
import { z } from "zod";
1112
import { env } from "./env.server";
1213
import { logger } from "./services/logger.server";
1314
import { isValidDatabaseUrl } from "./utils/db";
1415
import { singleton } from "./utils/singleton";
15-
import { $transaction as transac } from "@trigger.dev/database";
1616
import { startActiveSpan } from "./v3/tracer.server";
1717
import { Span } from "@opentelemetry/api";
18+
import { queryPerformanceMonitor } from "./utils/queryPerformanceMonitor.server";
1819

1920
export type {
2021
PrismaTransactionClient,
@@ -153,13 +154,19 @@ function getClient() {
153154
},
154155
]
155156
: []) satisfies Prisma.LogDefinition[]),
156-
// verbose
157-
...((process.env.VERBOSE_PRISMA_LOGS === "1"
157+
// Query performance monitoring
158+
...((process.env.VERBOSE_PRISMA_LOGS === "1" ||
159+
process.env.VERY_SLOW_QUERY_THRESHOLD_MS !== undefined
158160
? [
159161
{
160162
emit: "event",
161163
level: "query",
162164
},
165+
]
166+
: []) satisfies Prisma.LogDefinition[]),
167+
// verbose
168+
...((process.env.VERBOSE_PRISMA_LOGS === "1"
169+
? [
163170
{
164171
emit: "stdout",
165172
level: "query",
@@ -206,6 +213,11 @@ function getClient() {
206213
});
207214
}
208215

216+
// Add query performance monitoring
217+
client.$on("query", (log) => {
218+
queryPerformanceMonitor.onQuery("writer", log);
219+
});
220+
209221
// connect eagerly
210222
client.$connect();
211223

@@ -265,13 +277,19 @@ function getReplicaClient() {
265277
},
266278
]
267279
: []) satisfies Prisma.LogDefinition[]),
268-
// verbose
269-
...((process.env.VERBOSE_PRISMA_LOGS === "1"
280+
// Query performance monitoring
281+
...((process.env.VERBOSE_PRISMA_LOGS === "1" ||
282+
process.env.VERY_SLOW_QUERY_THRESHOLD_MS !== undefined
270283
? [
271284
{
272285
emit: "event",
273286
level: "query",
274287
},
288+
]
289+
: []) satisfies Prisma.LogDefinition[]),
290+
// verbose
291+
...((process.env.VERBOSE_PRISMA_LOGS === "1"
292+
? [
275293
{
276294
emit: "stdout",
277295
level: "query",
@@ -317,6 +335,11 @@ function getReplicaClient() {
317335
});
318336
}
319337

338+
// Add query performance monitoring for replica client
339+
replicaClient.$on("query", (log) => {
340+
queryPerformanceMonitor.onQuery("replica", log);
341+
});
342+
320343
// connect eagerly
321344
replicaClient.$connect();
322345

apps/webapp/app/env.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,8 @@ const EnvironmentSchema = z.object({
10801080
AI_RUN_FILTER_MODEL: z.string().optional(),
10811081

10821082
EVENT_LOOP_MONITOR_THRESHOLD_MS: z.coerce.number().int().default(100),
1083+
1084+
VERY_SLOW_QUERY_THRESHOLD_MS: z.coerce.number().int().optional(),
10831085
});
10841086

10851087
export type Environment = z.infer<typeof EnvironmentSchema>;

apps/webapp/app/routes/api.v1.projects.$projectRef.background-workers.$envSlug.$version.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
8282
});
8383
}
8484

85-
function decompressContent(compressedBuffer: Buffer): string {
86-
// First, we need to decode the base64 Buffer to get the actual compressed data
87-
const decodedBuffer = Buffer.from(compressedBuffer.toString("utf-8"), "base64");
85+
function decompressContent(compressedBuffer: Uint8Array): string {
86+
// Convert Uint8Array to Buffer and decode base64 in one step
87+
const decodedBuffer = Buffer.from(Buffer.from(compressedBuffer).toString("utf-8"), "base64");
8888

8989
// Decompress the data
9090
const decompressedData = zlib.inflateSync(decodedBuffer);
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { env } from "~/env.server";
2+
import { logger } from "~/services/logger.server";
3+
4+
export interface QueryPerformanceConfig {
5+
verySlowQueryThreshold?: number; // ms
6+
maxQueryLogLength: number;
7+
}
8+
9+
export class QueryPerformanceMonitor {
10+
private config: QueryPerformanceConfig;
11+
12+
constructor(config: Partial<QueryPerformanceConfig> = {}) {
13+
this.config = {
14+
maxQueryLogLength: 1000,
15+
...config,
16+
};
17+
}
18+
19+
onQuery(
20+
clientType: "writer" | "replica",
21+
log: {
22+
duration: number;
23+
query: string;
24+
params: string;
25+
target: string;
26+
timestamp: Date;
27+
}
28+
) {
29+
if (this.config.verySlowQueryThreshold === undefined) {
30+
return;
31+
}
32+
33+
const { duration, query, params, target, timestamp } = log;
34+
35+
// Only log very slow queries as errors
36+
if (duration > this.config.verySlowQueryThreshold) {
37+
// Truncate long queries for readability
38+
const truncatedQuery =
39+
query.length > this.config.maxQueryLogLength
40+
? query.substring(0, this.config.maxQueryLogLength) + "..."
41+
: query;
42+
43+
logger.error("Prisma: very slow database query", {
44+
clientType,
45+
durationMs: duration,
46+
query: truncatedQuery,
47+
target,
48+
timestamp,
49+
paramCount: this.countParams(query),
50+
hasParams: params !== "[]" && params !== "",
51+
});
52+
}
53+
}
54+
55+
private countParams(query: string): number {
56+
// Count the number of $1, $2, etc. parameters in the query
57+
const paramMatches = query.match(/\$\d+/g);
58+
return paramMatches ? paramMatches.length : 0;
59+
}
60+
}
61+
62+
export const queryPerformanceMonitor = new QueryPerformanceMonitor({
63+
verySlowQueryThreshold: env.VERY_SLOW_QUERY_THRESHOLD_MS,
64+
});

apps/webapp/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
"@opentelemetry/sdk-trace-node": "1.25.1",
7979
"@opentelemetry/semantic-conventions": "1.25.1",
8080
"@popperjs/core": "^2.11.8",
81-
"@prisma/instrumentation": "^5.11.0",
81+
"@prisma/instrumentation": "^6.14.0",
8282
"@radix-ui/react-accordion": "^1.2.11",
8383
"@radix-ui/react-alert-dialog": "^1.0.4",
8484
"@radix-ui/react-dialog": "^1.0.3",
@@ -137,6 +137,7 @@
137137
"dotenv": "^16.4.5",
138138
"effect": "^3.11.7",
139139
"emails": "workspace:*",
140+
"eventsource": "^4.0.0",
140141
"evt": "^2.4.13",
141142
"express": "4.20.0",
142143
"framer-motion": "^10.12.11",
@@ -214,9 +215,9 @@
214215
"@remix-run/dev": "2.1.0",
215216
"@remix-run/eslint-config": "2.1.0",
216217
"@remix-run/testing": "^2.1.0",
218+
"@sentry/cli": "2.50.2",
217219
"@swc/core": "^1.3.4",
218220
"@swc/helpers": "^0.4.11",
219-
"@sentry/cli": "2.50.2",
220221
"@tailwindcss/forms": "^0.5.3",
221222
"@tailwindcss/typography": "^0.5.9",
222223
"@total-typescript/ts-reset": "^0.4.2",

docker/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ RUN corepack enable
3636
ENV NODE_ENV production
3737
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpm install --prod --no-frozen-lockfile
3838
COPY --from=pruner --chown=node:node /triggerdotdev/internal-packages/database/prisma/schema.prisma /triggerdotdev/internal-packages/database/prisma/schema.prisma
39-
# RUN pnpm add @prisma/client@5.1.1 -w
39+
# RUN pnpm add @prisma/client@6.14.0 -w
4040
ENV NPM_CONFIG_IGNORE_WORKSPACE_ROOT_CHECK true
41-
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpx prisma@5.4.1 generate --schema /triggerdotdev/internal-packages/database/prisma/schema.prisma
41+
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store pnpx prisma@6.14.0 generate --schema /triggerdotdev/internal-packages/database/prisma/schema.prisma
4242

4343
## Builder (builds the webapp)
4444
FROM base AS builder

docker/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ services:
7777

7878
clickhouse:
7979
image: bitnami/clickhouse:latest
80+
restart: always
8081
container_name: clickhouse
8182
environment:
8283
CLICKHOUSE_ADMIN_USER: default

internal-packages/database/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
node_modules
22
# Ensure the .env symlink is not removed by accident
33
!.env
4+
5+
generated/prisma

0 commit comments

Comments
 (0)