mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-11 17:26:32 +02:00
fix(qlik): reprise globale quand le moteur abandonne l'extraction (code 15)
Deux exécutions du même fournisseur D005, même code, même jour : 04:17 → OpenDoc en 78 s, succès en 479 s 06:13 → OpenDoc en 233 s, « code 15 / Request aborted » après 333 s Le moteur Qlik est trois fois plus lent à 6 h qu'à 4 h et finit par abandonner la requête. Les reprises par requête de rpcWithRetry ne servent à rien ici : c'est toute l'extraction qui tombe, et la sync repartait à zéro sans réessayer. L'extraction est désormais relancée entièrement sur erreur transitoire (code 15 ou « Request aborted »), une fois par défaut, après une pause de 60 s — pas immédiatement : relancer aussitôt ne ferait qu'ajouter de la charge au serveur qui vient de renoncer. Réglable via QLIK_EXTRACTION_RETRIES et QLIK_EXTRACTION_RETRY_PAUSE_MS. Le reste du comportement est préservé : le refus de publier une fenêtre 12 mois incomplète, et la reprise sur point de contrôle hors fenêtre datée, s'appliquent comme avant une fois les reprises épuisées. Le checkpoint est vidé entre deux tentatives pour ne pas mélanger deux extractions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y26nRZxTR57K7h8yqsF675
This commit is contained in:
1 file changed
+40
-2
@@ -2296,11 +2296,45 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
},
|
||||
);
|
||||
|
||||
// Reprise globale sur erreur transitoire.
|
||||
//
|
||||
// Le moteur Qlik abandonne les requêtes longues quand il est chargé
|
||||
// (« code 15 / Request aborted »). Observé sur un même fournisseur, avec
|
||||
// le même code : réussite à 4 h du matin (OpenDoc en 78 s), échec à 6 h
|
||||
// (OpenDoc en 233 s, puis abandon). Les reprises par requête de
|
||||
// `rpcWithRetry` ne suffisent pas — c'est toute l'extraction qui tombe.
|
||||
//
|
||||
// La pause est volontairement longue : relancer aussitôt ne ferait
|
||||
// qu'ajouter de la charge au serveur qui vient déjà de renoncer.
|
||||
const extractionEssais = 1 + envNumber("QLIK_EXTRACTION_RETRIES", 1, 0, 3);
|
||||
const extractionPauseMs = envNumber("QLIK_EXTRACTION_RETRY_PAUSE_MS", 60_000, 0, 600_000);
|
||||
const estTransitoire = (m: string) => /"code"\s*:\s*15\b/.test(m) || /request aborted/i.test(m);
|
||||
const pause = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
|
||||
|
||||
let result: InPageResult;
|
||||
let essai = 0;
|
||||
for (;;) {
|
||||
essai++;
|
||||
const reste = essai < extractionEssais;
|
||||
try {
|
||||
result = (await evaluer()) as InPageResult;
|
||||
const candidat = (await evaluer()) as InPageResult;
|
||||
if (candidat.ok || !reste || !estTransitoire(String(candidat.error ?? ""))) {
|
||||
result = candidat;
|
||||
break;
|
||||
}
|
||||
console.warn(
|
||||
`[qlik-pw] tentative ${essai}/${extractionEssais} abandonnée par le moteur (${String(candidat.error).slice(0, 160)})`
|
||||
+ ` — nouvelle tentative dans ${Math.round(extractionPauseMs / 1000)} s`,
|
||||
);
|
||||
} catch (e) {
|
||||
const msg = String((e as Error)?.message || e);
|
||||
if (reste && estTransitoire(msg)) {
|
||||
console.warn(
|
||||
`[qlik-pw] tentative ${essai}/${extractionEssais} interrompue (${msg.slice(0, 160)})`
|
||||
+ ` — nouvelle tentative dans ${Math.round(extractionPauseMs / 1000)} s`,
|
||||
);
|
||||
} else {
|
||||
// Comportement d'origine, une fois les reprises épuisées.
|
||||
// Une fenêtre 12 mois n'est publiable que si l'extraction est
|
||||
// complète et a passé tous les contrôles. Conserver un checkpoint
|
||||
// partiel fabriquerait précisément des graphiques à 2 ou 3 mois.
|
||||
@@ -2311,10 +2345,14 @@ export async function fetchNetworkMetricsPlaywright(
|
||||
if (secours?.rows?.length) {
|
||||
console.error(`[qlik-pw] extraction interrompue (${msg.slice(0, 200)}) — reprise du dernier point de contrôle : ${secours.rows.length} lignes`);
|
||||
result = { ok: true, rows: secours.rows, monthly: secours.monthly, size: secours.rows.length, partiel: true };
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
checkpoint = null; // la nouvelle tentative repart d'une extraction vierge
|
||||
await pause(extractionPauseMs);
|
||||
}
|
||||
|
||||
if (!result.ok) {
|
||||
if (dateFilter) {
|
||||
|
||||
Reference in new issue
Block a user