fix(recipes): make source_path backfill a one-shot migration

This commit is contained in:
Will Miao
2026-08-25 17:38:17 +08:00
parent cdb044cb45
commit c51090ab16
4 changed files with 183 additions and 2 deletions
+38
View File
@@ -333,6 +333,44 @@ class PersistentRecipeCache:
except Exception as exc:
logger.debug("Failed to persist image_id_map: %s", exc)
def get_metadata_value(self, key: str) -> Optional[str]:
"""Return a value from cache_metadata, or None if missing."""
if not self.is_enabled() or not self._schema_initialized:
return None
try:
with self._db_lock:
conn = self._connect(readonly=True)
try:
row = conn.execute(
"SELECT value FROM cache_metadata WHERE key = ?",
(key,),
).fetchone()
return row["value"] if row else None
finally:
conn.close()
except Exception:
return None
def set_metadata_value(self, key: str, value: str) -> None:
"""Store a value in cache_metadata without rewriting the full cache."""
if not self.is_enabled() or not self._schema_initialized:
return
try:
with self._db_lock:
conn = self._connect()
try:
conn.execute(
"INSERT OR REPLACE INTO cache_metadata (key, value) VALUES (?, ?)",
(key, value),
)
conn.commit()
finally:
conn.close()
except Exception as exc:
logger.debug("Failed to persist cache metadata %s: %s", key, exc)
def get_indexed_recipe_ids(self) -> Set[str]:
"""Return all recipe IDs in the cache.
+20 -2
View File
@@ -1547,7 +1547,7 @@ class RecipeScanner:
self._cache.raw_data = recipes
self._update_folder_metadata(self._cache)
self._sort_cache_sync()
# Backfill source_path from JSON files if missing (schema migration)
# Backfill source_path from JSON files if missing (one-shot schema migration)
if self._backfill_source_path_if_needed(recipes, json_paths):
self._cache.image_id_map = self._build_image_id_map()
self._persistent_cache.save_cache(
@@ -1574,7 +1574,7 @@ class RecipeScanner:
self._cache.raw_data = recipes
self._update_folder_metadata(self._cache)
self._sort_cache_sync()
# Backfill source_path from JSON files if missing (schema migration)
# Backfill source_path from JSON files if missing (one-shot schema migration)
self._backfill_source_path_if_needed(recipes, json_paths)
self._cache.image_id_map = self._build_image_id_map()
# Persist updated cache
@@ -1711,6 +1711,9 @@ class RecipeScanner:
return recipes, changed, json_paths
# Metadata key recording that the one-shot source_path backfill has run.
_SOURCE_PATH_BACKFILL_MARKER = "source_path_backfilled"
def _backfill_source_path_if_needed(
self,
recipes: List[Dict[str, Any]],
@@ -1718,8 +1721,21 @@ class RecipeScanner:
) -> bool:
"""Backfill source_path from recipe JSON files if missing from cache.
This is a one-shot schema migration: once it has run, a completion
marker is stored in the persistent cache metadata and later startups
skip it entirely. Recipes without a source_path in their JSON file
would otherwise be re-read and re-parsed on every startup. New or
changed recipe files still get source_path from the normal parse path
during reconciliation.
Returns True if any recipes were updated (caller should persist cache).
"""
cache = self._persistent_cache
if (
cache is not None
and cache.get_metadata_value(self._SOURCE_PATH_BACKFILL_MARKER) == "1"
):
return False
updated = False
for recipe in recipes:
if recipe.get("source_path"):
@@ -1737,6 +1753,8 @@ class RecipeScanner:
updated = True
except Exception:
pass
if cache is not None:
cache.set_metadata_value(self._SOURCE_PATH_BACKFILL_MARKER, "1")
return updated
def _full_directory_scan_sync(