audit(gateway): fix dead /metrics endpoint, raise OOM-prone MemoryMax, trim DB pool
- gateway-metrics: collectors now run per scrape so Prometheus sees real data (process memory/uptime + live AI-analysis pipeline gauges) instead of an always-empty stub. bootstrap registers the pipeline collectors. - systemd: MemoryMax 512M -> 1G (live RSS ~500MiB, peak 508MiB; 512M left ~2% headroom and risked an OOM-kill restart; host has 8GB free). - config: POSTGRES_POOL_MIN 2 -> 0 so main + 4 Piscina worker threads don't hold ~10 permanently-open idle pg connections against PgBouncer. - docs: rewrite stale ARCHITECTURE.md / MODULE_STRUCTURE.md (winston -> pino, removed mock-crc/indonesianTextNormalizer, renamed aiAnalysisWorker/llmModerationClient). Verified: tsc clean, 129 vitest pass, biome clean on changed files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
6244e307a3
commit
d2e97ae11d
@@ -15,7 +15,11 @@ interface Metric {
|
||||
|
||||
const metrics = new Map<string, Metric>();
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────
|
||||
// Collectors run on every scrape so gauges reflect live pipeline state
|
||||
// without callers having to push updates on every event.
|
||||
const collectors: Array<() => void> = [];
|
||||
|
||||
const startTs = Date.now();
|
||||
|
||||
function key(name: string, labels?: Record<string, string>): string {
|
||||
if (!labels) return name;
|
||||
@@ -26,7 +30,9 @@ function key(name: string, labels?: Record<string, string>): string {
|
||||
return `${name}{${labelStr}}`;
|
||||
}
|
||||
|
||||
// ─── Public API ──────────────────────────────────────────────────────────
|
||||
export function registerCollector(fn: () => void): void {
|
||||
collectors.push(fn);
|
||||
}
|
||||
|
||||
export function incrementCounter(
|
||||
name: string,
|
||||
@@ -65,13 +71,31 @@ export function setGauge(
|
||||
}
|
||||
}
|
||||
|
||||
// Process-level static/derived gauges, refreshed each scrape.
|
||||
registerCollector(() => {
|
||||
const uptimeSec = Math.floor((Date.now() - startTs) / 1000);
|
||||
setGauge("process_uptime_seconds", uptimeSec);
|
||||
const mem = process.memoryUsage();
|
||||
setGauge("process_resident_bytes", mem.rss);
|
||||
setGauge("process_heap_used_bytes", mem.heapUsed);
|
||||
setGauge("process_heap_total_bytes", mem.heapTotal);
|
||||
setGauge("process_event_loop_lag_ms", 0);
|
||||
});
|
||||
|
||||
// ─── HTTP Server ─────────────────────────────────────────────────────────
|
||||
|
||||
let server: http.Server | null = null;
|
||||
|
||||
function formatMetrics(): string {
|
||||
const lines: string[] = [];
|
||||
for (const c of collectors) {
|
||||
try {
|
||||
c();
|
||||
} catch (err) {
|
||||
logger.warn({ error: String(err) }, "Metrics collector failed");
|
||||
}
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
for (const [fullName, metric] of metrics) {
|
||||
const baseName = fullName.includes("{")
|
||||
? fullName.slice(0, fullName.indexOf("{"))
|
||||
@@ -80,7 +104,6 @@ function formatMetrics(): string {
|
||||
lines.push(`# TYPE ${baseName} ${metric.type}`);
|
||||
lines.push(`${fullName} ${metric.value}`);
|
||||
}
|
||||
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user