mirror of
https://github.com/R0m1k3/xtremflow.git
synced 2026-10-12 01:36:20 +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>
78 lines
2.2 KiB
Dart
78 lines
2.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:test/test.dart';
|
|
import '../utils/stream_pipe.dart';
|
|
|
|
void main() {
|
|
group('pipeWithBackpressure', () {
|
|
test('relaie les données au client', () async {
|
|
final source = StreamController<List<int>>();
|
|
final piped = pipeWithBackpressure(source.stream, onStop: () {});
|
|
|
|
final received = <List<int>>[];
|
|
final done = piped.listen(received.add).asFuture<void>();
|
|
|
|
source.add([1, 2, 3]);
|
|
await source.close();
|
|
await done;
|
|
|
|
expect(received, [
|
|
[1, 2, 3]
|
|
]);
|
|
});
|
|
|
|
test('propage la pause du client vers la source', () async {
|
|
// Sans cette propagation, un client plus lent que le flux laissait
|
|
// FFmpeg produire à pleine vitesse et le serveur empilait les paquets
|
|
// en mémoire : latence qui dérive et lecture qui saccade.
|
|
var paused = false;
|
|
var resumed = false;
|
|
final source = StreamController<List<int>>(
|
|
onPause: () => paused = true,
|
|
onResume: () => resumed = true,
|
|
);
|
|
|
|
final subscription = pipeWithBackpressure(
|
|
source.stream,
|
|
onStop: () {},
|
|
).listen((_) {});
|
|
|
|
subscription.pause();
|
|
await Future<void>.delayed(Duration.zero);
|
|
expect(paused, isTrue);
|
|
|
|
subscription.resume();
|
|
await Future<void>.delayed(Duration.zero);
|
|
expect(resumed, isTrue);
|
|
|
|
await subscription.cancel();
|
|
await source.close();
|
|
});
|
|
|
|
test('arrête le producteur quand le client se déconnecte', () async {
|
|
var stopped = 0;
|
|
final source = StreamController<List<int>>();
|
|
final subscription =
|
|
pipeWithBackpressure(source.stream, onStop: () => stopped++)
|
|
.listen((_) {});
|
|
|
|
await subscription.cancel();
|
|
|
|
expect(stopped, 1);
|
|
await source.close();
|
|
});
|
|
|
|
test('arrête le producteur quand la source se termine', () async {
|
|
var stopped = 0;
|
|
final source = StreamController<List<int>>();
|
|
final piped = pipeWithBackpressure(source.stream, onStop: () => stopped++);
|
|
final done = piped.listen((_) {}).asFuture<void>();
|
|
|
|
await source.close();
|
|
await done;
|
|
|
|
expect(stopped, 1);
|
|
});
|
|
});
|
|
}
|