mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 06:12:10 +03:00
refactor(chatCore): extract checkHeapPressureGuard leaf (god-file decomposition start) (#4371)
Primeiro incremento da decomposição do chatCore.ts (5127 LOC, hot-path mais quente). O guard de memória do topo do handleChatCore (rejeita 503 quando o heap V8 passa o threshold de shed) vira um leaf testável, co-locado com o threshold em heapPressure.ts. - heapPressure.ts: checkHeapPressureGuard(heapUsedMb, thresholdMb) — retorna o result 503 pronto ou null. Byte-idêntico ao guard inline (mesmo check, mesma 503, mesmo warn). A figura de heap fica em telemetria INTERNA, nunca no response do cliente (Hard Rule #12). - chatCore.ts: o bloco inline (~22 ln) vira 3 linhas; import órfão de HEAP_PRESSURE_THRESHOLD_MB trocado por checkHeapPressureGuard. - 3 testes novos (incl. assert Rule #12: o MB medido não vaza no payload). complexity-baseline 1895->1896: drift de base pós-#4338 (medido com minhas mudanças stashed = 1896); esta mudança é complexity-NEUTRA (helper complexity 2, handleChatCore só perde código). 190/190 chatcore tests, typecheck 0, file-size encolhe.
This commit is contained in:
committed by
GitHub
parent
c0291f8fbb
commit
542e0b5241
@@ -2,6 +2,7 @@
|
||||
"_comment": "Catraca de complexidade (check-complexity.mjs, ESLint core rules complexity>=15 e max-lines-per-function>80 sobre src+open-sse+electron+bin via eslint.complexity.config.mjs). Conta total de violacoes; so pode cair. --update ratcheta.",
|
||||
"count": 1896,
|
||||
"_rebaseline_2026_06_20_postlote_concurrent_drift": "Reconciliacao release-volatil: 1895->1896 (+1). Drift de condicional NOVO de PRs mergeados pela sessao concorrente APOS o #4338 ratchetar para 1895 (#4355 pricing gpt-5.x-pro / #4364 cli active-context cred / #4363 compliance cleanup / #4358 mitm mask / #4332 injection-guard-16KB). O fast-path do release nao roda check:complexity (so release->main), entao o ramo acumula sem rebaselinar. Medido com `node scripts/check/check-complexity.mjs` no tip cdfd71c17. Mesma familia — crescimento de feature legitimo, nao regressao.",
|
||||
"_note_2026_06_20_4371_chatcore_heap_leaf": "PR #4371 (extract checkHeapPressureGuard leaf, god-file decomposition start) e complexity-NEUTRO: handleChatCore so PERDE codigo e o novo heapPressure.ts checkHeapPressureGuard fica sob o teto — a contagem permanece 1896 (medido no tip mesclado com check-complexity.mjs).",
|
||||
"_ratchet_2026_06_19_phasecombosetup_fix": "1896->1895 (-1, ratchet DOWN — melhoria, NAO reconciliacao). O #4336 reconciliou o drift do lote para 1896 INCLUINDO a violacao que o #4326 (ComboContext) introduziu: phaseComboSetup media complexity 17 (>15) porque a extracao concentrou os condicionais de pinning/ternarios numa funcao que estourava o teto (irônico p/ uma decomposicao). Este PR CORRIGE na origem — extrai resolveContextCachePin (helper do pinning), phaseComboSetup volta a <15 — baixando a contagem 1. Medido com `node scripts/check/check-complexity.mjs` no tip pos-#4336.",
|
||||
"_rebaseline_2026_06_19_lote3_postdeploy_drift": "Reconciliacao release-volatil pos-merge do lote adicional (6 PRs apos o deploy): 1890->1896 (+6). Drift de condicionais NOVOS de #4327 (per-key USD usage quotas — apiKeyUsageLimits.ts + validation/policy branches), #4334 (cache-aware compression guard) e #4326 (phaseComboSetup extraido). Medido no tip real da release com `node scripts/check/check-complexity.mjs`. Mesma familia/justificativa do _rebaseline_2026_06_19_lote3_merge_drift abaixo — feature legitima, nao regressao.",
|
||||
"_rebaseline_2026_06_19_lote3_merge_drift": "Reconciliacao release-volatil pos-merge do lote de 13 PRs (release/v3.8.30): 1888->1890 (+2). Drift de condicionais NOVOS trazidos por #4313 (5 harvested features — combo allowlist intersection, serviceKind filter) e #4323 (compression e2e — novos ramos em ultra/aggressive/gcf/strategySelector), merges que entraram DEPOIS do #4318 medir 1888. O fast-path do release nao roda check:complexity (so release->main), entao os ramos acumulam sem rebaselinar. Medido no tip real da release pos-merge com `node scripts/check/check-complexity.mjs`. Mesma familia dos rebaselines anteriores — crescimento de feature legitimo, nao regressao; reducao fica como debt de refactor dedicado.",
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
resolveMemoryOwnerId,
|
||||
} from "./chatCore/memoryExtraction.ts";
|
||||
import { CORS_HEADERS } from "../utils/cors.ts";
|
||||
import { HEAP_PRESSURE_THRESHOLD_MB } from "../utils/heapPressure.ts";
|
||||
import { checkHeapPressureGuard } from "../utils/heapPressure.ts";
|
||||
import { normalizeHeaders } from "../utils/headers.ts";
|
||||
import { detectFormatFromEndpoint, getTargetFormat } from "../services/provider.ts";
|
||||
import { injectSystemPrompt } from "../services/systemPrompt.ts";
|
||||
@@ -643,27 +643,8 @@ export async function handleChatCore({
|
||||
// cascading OOM when many large-context requests arrive concurrently.
|
||||
try {
|
||||
const heapUsedMB = process.memoryUsage().heapUsed / (1024 * 1024);
|
||||
if (heapUsedMB > HEAP_PRESSURE_THRESHOLD_MB) {
|
||||
// Internal telemetry only — never expose the heap figure to clients (Hard Rule #12).
|
||||
console.warn(
|
||||
`[chatCore] heap pressure guard tripped: ${Math.round(heapUsedMB)}MB > ${HEAP_PRESSURE_THRESHOLD_MB}MB; returning 503`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
status: 503,
|
||||
error: "Service temporarily unavailable due to resource pressure. Retry shortly.",
|
||||
response: new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: "Service temporarily unavailable due to resource pressure. Retry shortly.",
|
||||
type: "server_error",
|
||||
code: "heap_pressure",
|
||||
},
|
||||
}),
|
||||
{ status: 503, headers: { "Content-Type": "application/json", "Retry-After": "5" } }
|
||||
),
|
||||
};
|
||||
}
|
||||
const heapGuard = checkHeapPressureGuard(heapUsedMB);
|
||||
if (heapGuard) return heapGuard;
|
||||
} catch {
|
||||
/* memoryUsage() never throws */
|
||||
}
|
||||
|
||||
@@ -42,3 +42,40 @@ export const HEAP_PRESSURE_THRESHOLD_MB = computeHeapPressureThresholdMb(
|
||||
v8.getHeapStatistics().heap_size_limit / (1024 * 1024),
|
||||
process.env.HEAP_PRESSURE_THRESHOLD_MB
|
||||
);
|
||||
|
||||
const HEAP_PRESSURE_MESSAGE =
|
||||
"Service temporarily unavailable due to resource pressure. Retry shortly.";
|
||||
|
||||
export type HeapPressureGuardResult = {
|
||||
success: false;
|
||||
status: 503;
|
||||
error: string;
|
||||
response: Response;
|
||||
};
|
||||
|
||||
/**
|
||||
* Memory-pressure shed guard for the chat pipeline (extracted from chatCore's handleChatCore).
|
||||
* Returns a ready-to-return 503 result when live heap usage exceeds the shed threshold, else null
|
||||
* to proceed. The heap figure is logged for INTERNAL telemetry only and is NEVER placed in the
|
||||
* client-facing response (Hard Rule #12). Behaviour is byte-identical to the previous inline guard.
|
||||
*/
|
||||
export function checkHeapPressureGuard(
|
||||
heapUsedMb: number,
|
||||
thresholdMb: number = HEAP_PRESSURE_THRESHOLD_MB
|
||||
): HeapPressureGuardResult | null {
|
||||
if (heapUsedMb <= thresholdMb) return null;
|
||||
console.warn(
|
||||
`[chatCore] heap pressure guard tripped: ${Math.round(heapUsedMb)}MB > ${thresholdMb}MB; returning 503`
|
||||
);
|
||||
return {
|
||||
success: false,
|
||||
status: 503,
|
||||
error: HEAP_PRESSURE_MESSAGE,
|
||||
response: new Response(
|
||||
JSON.stringify({
|
||||
error: { message: HEAP_PRESSURE_MESSAGE, type: "server_error", code: "heap_pressure" },
|
||||
}),
|
||||
{ status: 503, headers: { "Content-Type": "application/json", "Retry-After": "5" } }
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { computeHeapPressureThresholdMb } from "../../open-sse/utils/heapPressure.ts";
|
||||
import {
|
||||
checkHeapPressureGuard,
|
||||
computeHeapPressureThresholdMb,
|
||||
} from "../../open-sse/utils/heapPressure.ts";
|
||||
|
||||
// Regression guard for the v3.8.8 "Service temporarily unavailable due to resource
|
||||
// pressure" outage: a fixed 200MB threshold sat below the app's ~260MB working set,
|
||||
@@ -46,3 +49,33 @@ describe("computeHeapPressureThresholdMb", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Extracted from chatCore's handleChatCore (god-file decomposition, first leaf). The SET of
|
||||
// behaviours below is byte-identical to the previous inline guard.
|
||||
describe("checkHeapPressureGuard", () => {
|
||||
it("returns null (proceed) when heap usage is at or below the threshold", () => {
|
||||
assert.equal(checkHeapPressureGuard(100, 256), null);
|
||||
assert.equal(checkHeapPressureGuard(256, 256), null, "boundary: == threshold proceeds");
|
||||
});
|
||||
|
||||
it("returns a 503 shed result when heap usage exceeds the threshold", async () => {
|
||||
const guard = checkHeapPressureGuard(300, 256);
|
||||
assert.ok(guard, "must shed above threshold");
|
||||
assert.equal(guard.success, false);
|
||||
assert.equal(guard.status, 503);
|
||||
assert.equal(guard.response.status, 503);
|
||||
assert.equal(guard.response.headers.get("Retry-After"), "5");
|
||||
assert.equal(guard.response.headers.get("Content-Type"), "application/json");
|
||||
const payload = await guard.response.json();
|
||||
assert.equal(payload.error.code, "heap_pressure");
|
||||
assert.equal(payload.error.type, "server_error");
|
||||
});
|
||||
|
||||
it("never leaks the heap figure to the client response (Hard Rule #12)", async () => {
|
||||
const guard = checkHeapPressureGuard(987, 256);
|
||||
assert.ok(guard);
|
||||
const text = JSON.stringify(await guard.response.clone().json()) + (guard.error ?? "");
|
||||
assert.ok(!text.includes("987"), "the measured heap MB must not appear in the client payload");
|
||||
assert.ok(!/\bMB\b/.test(text), "no heap-size detail should reach the client");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user