From 051934c7a72259bfd001a33248758b39cd3fb807 Mon Sep 17 00:00:00 2001 From: Ryder Casazza Date: Tue, 21 Jul 2026 14:24:11 -0700 Subject: [PATCH] perf: serve mapping lookups from the in-memory cache Every resolve went through load_mappings, which stats the mapping file and reads the etag file before consulting the cache -- two disk reads per search request. The refresh task is the sole writer and updates the cache itself, so lookups now clone the cached index directly and only fall back to disk when the cache was never hydrated. External edits to data/mappings.json are now picked up on refresh/restart rather than immediately. --- src/mapping.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/mapping.rs b/src/mapping.rs index 91d606c..8bc2705 100644 --- a/src/mapping.rs +++ b/src/mapping.rs @@ -276,6 +276,18 @@ impl PlexAniBridgeMappings { Ok(()) } + /// Returns the current mapping index. Falls back to loading from + /// disk only if the cache was never hydrated. + async fn index(&self) -> Result> { + { + let guard = self.cache.read().await; + if let Some(cache) = guard.as_ref() { + return Ok(cache.entries.clone()); + } + } + self.load_mappings().await + } + async fn load_mappings(&self) -> Result> { let metadata = match fs::metadata(&self.path).await { Ok(metadata) => metadata, @@ -438,7 +450,7 @@ impl PlexAniBridgeMappings { } pub async fn resolve_anilist_id(&self, tvdb_id: i64, season: u32) -> Result> { - let mappings = self.load_mappings().await?; + let mappings = self.index().await?; let season_key = format!("s{season}"); if let Some(entries) = mappings.tvdb_to_entries.get(&tvdb_id) { @@ -473,7 +485,7 @@ impl PlexAniBridgeMappings { } pub async fn resolve_anilist_id_for_tmdb(&self, tmdb_id: i64) -> Result> { - let mappings = self.load_mappings().await?; + let mappings = self.index().await?; if let Some(anilist_id) = mappings.tmdb_to_anilist.get(&tmdb_id) { trace!(tmdb_id, anilist_id, "resolved tmdb mapping"); Ok(Some(*anilist_id)) @@ -484,12 +496,12 @@ impl PlexAniBridgeMappings { } pub async fn resolve_tmdb_id(&self, anilist_id: i64) -> Result> { - let mappings = self.load_mappings().await?; + let mappings = self.index().await?; Ok(mappings.anilist_to_tmdb.get(&anilist_id).copied()) } pub async fn resolve_tvdb_mappings(&self, anilist_id: i64) -> Result> { - let mappings = self.load_mappings().await?; + let mappings = self.index().await?; let result = mappings .anilist_to_entries