mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-11 17:30:00 +02:00
Logos absents : l'hébergeur des picons du fournisseur répond 503 sur tout, y compris sa racine (serveur en maintenance). Le proxy relayait ce 503 au navigateur, qui redemandait chaque logo à chaque affichage de la grille — des centaines de requêtes sortantes mortes par seconde. - Un statut >= 400 sur une image rend désormais un pixel transparent avec `Cache-Control: max-age=600`, au lieu de propager l'erreur - Après trois échecs consécutifs, l'hôte est marqué hors service cinq minutes : plus aucun appel sortant, réponse servie localement - Le corps d'erreur amont est drainé, sinon la connexion restait ouverte jusqu'au timeout Guide TV très lent (4 à 8 s par chaîne dans les logs) : `player_api` demande un appel par chaîne, et le panneau met ~4 s à répondre — une grille de trente chaînes dépassait la minute. - Le dump `xmltv.php` du panneau passe en premier : une requête couvre toutes les chaînes, réutilisée trois heures - L'interrogation chaîne par chaîne reste le repli, puis la source XMLTV externe - Une source XMLTV en échec n'est plus retéléchargée à chaque consultation : quinze minutes de recul - Index borné à 48 h en avant : un dump national couvre sept jours pour un millier de chaînes, soit des centaines de Mo pour un guide qui n'affiche que le programme courant Lecture saccadée : le relais FFmpeg -> navigateur de `/turbo.ts` ne transmettait pas la pause du client à la source. Un lecteur plus lent que le flux laissait FFmpeg produire à pleine vitesse et le serveur empilait les paquets en mémoire, d'où la dérive derrière le direct. La contre-pression est maintenant propagée (`pipeWithBackpressure`). Le serveur tourne sur un seul isolate : le flot de requêtes mortes et les attentes EPG de plusieurs secondes partageaient la boucle d'événements avec les paquets vidéo. Tests ajoutés : cache d'échecs par hôte, contre-pression du relais, priorité des sources EPG, recul après échec XMLTV, horizon d'index. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62 lines
2.1 KiB
Dart
62 lines
2.1 KiB
Dart
/// Mémoire courte des hôtes d'images injoignables.
|
|
///
|
|
/// POURQUOI : les URL de picons pointent chez l'hébergeur du revendeur, pas
|
|
/// sur le panneau. Quand cette machine tombe (maintenance, DNS, quota), elle
|
|
/// répond 5xx en quelques millisecondes, le navigateur réessaie à chaque
|
|
/// rendu de la grille et le proxy relaie des centaines de requêtes mortes par
|
|
/// seconde. Le serveur tournant sur un unique isolate Dart, ce flot passe
|
|
/// devant les paquets vidéo dans la boucle d'événements : l'image saccade
|
|
/// pendant qu'on s'acharne sur un hôte qu'on sait hors service.
|
|
///
|
|
/// On coupe donc court : au bout de [threshold] échecs consécutifs, l'hôte
|
|
/// est considéré mort pendant [ttl] et les requêtes suivantes sont servies
|
|
/// localement, sans appel sortant.
|
|
class AssetFailureCache {
|
|
AssetFailureCache({
|
|
this.threshold = 3,
|
|
this.ttl = const Duration(minutes: 5),
|
|
DateTime Function()? clock,
|
|
}) : _clock = clock ?? DateTime.now;
|
|
|
|
/// Nombre d'échecs consécutifs avant de couper un hôte. Un 5xx passager ne
|
|
/// doit pas priver l'utilisateur de ses logos.
|
|
final int threshold;
|
|
|
|
/// Durée de la coupure. Assez courte pour qu'un hôte réparé revienne seul.
|
|
final Duration ttl;
|
|
|
|
final DateTime Function() _clock;
|
|
|
|
final Map<String, int> _failures = {};
|
|
final Map<String, DateTime> _downUntil = {};
|
|
|
|
/// L'hôte est-il réputé hors service en ce moment ?
|
|
bool isDown(String host) {
|
|
final key = host.toLowerCase();
|
|
final until = _downUntil[key];
|
|
if (until == null) return false;
|
|
if (_clock().isBefore(until)) return true;
|
|
|
|
// Coupure expirée : on repart d'une ardoise vierge pour laisser une
|
|
// vraie chance au prochain appel.
|
|
_downUntil.remove(key);
|
|
_failures.remove(key);
|
|
return false;
|
|
}
|
|
|
|
void recordFailure(String host) {
|
|
final key = host.toLowerCase();
|
|
final count = (_failures[key] ?? 0) + 1;
|
|
_failures[key] = count;
|
|
if (count >= threshold) {
|
|
_downUntil[key] = _clock().add(ttl);
|
|
}
|
|
}
|
|
|
|
void recordSuccess(String host) {
|
|
final key = host.toLowerCase();
|
|
_failures.remove(key);
|
|
_downUntil.remove(key);
|
|
}
|
|
}
|