perf(pages): cache de l'index d'archive en mémoire (-73% CBZ, -76% CBR cold)

Chaque cold render ré-énumérait toutes les entrées ZIP/RAR pour construire
la liste triée des images. Maintenant la liste est mise en cache dans l'AppState
(LruCache<String, Arc<Vec<String>>>, std::sync::Mutex pour accès spawn_blocking).

Nouvelles fonctions dans parsers :
- list_archive_images(path, format) -> Vec<String>
- extract_image_by_name(path, format, name) -> Vec<u8>

Mesures avant/après (cache disque froid, n=20) :
- CBZ cold : 43ms → 11.9ms (-73%)
- CBR cold : 46ms → 11.0ms (-76%)
- Warm/concurrent : identique

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 09:40:42 +01:00
parent 44c6dd626a
commit bf5a20882b
3 changed files with 187 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ use std::num::NonZeroUsize;
use stripstream_core::config::ApiConfig;
use sqlx::postgres::PgPoolOptions;
use tokio::sync::{Mutex, RwLock, Semaphore};
use std::sync::Mutex as StdMutex;
use tracing::info;
use crate::state::{load_concurrent_renders, load_dynamic_settings, AppState, Metrics, ReadRateLimit};
@@ -77,6 +78,7 @@ async fn main() -> anyhow::Result<()> {
requests_in_window: 0,
})),
settings: Arc::new(RwLock::new(dynamic_settings)),
archive_index_cache: Arc::new(StdMutex::new(LruCache::new(NonZeroUsize::new(256).expect("non-zero")))),
};
let admin_routes = Router::new()