fix race condition issue
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Helper transaksi Serializable + retry. Single source of truth supaya semua
|
||||
* operasi yang rawan race (join/cancel trip, refund, payment) konsisten:
|
||||
* - Isolation level Serializable → Postgres SSI mendeteksi konflik.
|
||||
* - Retry otomatis saat serialization failure (Prisma error code `P2034`).
|
||||
*/
|
||||
import { Prisma } from "@/app/generated/prisma/client";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
const SERIAL_TX_ATTEMPTS = 6;
|
||||
|
||||
export function isSerializationConflict(err: unknown): boolean {
|
||||
return (
|
||||
typeof err === "object" &&
|
||||
err !== null &&
|
||||
"code" in err &&
|
||||
(err as { code: string }).code === "P2034"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jalankan `fn` dalam transaksi Serializable. Kalau gagal karena konflik
|
||||
* serialisasi, ulang sampai `SERIAL_TX_ATTEMPTS` kali. Error bisnis (non-P2034)
|
||||
* langsung dilempar tanpa retry.
|
||||
*/
|
||||
export async function runSerializable<T>(
|
||||
fn: (tx: Prisma.TransactionClient) => Promise<T>,
|
||||
fallbackMessage = "Operasi sedang ramai. Coba lagi sebentar."
|
||||
): Promise<T> {
|
||||
let lastErr: unknown;
|
||||
for (let attempt = 0; attempt < SERIAL_TX_ATTEMPTS; attempt++) {
|
||||
try {
|
||||
return await prisma.$transaction(fn, {
|
||||
isolationLevel: Prisma.TransactionIsolationLevel.Serializable,
|
||||
maxWait: 5000,
|
||||
timeout: 15000,
|
||||
});
|
||||
} catch (e) {
|
||||
lastErr = e;
|
||||
if (isSerializationConflict(e) && attempt < SERIAL_TX_ATTEMPTS - 1) {
|
||||
continue;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
throw lastErr instanceof Error ? lastErr : new Error(fallbackMessage);
|
||||
}
|
||||
Reference in New Issue
Block a user