feat: support optional read replicas (#2540)

This commit is contained in:
Lucas Smith 2026-02-25 19:07:02 +11:00 committed by GitHub
parent c112392da9
commit 6f5014a561
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 38 additions and 3 deletions

10
package-lock.json generated
View file

@ -18,6 +18,7 @@
"@libpdf/core": "^0.2.9",
"@lingui/conf": "^5.6.0",
"@lingui/core": "^5.6.0",
"@prisma/extension-read-replicas": "^0.4.1",
"ai": "^5.0.104",
"cron-parser": "^5.5.0",
"luxon": "^3.7.2",
@ -12193,6 +12194,15 @@
"integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==",
"license": "Apache-2.0"
},
"node_modules/@prisma/extension-read-replicas": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@prisma/extension-read-replicas/-/extension-read-replicas-0.4.1.tgz",
"integrity": "sha512-mCMDloqUKUwx2o5uedTs1FHX3Nxdt1GdRMoeyp1JggjiwOALmIYWhxfIN08M2BZ0w8SKwvJqicJZMjkQYkkijw==",
"license": "Apache-2.0",
"peerDependencies": {
"@prisma/client": "^6.5.0"
}
},
"node_modules/@prisma/fetch-engine": {
"version": "6.19.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz",

View file

@ -89,6 +89,7 @@
"@libpdf/core": "^0.2.9",
"@lingui/conf": "^5.6.0",
"@lingui/core": "^5.6.0",
"@prisma/extension-read-replicas": "^0.4.1",
"ai": "^5.0.104",
"cron-parser": "^5.5.0",
"luxon": "^3.7.2",

View file

@ -2,6 +2,7 @@ import { expect, test } from '@playwright/test';
import { WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { alphaid } from '@documenso/lib/universal/id';
import { prisma } from '@documenso/prisma';
import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users';
@ -279,8 +280,8 @@ test('[WEBHOOKS]: cannot see unrelated webhooks', async ({ page }) => {
const user1Data = await seedUser();
const user2Data = await seedUser();
const webhookUrl1 = `https://example.com/webhook-team1-${Date.now()}`;
const webhookUrl2 = `https://example.com/webhook-team2-${Date.now()}`;
const webhookUrl1 = `https://example.com/webhook-team1-${alphaid(12)}`;
const webhookUrl2 = `https://example.com/webhook-team2-${alphaid(12)}`;
// Create webhooks for both teams with DOCUMENT_CREATED event
const webhook1 = await seedWebhook({

View file

@ -1,5 +1,6 @@
/// <reference types="@documenso/prisma/types/types.d.ts" />
import { PrismaClient } from '@prisma/client';
import { readReplicas } from '@prisma/extension-read-replicas';
import { Kysely, PostgresAdapter, PostgresIntrospector, PostgresQueryCompiler } from 'kysely';
import kyselyExtension from 'prisma-extension-kysely';
@ -7,7 +8,7 @@ import type { DB } from './generated/types';
import { getDatabaseUrl } from './helper';
import { remember } from './utils/remember';
export const prisma = remember(
const prisma = remember(
'prisma',
() =>
new PrismaClient({
@ -65,4 +66,25 @@ export const prismaWithLogging = remember('prismaWithLogging', () => {
return client;
});
export const prismaWithReplicas = remember('prismaWithReplicas', () => {
if (!process.env.NEXT_PRIVATE_DATABASE_REPLICA_URLS) {
return prisma;
}
const replicaUrls = process.env.NEXT_PRIVATE_DATABASE_REPLICA_URLS.split(',').map((url) =>
url.trim(),
);
// !: Nasty hack, means we can't do any fancy $primary/$replica queries
// !: but it is acceptable since not all setups will have replicas anyway.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return prisma.$extends(
readReplicas({
url: replicaUrls,
}),
) as unknown as typeof prisma;
});
export { prismaWithReplicas as prisma };
export { sql } from 'kysely';

View file

@ -52,6 +52,7 @@
"NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT",
"NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY",
"NEXT_PRIVATE_DATABASE_URL",
"NEXT_PRIVATE_DATABASE_REPLICA_URLS",
"NEXT_PRIVATE_DIRECT_DATABASE_URL",
"NEXT_PRIVATE_LOGGER_FILE_PATH",
"NEXT_PRIVATE_SIGNING_TRANSPORT",