Initial commit: Kalendartool (Next.js, Prisma, Docker)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
23
lib/db/client.ts
Normal file
23
lib/db/client.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
/**
|
||||
* Globaler Prisma-Client.
|
||||
*
|
||||
* In der Entwicklung startet Next.js bei Hot Reload mehrfach Modul-Instanzen;
|
||||
* ohne Caching wuerde jede Instanz eigene Verbindungen aufbauen und die
|
||||
* Datenbank mit Verbindungen ueberfluten. Daher wird die Instanz auf
|
||||
* `globalThis` gecacht (offizielles Prisma-Rezept fuer Next.js).
|
||||
*/
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined;
|
||||
};
|
||||
|
||||
export const prisma: PrismaClient =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['warn', 'error'] : ['error'],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
globalForPrisma.prisma = prisma;
|
||||
}
|
||||
43
lib/db/tx.ts
Normal file
43
lib/db/tx.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Transaktions-Helper: SERIALIZABLE mit Retry bei Serialisierungs-
|
||||
* konflikten (Prisma-Fehlercode P2034).
|
||||
*
|
||||
* Postgres bricht bei Parallelitaets-Anomalien unter SERIALIZABLE eine
|
||||
* der beteiligten Transaktionen ab. Wiederholbare Operationen (z. B.
|
||||
* Registrierung mit Invite-Verbrauch, Rollen-Aenderung) werden dann
|
||||
* einfach erneut ausgefuehrt - der Konflikt loest sich auf, weil die
|
||||
* andere Transaktion inzwischen committet hat.
|
||||
*/
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { prisma } from '@/lib/db/client';
|
||||
|
||||
/** Ist es ein wiederholbarer Serialisierungs-Konflikt? */
|
||||
function isSerializationError(error: unknown): boolean {
|
||||
return (
|
||||
error instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
error.code === 'P2034'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fuehrt die Operation in einer SERIALIZABLE-Transaktion aus und
|
||||
* wiederholt sie bei Serialisierungs-Konflikten (max. 3 Nachlaeufe).
|
||||
*/
|
||||
export async function withSerializableTx<T>(
|
||||
operation: (tx: Prisma.TransactionClient) => Promise<T>,
|
||||
maxRetries = 3,
|
||||
): Promise<T> {
|
||||
for (let attempt = 0; ; attempt++) {
|
||||
try {
|
||||
return await prisma.$transaction(operation, {
|
||||
isolationLevel: 'Serializable',
|
||||
});
|
||||
} catch (error) {
|
||||
if (attempt >= maxRetries || !isSerializationError(error)) {
|
||||
throw error;
|
||||
}
|
||||
// Naechster Versuch: Die abgebrochene Transaktion hat nichts
|
||||
// committet, ein erneuter Lauf sieht den aktuellen Stand.
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user