fix: enhance health check functionality with detailed memory diagnostics and stable liveness probe
This commit is contained in:
+26
-35
@@ -1,10 +1,21 @@
|
||||
import v8 from 'node:v8';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextRequest } from 'next/server';
|
||||
import { logger } from '@/lib/logger';
|
||||
|
||||
// Health check thresholds
|
||||
const MEMORY_WARNING_THRESHOLD = 0.85; // 85% heap usage
|
||||
const MEMORY_CRITICAL_THRESHOLD = 0.95; // 95% heap usage
|
||||
const MEMORY_WARNING_THRESHOLD = 0.85;
|
||||
const MEMORY_CRITICAL_THRESHOLD = 0.95;
|
||||
|
||||
function getHeapUsagePercent(heapUsed: number, heapTotal: number): number {
|
||||
const heapSizeLimit = v8.getHeapStatistics().heap_size_limit;
|
||||
const denominator = heapSizeLimit > 0 ? heapSizeLimit : heapTotal;
|
||||
|
||||
if (denominator <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (heapUsed / denominator) * 100;
|
||||
}
|
||||
|
||||
interface HealthStatus {
|
||||
status: 'healthy' | 'degraded' | 'unhealthy';
|
||||
@@ -14,6 +25,7 @@ interface HealthStatus {
|
||||
memory?: {
|
||||
heapUsed: number;
|
||||
heapTotal: number;
|
||||
heapSizeLimit: number;
|
||||
rss: number;
|
||||
external: number;
|
||||
heapUsagePercent: number;
|
||||
@@ -27,14 +39,9 @@ interface HealthStatus {
|
||||
/**
|
||||
* Health check endpoint for container orchestration
|
||||
*
|
||||
* GET /api/health - Basic health check (returns 200 OK or 503 Service Unavailable)
|
||||
* GET /api/health?detailed=true - Detailed diagnostics with memory stats
|
||||
* HEAD /api/health - Lightweight health check (status code only)
|
||||
*
|
||||
* Health status based on Node.js heap usage:
|
||||
* - Healthy (200): < 85% heap usage
|
||||
* - Degraded (200): 85-95% heap usage (warnings in detailed mode)
|
||||
* - Unhealthy (503): > 95% heap usage
|
||||
* GET /api/health - Liveness probe for container orchestration
|
||||
* GET /api/health?detailed=true - Diagnostics with advisory memory warnings
|
||||
* HEAD /api/health - Lightweight liveness probe (status code only)
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const searchParams = request.nextUrl.searchParams;
|
||||
@@ -43,38 +50,31 @@ export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const timestamp = new Date().toISOString();
|
||||
const memUsage = process.memoryUsage();
|
||||
const heapUsagePercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
|
||||
|
||||
// Determine health status based on memory usage
|
||||
const heapSizeLimit = v8.getHeapStatistics().heap_size_limit;
|
||||
const heapUsagePercent = getHeapUsagePercent(memUsage.heapUsed, memUsage.heapTotal);
|
||||
let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy';
|
||||
const warnings: string[] = [];
|
||||
let httpStatus = 200;
|
||||
|
||||
if (heapUsagePercent >= MEMORY_CRITICAL_THRESHOLD * 100) {
|
||||
status = 'unhealthy';
|
||||
httpStatus = 503;
|
||||
status = 'degraded';
|
||||
warnings.push(`V8 heap usage is very high: ${heapUsagePercent.toFixed(1)}% of heap limit`);
|
||||
} else if (heapUsagePercent >= MEMORY_WARNING_THRESHOLD * 100) {
|
||||
status = 'degraded';
|
||||
warnings.push(`Memory usage high: ${heapUsagePercent.toFixed(1)}%`);
|
||||
warnings.push(`V8 heap usage is high: ${heapUsagePercent.toFixed(1)}% of heap limit`);
|
||||
}
|
||||
|
||||
// Build response
|
||||
const response: HealthStatus = {
|
||||
status,
|
||||
status: detailed ? status : 'healthy',
|
||||
timestamp,
|
||||
};
|
||||
|
||||
if (status === 'unhealthy') {
|
||||
response.reason = `Memory usage critical: ${heapUsagePercent.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
// Add detailed information if requested
|
||||
if (detailed) {
|
||||
response.uptime = process.uptime();
|
||||
response.version = process.env.npm_package_version || '0.1.0';
|
||||
response.memory = {
|
||||
heapUsed: memUsage.heapUsed,
|
||||
heapTotal: memUsage.heapTotal,
|
||||
heapSizeLimit,
|
||||
rss: memUsage.rss,
|
||||
external: memUsage.external,
|
||||
heapUsagePercent: Number(heapUsagePercent.toFixed(2)),
|
||||
@@ -87,10 +87,8 @@ export async function GET(request: NextRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Health check', { status, detailed });
|
||||
|
||||
return NextResponse.json(response, {
|
||||
status: httpStatus,
|
||||
status: 200,
|
||||
headers: {
|
||||
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
@@ -116,13 +114,6 @@ export async function GET(request: NextRequest) {
|
||||
*/
|
||||
export async function HEAD() {
|
||||
try {
|
||||
const memUsage = process.memoryUsage();
|
||||
const heapUsagePercent = (memUsage.heapUsed / memUsage.heapTotal) * 100;
|
||||
|
||||
if (heapUsagePercent >= MEMORY_CRITICAL_THRESHOLD * 100) {
|
||||
return new Response(null, { status: 503 });
|
||||
}
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
} catch {
|
||||
return new Response(null, { status: 503 });
|
||||
|
||||
Reference in New Issue
Block a user