perf: replace sleep(5) with 429 backoff and add cache warm-up script
This commit is contained in:
+15
@@ -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.
|
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
|
== 🧰 Dépannage
|
||||||
|
|
||||||
[cols="1,3",options="header"]
|
[cols="1,3",options="header"]
|
||||||
|
|||||||
+10
-4
@@ -197,7 +197,8 @@ Toutes ces sources sont modifiables dans `includes/config.local.php` (voir <<fr-
|
|||||||
│ ├── pleroma-adapter.js # Adaptateur Pleroma → API Mastodon
|
│ ├── pleroma-adapter.js # Adaptateur Pleroma → API Mastodon
|
||||||
│ └── pwa-update.js # Enregistrement SW + modal de mise à jour
|
│ └── pwa-update.js # Enregistrement SW + modal de mise à jour
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── check.sh # Vérifications qualité en local (avant push)
|
│ ├── check.sh # Vérifications qualité en local (avant push)
|
||||||
|
│ └── warm-cache.php # Préchauffage du cache API (cron)
|
||||||
├── uploads/ # Images d'annonces (non versionné, hors .gitkeep)
|
├── uploads/ # Images d'annonces (non versionné, hors .gitkeep)
|
||||||
├── index.php # Page d'accueil (agrégateur)
|
├── index.php # Page d'accueil (agrégateur)
|
||||||
├── video.php # Page de lecture d'une vidéo
|
├── video.php # Page de lecture d'une vidéo
|
||||||
@@ -415,7 +416,7 @@ Valeurs par défaut issues de `includes/config.default.php`.
|
|||||||
| Nombre total d'épisodes affichés
|
| Nombre total d'épisodes affichés
|
||||||
|===
|
|===
|
||||||
|
|
||||||
WARNING: Castopod applique un *rate limit* strict (HTTP 429) sur les flux RSS. Le code patiente 5 s entre chaque podcast. *Activez le cache* (`CACHE_ENABLED`) pour éviter ces délais à chaque page.
|
WARNING: Castopod applique un *rate limit* strict (HTTP 429) sur les flux RSS. Le code applique un backoff exponentiel (5 s, 10 s, 15 s) uniquement en cas de 429. *Activez le cache* (`CACHE_ENABLED`) et préchauffez-le avec `scripts/warm-cache.php` pour éviter ces délais à chaque page.
|
||||||
|
|
||||||
.Funkwhale (musique, optionnel)
|
.Funkwhale (musique, optionnel)
|
||||||
[cols="2,2,3",options="header"]
|
[cols="2,2,3",options="header"]
|
||||||
@@ -695,6 +696,8 @@ TIP: Si vous déployez une instance indépendante, remplacez `LIBERAPAY_URL` par
|
|||||||
|
|
||||||
NOTE: Le cache de l'API PeerTube et de WordPress (`includes/simple-cache.php`) est *toujours actif*, indépendamment de `CACHE_ENABLED`, avec des TTL par endpoint (catégories 1 h, vidéos/recherche 10 min, articles WP 15 min, comptes 5 min). Les fichiers sont stockés dans `cache/api/`.
|
NOTE: Le cache de l'API PeerTube et de WordPress (`includes/simple-cache.php`) est *toujours actif*, indépendamment de `CACHE_ENABLED`, avec des TTL par endpoint (catégories 1 h, vidéos/recherche 10 min, articles WP 15 min, comptes 5 min). Les fichiers sont stockés dans `cache/api/`.
|
||||||
|
|
||||||
|
TIP: Pour éviter que le premier visiteur ne paye le coût des appels API sur cache froid, préchauffez le cache via cron : `php /var/www/annu-kute-ced/scripts/warm-cache.php` (toutes les 5 minutes par exemple).
|
||||||
|
|
||||||
.Sécurité
|
.Sécurité
|
||||||
[cols="2,2,3",options="header"]
|
[cols="2,2,3",options="header"]
|
||||||
|===
|
|===
|
||||||
@@ -1167,7 +1170,8 @@ All of these sources can be changed in `includes/config.local.php` (see <<en-con
|
|||||||
│ ├── pleroma-adapter.js # Pleroma → Mastodon API adapter
|
│ ├── pleroma-adapter.js # Pleroma → Mastodon API adapter
|
||||||
│ └── pwa-update.js # SW registration + update modal
|
│ └── pwa-update.js # SW registration + update modal
|
||||||
├── scripts/
|
├── scripts/
|
||||||
│ └── check.sh # Local quality checks (before pushing)
|
│ ├── check.sh # Local quality checks (before pushing)
|
||||||
|
│ └── warm-cache.php # API cache warm-up (cron)
|
||||||
├── uploads/ # Announcement images (not versioned, except .gitkeep)
|
├── uploads/ # Announcement images (not versioned, except .gitkeep)
|
||||||
├── index.php # Homepage (aggregator)
|
├── index.php # Homepage (aggregator)
|
||||||
├── video.php # Video playback page
|
├── video.php # Video playback page
|
||||||
@@ -1385,7 +1389,7 @@ Default values from `includes/config.default.php`.
|
|||||||
| Total number of episodes displayed
|
| Total number of episodes displayed
|
||||||
|===
|
|===
|
||||||
|
|
||||||
WARNING: Castopod enforces a strict *rate limit* (HTTP 429) on RSS feeds. The code waits 5 s between podcasts. *Enable the cache* (`CACHE_ENABLED`) to avoid these delays on every page.
|
WARNING: Castopod enforces a strict *rate limit* (HTTP 429) on RSS feeds. The code applies exponential backoff (5 s, 10 s, 15 s) only on HTTP 429. *Enable the cache* (`CACHE_ENABLED`) and warm it with `scripts/warm-cache.php` to avoid these delays on every page.
|
||||||
|
|
||||||
.Funkwhale (music, optional)
|
.Funkwhale (music, optional)
|
||||||
[cols="2,2,3",options="header"]
|
[cols="2,2,3",options="header"]
|
||||||
@@ -1665,6 +1669,8 @@ TIP: If you deploy an independent instance, replace `LIBERAPAY_URL` with your ow
|
|||||||
|
|
||||||
NOTE: The PeerTube API and WordPress cache (`includes/simple-cache.php`) is *always on*, regardless of `CACHE_ENABLED`, with per-endpoint TTLs (categories 1 h, videos/search 10 min, WP posts 15 min, accounts 5 min). Files are stored in `cache/api/`.
|
NOTE: The PeerTube API and WordPress cache (`includes/simple-cache.php`) is *always on*, regardless of `CACHE_ENABLED`, with per-endpoint TTLs (categories 1 h, videos/search 10 min, WP posts 15 min, accounts 5 min). Files are stored in `cache/api/`.
|
||||||
|
|
||||||
|
TIP: To prevent the first visitor from paying the cost of API calls on a cold cache, warm the cache via cron: `php /var/www/annu-kute-ced/scripts/warm-cache.php` (every 5 minutes for example).
|
||||||
|
|
||||||
.Security
|
.Security
|
||||||
[cols="2,2,3",options="header"]
|
[cols="2,2,3",options="header"]
|
||||||
|===
|
|===
|
||||||
|
|||||||
+19
-7
@@ -690,16 +690,16 @@ function getCastopodEpisodes($castopodUrl = null, $podcastSlugs = null, $count =
|
|||||||
// Itérer sur chaque podcast
|
// Itérer sur chaque podcast
|
||||||
foreach ($podcastSlugs as $index => $podcastSlug) {
|
foreach ($podcastSlugs as $index => $podcastSlug) {
|
||||||
try {
|
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
|
// Construire l'URL du feed RSS pour ce podcast
|
||||||
$feedUrl = rtrim($castopodUrl, '/') . '/@' . $podcastSlug . '/feed';
|
$feedUrl = rtrim($castopodUrl, '/') . '/@' . $podcastSlug . '/feed';
|
||||||
|
|
||||||
// Récupérer le feed RSS
|
// 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();
|
$ch = curl_init();
|
||||||
curl_setopt($ch, CURLOPT_URL, $feedUrl);
|
curl_setopt($ch, CURLOPT_URL, $feedUrl);
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
@@ -711,6 +711,18 @@ function getCastopodEpisodes($castopodUrl = null, $podcastSlugs = null, $count =
|
|||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
curl_close($ch);
|
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) {
|
if ($httpCode !== 200 || !$xmlContent) {
|
||||||
continue; // Passer au podcast suivant en cas d'erreur
|
continue; // Passer au podcast suivant en cas d'erreur
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Préchauffe le cache API de l'application.
|
||||||
|
*
|
||||||
|
* À appeler en tâche cron pour éviter que le premier visiteur ne paye le coût
|
||||||
|
* des appels API externes (PeerTube, Castopod, Funkwhale) sur cache froid.
|
||||||
|
*
|
||||||
|
* Exemple cron (toutes les 5 minutes) :
|
||||||
|
* php /var/www/annu-kute-ced/scripts/warm-cache.php >/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";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user