fix(fallback): connectTimeoutMs 2500→15000 e breakerTripAfter 2→3

Il default di 2,5 s per connect+headers è troppo stretto su percorsi VPN/AP:
misurati GET /v1/status 1,23 s e POST /v1/memories:search 1,98 s, talvolta
oltre 2,5 s. In quelle condizioni il breaker si apriva pur con gateway
raggiungibile e tutte le chiamate successive andavano in fast-fail per 2–10
minuti, spingendo di fatto ogni operazione sul solo fallback locale e
lasciando l'outbox non sincronizzata.

- extensions/shared.ts: connectTimeoutMs 2500 → 15_000 (default, fallback nel
  path di richiesta e commento), breakerTripAfter 2 → 3
- README.md: default aggiornati + motivazione nella tabella dei timeout
- skills/qmem/SKILL.md: default aggiornato e sintassi CLI del reset breaker
  (`qmem-sqlite.mjs breaker --reset`)

Verifica sul campo: con connectTimeoutMs=60000 l'outbox (18 record) è stata
sincronizzata completamente e il breaker è rimasto chiuso.
This commit is contained in:
Matteo Benedetto
2026-09-16 10:26:52 +02:00
parent 76eedcd526
commit 12e63d09fa
3 changed files with 12 additions and 12 deletions
+5 -5
View File
@@ -31,7 +31,7 @@ export interface MemoryConfig {
apiKey: string;
/** Budget (ms) per la risposta DOPO gli header: distingue l'elaborazione lunga (default 30000). */
timeoutMs?: number;
/** Timeout (ms) per connect+headers: oltre questo il gateway è "non raggiungibile" (default 2500). */
/** Timeout (ms) per connect+headers: oltre questo il gateway è "non raggiungibile" (default 15000). */
connectTimeoutMs?: number;
/** Attesa base del circuit breaker dopo un fallimento definitivo (default 120000 = 2 min). */
breakerBaseMs?: number;
@@ -52,10 +52,10 @@ const CONFIG_DEFAULTS: MemoryConfig = {
url: "https://qmem.enne2.net",
apiKey: "",
timeoutMs: 30_000,
connectTimeoutMs: 2500,
connectTimeoutMs: 15_000,
breakerBaseMs: 120_000,
breakerMaxMs: 600_000,
breakerTripAfter: 2,
breakerTripAfter: 3,
correctMinScore: 0.6,
localFallback: true,
offlineQueue: true,
@@ -147,7 +147,7 @@ export function breakerIsOpen(): boolean {
/** Registra un fallimento; con `definitive` (connessione) apre immediatamente. */
export function tripBreaker(reason: string, definitive: boolean, cfg?: MemoryConfig): BreakerInfo {
const s = loadBreaker();
const tripAfter = Math.max(1, cfg?.breakerTripAfter ?? CONFIG_DEFAULTS.breakerTripAfter ?? 2);
const tripAfter = Math.max(1, cfg?.breakerTripAfter ?? CONFIG_DEFAULTS.breakerTripAfter ?? 3);
s.failures = definitive ? Math.max((s.failures ?? 0) + 1, tripAfter) : (s.failures ?? 0) + 1;
s.lastError = reason;
s.lastChange = new Date().toISOString();
@@ -249,7 +249,7 @@ export async function gatewayRequest(
};
if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
const connectMs = Math.max(200, opts?.connectTimeoutMs ?? cfg.connectTimeoutMs ?? 2500);
const connectMs = Math.max(200, opts?.connectTimeoutMs ?? cfg.connectTimeoutMs ?? 15_000);
const bodyMs = Math.max(500, opts?.timeoutMs ?? cfg.timeoutMs ?? 30_000);
const url = `${cfg.url}${route}`;
let lastError: unknown = null;