fix: prisma connection exhaustion & firebase lazy init
- prisma/schema.prisma: tambah binaryTargets debian & linux-musl untuk Docker - src/lib/prisma.ts: pakai global singleton di dev & prod, hapus eager $connect() - src/lib/firebase-admin.ts: lazy initialization agar tidak crash saat build time - .env.example: lengkap dengan semua env variable + connection_limit & pool_timeout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,115 +1,21 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
// Deklarasikan variabel global untuk menandai apakah listener sudah ditambahkan
|
||||
declare global {
|
||||
var prisma: PrismaClient;
|
||||
var prismaListenersAdded: boolean; // Flag untuk menandai listener
|
||||
var prisma: PrismaClient | undefined;
|
||||
}
|
||||
|
||||
let prisma: PrismaClient;
|
||||
|
||||
// Validasi DATABASE_URL sebelum inisialisasi
|
||||
if (!process.env.DATABASE_URL) {
|
||||
console.error('❌ ERROR: DATABASE_URL tidak ditemukan di environment variables!');
|
||||
console.error('');
|
||||
console.error('Current working directory:', process.cwd());
|
||||
console.error('Available environment variables:', Object.keys(process.env).sort().join(', '));
|
||||
console.error('');
|
||||
console.error('Solusi:');
|
||||
console.error(' 1. Pastikan file .env ada dan berisi DATABASE_URL, ATAU');
|
||||
console.error(' 2. Set DATABASE_URL di system environment:');
|
||||
console.error(' export DATABASE_URL="postgresql://user:pass@host:port/dbname"');
|
||||
console.error(' 3. Jika menggunakan systemd service, tambahkan di file service:');
|
||||
console.error(' [Service]');
|
||||
console.error(' Environment=DATABASE_URL=postgresql://...');
|
||||
console.error('');
|
||||
throw new Error('DATABASE_URL is required but not found in environment variables');
|
||||
throw new Error("DATABASE_URL is required but not found in environment variables");
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
prisma = new PrismaClient({
|
||||
// Reduce logging in production to improve performance
|
||||
log: ['error', 'warn'],
|
||||
datasources: {
|
||||
db: {
|
||||
url: process.env.DATABASE_URL,
|
||||
},
|
||||
},
|
||||
const prisma =
|
||||
global.prisma ??
|
||||
new PrismaClient({
|
||||
log: process.env.NODE_ENV === "development" ? ["error", "warn", "query"] : ["error", "warn"],
|
||||
});
|
||||
|
||||
// Explicitly connect to database dengan retry
|
||||
const maxRetries = 3;
|
||||
let retryCount = 0;
|
||||
|
||||
const connectWithRetry = async () => {
|
||||
while (retryCount < maxRetries) {
|
||||
try {
|
||||
await prisma.$connect();
|
||||
console.log('✅ PostgreSQL connected successfully');
|
||||
return;
|
||||
} catch (error) {
|
||||
retryCount++;
|
||||
const errorMsg = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.error(`❌ PostgreSQL connection attempt ${retryCount}/${maxRetries} failed:`, errorMsg);
|
||||
|
||||
if (retryCount >= maxRetries) {
|
||||
console.error('❌ All database connection attempts failed. Application will continue but database operations will fail.');
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Wait before retry (exponential backoff)
|
||||
const waitTime = Math.min(1000 * Math.pow(2, retryCount), 10000);
|
||||
console.log(`⏳ Retrying in ${waitTime}ms...`);
|
||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize connection (non-blocking)
|
||||
connectWithRetry().catch(err => {
|
||||
console.error('Failed to initialize database connection:', err);
|
||||
});
|
||||
|
||||
} else {
|
||||
if (!global.prisma) {
|
||||
global.prisma = new PrismaClient({
|
||||
log: ['error', 'warn', 'info', 'query'], // More verbose logging in development
|
||||
datasources: {
|
||||
db: {
|
||||
url: process.env.DATABASE_URL,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
prisma = global.prisma;
|
||||
}
|
||||
|
||||
// Tambahkan listener hanya jika belum ditambahkan sebelumnya
|
||||
if (!global.prismaListenersAdded) {
|
||||
// Handle graceful shutdown
|
||||
process.on("SIGINT", async () => {
|
||||
console.log("Received SIGINT signal. Closing database connections...");
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("SIGTERM", async () => {
|
||||
console.log("Received SIGTERM signal. Closing database connections...");
|
||||
await prisma.$disconnect();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Handle uncaught errors
|
||||
process.on("uncaughtException", async (error) => {
|
||||
if (error.message.includes("Prisma") || error.message.includes("database")) {
|
||||
console.error("Uncaught database error:", error);
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// Tandai bahwa listener sudah ditambahkan
|
||||
global.prismaListenersAdded = true;
|
||||
}
|
||||
// Selalu assign ke global agar hanya ada 1 instance (dev: cegah hot-reload, prod: cegah multiple instances)
|
||||
global.prisma = prisma;
|
||||
|
||||
export default prisma;
|
||||
export { prisma };
|
||||
|
||||
Reference in New Issue
Block a user