perf: skip previous-1950-2024 fetch si latest suffit (cold-fetch -90%)
Some checks are pending
Deploy info-canicule / deploy (push) Waiting to run

Latest fichier (~50 KB compressé) couvre l'année courante et la précédente.
En mai 2026 il contient janvier 2025 → présent (~17 mois > 365j requis).
Previous (~4 MB compressé) n'est nécessaire que si latest a < 365j —
ce qui ne se produit qu'en début d'année (jan-mar 2025 par exemple).

Cold-fetch dept :
- Avant : ~17s (latest + previous + parsing 70 ans de data inutiles)
- Après : ~2-3s (latest seul, parse de quelques mois)

+ mem_limit 256m → 512m pour donner du headroom au cold-fetch concurrent
  (constaté : 5 workers warmup parallèles ont OOM le container).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Florian 2026-05-26 02:40:37 +02:00
parent b342ea7375
commit ac46637377
2 changed files with 32 additions and 20 deletions

View file

@ -17,8 +17,8 @@ services:
- HOST=0.0.0.0 - HOST=0.0.0.0
networks: networks:
- shared-net - shared-net
mem_limit: 256m mem_limit: 512m
cpus: 0.5 cpus: 1.0
logging: logging:
driver: json-file driver: json-file
options: options:

View file

@ -83,23 +83,8 @@ async function fetchOne(url: string): Promise<string | null> {
return gunzipSync(buf).toString('utf-8'); return gunzipSync(buf).toString('utf-8');
} }
async function fetchClimato(dept: string): Promise<ClimatoSeries> { function aggregateDays(byDate: Map<string, Agg>): DayObservation[] {
const byDate = new Map<string, Agg>(); return [...byDate.entries()]
// 1) latest (rapide, ~50 KB compressé) — couvre l'année courante
const latestUrl = buildUrl(dept, LATEST);
if (latestUrl) {
const text = await fetchOne(latestUrl);
if (text) parseCsvInto(text, byDate);
}
// 2) previous (~4 MB compressé) — nécessaire pour combler les jours hors année courante
// On le fetch toujours, son taille est tolérable au cold-fetch (cache 24h).
const previousUrl = buildUrl(dept, PREVIOUS);
if (previousUrl) {
const text = await fetchOne(previousUrl);
if (text) parseCsvInto(text, byDate);
}
const days: DayObservation[] = [...byDate.entries()]
.sort(([a], [b]) => (a < b ? -1 : 1)) .sort(([a], [b]) => (a < b ? -1 : 1))
.map(([date, agg]) => ({ .map(([date, agg]) => ({
date, date,
@ -109,8 +94,35 @@ async function fetchClimato(dept: string): Promise<ClimatoSeries> {
rr: agg.rrN > 0 ? +(agg.rrSum / agg.rrN).toFixed(1) : null, rr: agg.rrN > 0 ? +(agg.rrSum / agg.rrN).toFixed(1) : null,
stations: agg.stations, stations: agg.stations,
})); }));
}
async function fetchClimato(dept: string): Promise<ClimatoSeries> {
const byDate = new Map<string, Agg>();
// 1) latest (~50 KB compressé) — couvre l'année courante.
// Le fichier latest-2025-2026 contient janvier 2025 → aujourd'hui.
// En 2026, ça couvre déjà > 365 jours, donc previous n'est pas nécessaire.
const latestUrl = buildUrl(dept, LATEST);
if (latestUrl) {
const text = await fetchOne(latestUrl);
if (text) parseCsvInto(text, byDate);
}
let days = aggregateDays(byDate);
// 2) previous (~4 MB compressé) — UNIQUEMENT si latest ne couvre pas MAX_DAYS.
// Cas où c'est nécessaire : début 2025 (latest n'a que ~quelques mois).
// En mai 2026, latest couvre déjà ~17 mois → previous skip → cold-fetch ~10× plus rapide.
if (days.length < MAX_DAYS) {
const previousUrl = buildUrl(dept, PREVIOUS);
if (previousUrl) {
const text = await fetchOne(previousUrl);
if (text) {
parseCsvInto(text, byDate);
days = aggregateDays(byDate);
}
}
}
// Garder MAX_DAYS derniers jours (365)
return { dept, fetchedAt: new Date().toISOString(), days: days.slice(-MAX_DAYS) }; return { dept, fetchedAt: new Date().toISOString(), days: days.slice(-MAX_DAYS) };
} }