mirror of
https://github.com/polaroi8d/cactoide.git
synced 2026-03-21 21:55:27 +00:00
feat: fail fast if db not connecting
This commit is contained in:
@@ -1,8 +1,30 @@
|
||||
// src/hooks.server.ts
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import { generateUserId } from '$lib/generateUserId.js';
|
||||
import { ensureDatabaseConnection } from '$lib/database/healthCheck';
|
||||
|
||||
// Global flag to track if database health check has been performed
|
||||
let dbHealthCheckPerformed = false;
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
// Perform database health check only once during application startup
|
||||
if (!dbHealthCheckPerformed) {
|
||||
try {
|
||||
await ensureDatabaseConnection({
|
||||
maxRetries: 3,
|
||||
baseDelay: 1000,
|
||||
maxDelay: 10000,
|
||||
timeout: 5000
|
||||
});
|
||||
dbHealthCheckPerformed = true;
|
||||
} catch (error) {
|
||||
console.error('Database health check failed:', error);
|
||||
// The ensureDatabaseConnection function will exit the process
|
||||
// if the database is unavailable, so this catch is just for safety
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const cactoideUserId = event.cookies.get('cactoideUserId');
|
||||
const userId = generateUserId();
|
||||
|
||||
|
||||
104
src/lib/database/healthCheck.ts
Normal file
104
src/lib/database/healthCheck.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import postgres from 'postgres';
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
interface HealthCheckOptions {
|
||||
maxRetries?: number;
|
||||
baseDelay?: number;
|
||||
maxDelay?: number;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
interface HealthCheckResult {
|
||||
success: boolean;
|
||||
error?: string;
|
||||
attempts: number;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a database health check with retry logic and exponential backoff
|
||||
*/
|
||||
export async function checkDatabaseHealth(
|
||||
options: HealthCheckOptions = {}
|
||||
): Promise<HealthCheckResult> {
|
||||
const {
|
||||
maxRetries = 3,
|
||||
baseDelay = 1000, // 1 second
|
||||
maxDelay = 10000, // 10 seconds
|
||||
timeout = 5000 // 5 seconds
|
||||
} = options;
|
||||
|
||||
if (!env.DATABASE_URL) {
|
||||
console.error('DATABASE_URL environment variable is not set');
|
||||
return {
|
||||
success: false,
|
||||
error: 'DATABASE_URL environment variable is not set',
|
||||
attempts: 0
|
||||
};
|
||||
}
|
||||
|
||||
let lastError: Error | null = null;
|
||||
|
||||
console.log(`Starting database health check (max retries: ${maxRetries})`);
|
||||
|
||||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||||
console.log(`Attempt ${attempt}/${maxRetries} - Testing database connection`);
|
||||
|
||||
try {
|
||||
// Create a new connection for the health check
|
||||
const client = postgres(env.DATABASE_URL, {
|
||||
max: 1,
|
||||
idle_timeout: 20,
|
||||
connect_timeout: timeout / 1000, // Convert to seconds
|
||||
onnotice: () => {} // Suppress notices
|
||||
});
|
||||
|
||||
// Test the connection with a simple query
|
||||
await client`SELECT 1 as health_check`;
|
||||
await client.end();
|
||||
|
||||
console.log(`Database connection successful on attempt ${attempt}.`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
attempts: attempt
|
||||
};
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.error(`Connection failed on attempt ${attempt}: ${errorMessage}`);
|
||||
|
||||
// Don't wait after the last attempt
|
||||
if (attempt < maxRetries) {
|
||||
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
||||
console.log(`Waiting ${delay}ms before retry...`);
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const finalError = lastError?.message || 'Unknown database connection error';
|
||||
|
||||
console.error(`All ${maxRetries} attempts failed. Final error: ${finalError}`);
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: finalError,
|
||||
attempts: maxRetries
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs database health check and exits the process if it fails
|
||||
*/
|
||||
export async function ensureDatabaseConnection(options?: HealthCheckOptions): Promise<void> {
|
||||
const result = await checkDatabaseHealth(options);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Database connection failed after all retry attempts');
|
||||
console.error(`Error: ${result.error}`);
|
||||
console.error(`Attempts made: ${result.attempts}`);
|
||||
console.error('Exiting application...');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user