From 8a89dbbac196d4f9f36733cdc2bcbbfcd0a67aba Mon Sep 17 00:00:00 2001 From: Florian Date: Tue, 26 May 2026 18:58:10 +0200 Subject: [PATCH] chore(scripts): diagnostic load-balancer MF (DPVigilance update_time) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Script ad-hoc pour mesurer la désynchro WSO2 du gateway MF. Tabule les update_time distincts sur N fetches séquentiels et conclut si la mitigation MF_PARALLEL_FETCHES=3 reste justifiée. Constaté 2026-05-26 : ~60/40 split entre 2 update_time → à revérifier périodiquement. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/check-mf-loadbalancer.mjs | 57 +++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 scripts/check-mf-loadbalancer.mjs diff --git a/scripts/check-mf-loadbalancer.mjs b/scripts/check-mf-loadbalancer.mjs new file mode 100644 index 0000000..ddd9f04 --- /dev/null +++ b/scripts/check-mf-loadbalancer.mjs @@ -0,0 +1,57 @@ +#!/usr/bin/env node +// Diagnostic ad-hoc : le gateway Météo France (WSO2) load-balance entre plusieurs +// instances qui peuvent être désynchronisées sur /DPVigilance/v1/cartevigilance/encours. +// Constaté 2026-05-26 (cf. vigilance.ts:84-89). Ce script déclenche N fetches +// séquentiels et tabule les `update_time` distincts pour décider si le hack +// MF_PARALLEL_FETCHES=3 est toujours nécessaire. +// +// Usage : METEOFRANCE_API_KEY=xxx node scripts/check-mf-loadbalancer.mjs [N=20] + +const KEY = process.env.METEOFRANCE_API_KEY; +if (!KEY) { + console.error('METEOFRANCE_API_KEY required'); + process.exit(1); +} +const N = parseInt(process.argv[2] ?? '20', 10); +const URL = 'https://public-api.meteofrance.fr/public/DPVigilance/v1/cartevigilance/encours'; + +const counts = new Map(); +const failures = []; +const start = Date.now(); + +for (let i = 0; i < N; i++) { + try { + const res = await fetch(URL, { headers: { apikey: KEY, Accept: 'application/json' } }); + if (!res.ok) { + failures.push(`#${i}: HTTP ${res.status}`); + continue; + } + const j = await res.json(); + const t = j?.product?.update_time ?? ''; + counts.set(t, (counts.get(t) ?? 0) + 1); + process.stdout.write(`${i + 1}/${N} → ${t}\n`); + } catch (e) { + failures.push(`#${i}: ${e.message}`); + } +} + +const elapsed = ((Date.now() - start) / 1000).toFixed(1); +console.log(`\n=== Résumé (${N} fetches en ${elapsed}s) ===`); +const sorted = [...counts.entries()].sort((a, b) => (a[0] < b[0] ? 1 : -1)); +for (const [t, c] of sorted) { + const bar = '█'.repeat(Math.round((c / N) * 40)); + console.log(`${t} ${c.toString().padStart(3)}/${N} ${bar}`); +} +if (failures.length) { + console.log(`\nÉchecs: ${failures.length}`); + for (const f of failures) console.log(' ' + f); +} +console.log(`\nDistinct update_time: ${counts.size}`); +if (counts.size === 1) { + console.log('→ Pas de désynchro détectée sur cet échantillon. Tu peux probablement réduire MF_PARALLEL_FETCHES à 1.'); +} else { + const newest = sorted[0]?.[0]; + const newestCount = sorted[0]?.[1] ?? 0; + console.log(`→ Désynchro confirmée. Le bulletin le plus récent est ${newest} (${newestCount}/${N} = ${Math.round(newestCount/N*100)}%).`); + console.log(' Mitigation actuelle (MF_PARALLEL_FETCHES=3 + sort par update_time desc) reste justifiée.'); +}