Files
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

152 lines
4.6 KiB
TypeScript

import type { IncomingMessage, ServerResponse } from 'node:http';
import { monitorEventLoopDelay } from 'node:perf_hooks';
import { getHeapStatistics } from 'node:v8';
import { MongoInternals } from 'meteor/mongo';
import { WebApp } from 'meteor/webapp';
import { SystemLogger } from '../lib/logger/system';
const { mongo } = MongoInternals.defaultRemoteCollectionDriver();
export function pingMongo() {
return mongo.db.command({ ping: 1 });
}
function setDefaultHeaders(res: ServerResponse) {
// Set headers to prevent any caching of the health check response.
res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
res.setHeader('Pragma', 'no-cache');
res.setHeader('Expires', '0');
res.setHeader('Content-Type', 'application/json');
}
/**
* DEPRECATED Liveness Probe (`/health`)
* Maintained for backward compatibility. Behaves like a simple liveness probe.
* @deprecated Update infrastructure to use /livez and /readyz.
*/
WebApp.rawHandlers.use('/health', (_req: IncomingMessage, res: ServerResponse) => {
setDefaultHeaders(res);
res.writeHead(200);
res.end(JSON.stringify({ status: 'ok' }));
});
/**
* Readiness Probe Configuration
* Defines tunable thresholds for readiness checks.
*/
const eventLoopHistogramLiveness = monitorEventLoopDelay();
eventLoopHistogramLiveness.enable();
const eventLoopHistogramReadiness = monitorEventLoopDelay();
eventLoopHistogramReadiness.enable();
const READINESS_THRESHOLDS = {
EVENT_LOOP_LAG_MS: Number.parseFloat(process.env.EVENT_LOOP_LAG_MS ?? '') || 70,
HEAP_USAGE_PERCENT: Number.parseFloat(process.env.HEAP_USAGE_PERCENT ?? '') || 0.85, // 85%
} as const;
/**
* Checks event loop lag using the provided histogram. Can optionally reset the histogram after reading.
* @param histogram - The event loop histogram to use.
* @returns The status and p99 lag.
*/
function checkEventLoopLag(histogram: ReturnType<typeof monitorEventLoopDelay>) {
const lagInNs = histogram.percentile(99);
const lagInMs = lagInNs / 1_000_000;
histogram.reset();
const lagValue = Number.parseFloat(lagInMs.toFixed(2));
if (lagInMs > READINESS_THRESHOLDS.EVENT_LOOP_LAG_MS) {
return { status: 'degraded', lagMs: lagValue };
}
return { status: 'ok', lagMs: lagValue };
}
/**
* Checks heap memory usage against a configured threshold.
* @returns The status and memory usage percentage.
*/
function checkMemoryUsage() {
const { heap_size_limit, used_heap_size } = getHeapStatistics();
const usageRatio = used_heap_size / heap_size_limit;
const usageValue = Number.parseFloat((usageRatio * 100).toFixed(2));
if (usageRatio > READINESS_THRESHOLDS.HEAP_USAGE_PERCENT) {
return { status: 'degraded', percentile: usageValue };
}
return { status: 'ok', percentile: usageValue };
}
async function checkMongo() {
try {
await pingMongo();
return { status: 'ok' };
} catch (err) {
return { status: 'degraded', error: err instanceof Error ? err.message : String(err) };
}
}
/**
* Performs the core health checks (memory, event loop) and returns the result.
* @param shouldResetHistogram - Controls whether the event loop histogram is reset.
*/
async function performHealthChecks(histogram: ReturnType<typeof monitorEventLoopDelay>) {
const checks = {
memory: checkMemoryUsage(),
eventLoop: checkEventLoopLag(histogram),
mongo: await checkMongo(),
};
const isHealthy = checks.memory.status === 'ok' && checks.eventLoop.status === 'ok' && checks.mongo.status === 'ok';
const statusCode = isHealthy ? 200 : 503;
const body = {
status: isHealthy ? 'ok' : 'unavailable',
checks,
};
return { statusCode, body, isHealthy };
}
/**
* Liveness Probe (`/livez`)
* Performs a non-destructive health check. A failure here indicates a pod is
* unrecoverable and should be restarted.
*/
WebApp.rawHandlers.use('/livez', async (_req: IncomingMessage, res: ServerResponse) => {
const { statusCode, body, isHealthy } = await performHealthChecks(eventLoopHistogramLiveness);
if (!isHealthy) {
SystemLogger.error({ msg: 'Liveness check failed', details: body });
}
setDefaultHeaders(res);
res.writeHead(statusCode);
res.end(JSON.stringify(body));
});
/**
* Readiness Probe Endpoint (`/readyz`)
* Performs a destructive health check, resetting the histogram for the next interval.
* A failure tells the orchestrator to stop sending traffic to this instance.
*/
WebApp.rawHandlers.use('/readyz', async (_req: IncomingMessage, res: ServerResponse) => {
const { statusCode, body, isHealthy } = await performHealthChecks(eventLoopHistogramReadiness);
if (!isHealthy) {
SystemLogger.warn({ msg: 'Readiness check failed', details: body });
}
setDefaultHeaders(res);
res.writeHead(statusCode);
res.end(JSON.stringify(body));
});