From b994383bf49e4b4a03270ff921f623c745b15f1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Mon, 27 Jul 2026 02:06:06 +0400 Subject: [PATCH] perf: replace sleep(5) with 429 backoff and add cache warm-up script --- DEPLOY.adoc | 15 ++++++++++++++ README.adoc | 14 +++++++++---- includes/config.php | 46 ++++++++++++++++++++++++++---------------- scripts/warm-cache.php | 46 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 scripts/warm-cache.php diff --git a/DEPLOY.adoc b/DEPLOY.adoc index 49acf87..81c1a55 100644 --- a/DEPLOY.adoc +++ b/DEPLOY.adoc @@ -319,6 +319,21 @@ grep STATIC_CACHE_NAME sw.js # le suffixe de version a été bumpé à l'heure d Le bump de version dans `sw.js` est fait *sur le serveur uniquement* : le dépôt garde sa valeur de référence, le working tree du serveur est nettoyé (`git checkout -- sw.js`) avant chaque pull pour garantir le fast-forward. +== 🔥 Préchauffage du cache + +Pour éviter que le premier visiteur ne paye le coût des appels API externes (PeerTube, Castopod) sur cache froid, préchauffez le cache par cron : + +[source,bash] +---- +# Toutes les 5 minutes, avec l'utilisateur deploy +sudo -u deploy crontab -e + +# Ajouter : +*/5 * * * * php /var/www/annu-kute-ced/scripts/warm-cache.php >/dev/null 2>&1 +---- + +Le script `scripts/warm-cache.php` appelle les fonctions de récupération des vidéos, catégories, podcasts et live, ce qui remplit `cache/api/` avant l'arrivée des visiteurs. + == 🧰 Dépannage [cols="1,3",options="header"] diff --git a/README.adoc b/README.adoc index 91bde97..667437d 100644 --- a/README.adoc +++ b/README.adoc @@ -197,7 +197,8 @@ Toutes ces sources sont modifiables dans `includes/config.local.php` (voir < $podcastSlug) { try { - // Ajouter un délai entre les requêtes pour éviter le rate limiting (sauf pour la première) - // Note: Castopod a un rate limit strict, le cache est fortement recommandé - if ($index > 0) { - sleep(5); // 5 secondes de délai minimum pour Castopod - } - // Construire l'URL du feed RSS pour ce podcast $feedUrl = rtrim($castopodUrl, '/') . '/@' . $podcastSlug . '/feed'; - // Récupérer le feed RSS - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $feedUrl); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_MAXREDIRS, 3); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - $xmlContent = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); + // Récupérer le feed RSS avec backoff uniquement sur rate limit (HTTP 429) + $maxAttempts = 3; + $attempt = 0; + $xmlContent = false; + $httpCode = 0; + + while ($attempt < $maxAttempts) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $feedUrl); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 3); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + $xmlContent = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + // Succès ou erreur définitive : on sort de la boucle + if ($httpCode !== 429) { + break; + } + + // Rate limit : attendre avant de réessayer (backoff exponentiel simple) + $attempt++; + if ($attempt < $maxAttempts) { + sleep(5 * $attempt); + } + } if ($httpCode !== 200 || !$xmlContent) { continue; // Passer au podcast suivant en cas d'erreur diff --git a/scripts/warm-cache.php b/scripts/warm-cache.php new file mode 100644 index 0000000..a169204 --- /dev/null +++ b/scripts/warm-cache.php @@ -0,0 +1,46 @@ +/dev/null 2>&1 + * (à programmer toutes les 5 minutes dans la crontab) + */ + +require_once __DIR__ . '/../includes/config.php'; + +$start = microtime(true); +$results = []; + +// Vidéos PeerTube +$results['recent_videos'] = count(getRecentVideos()); +$results['trending_videos'] = count(getTrendingVideos()); +$results['shorts'] = count(getShorts()); + +// Catégories affichées +$categories = getDisplayCategories(); +$results['display_categories'] = count($categories); +foreach ($categories as $category) { + $results['category_' . $category['id']] = count(getVideosByCategory($category['id'])); +} + +// Live +$live = getLiveStream(); +$results['live_stream'] = $live ? 1 : 0; + +// Podcasts Castopod +$results['castopod_episodes'] = count(getCastopodEpisodes()); + +// Funkwhale (si activé) +if (defined('FUNKWHALE_ENABLED') && FUNKWHALE_ENABLED) { + $results['funkwhale_tracks'] = count(getFunkwhaleTracks()); +} + +$duration = round(microtime(true) - $start, 2); +echo "Cache préchauffé en {$duration}s\n"; +foreach ($results as $key => $value) { + echo " - {$key}: {$value}\n"; +}