44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
/**
|
|
* 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 und 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.
|
|
}
|
|
}
|
|
}
|