mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-09-20 18:51:26 -03:00
fix(recipes): stop an all-missing scan from wiping the recipe cache
A scan that finds no recipe files at all is not a reliable deletion signal: an unmounted drive, a recipes_path that silently falls back to another LoRA root, or a cache shared with a second instance all look exactly like a real wipe. The reconcile step treated them all as deletions and overwrote the persistent cache with an empty one, so DELETE FROM recipes destroyed the user's only record of their recipes and the FTS index was rebuilt from the empty view (#1116). Guard the prune: - _reconcile_recipe_cache reports an all-missing result when every persisted recipe file is gone AND the stored rows match the recorded file stats. An internally inconsistent cache (leftover orphans) is stale, not evidence of a fresh disappearance, and still prunes. - The caller keeps the stored cache and logs a warning naming the directory it scanned and the number of recipes it preserved, instead of writing the empty result. It also skips the FTS rebuild so the index stays aligned with the stored rows. - save_cache gains skip_if_empty as a storage-level backstop: refuse to empty a populated cache. Intentional clears (manual rebuild) keep the default behaviour. - Log the resolved scan directory per run so a support reader can tell a real wipe apart from a scan that looked elsewhere. Partial orphans (ordinary manual deletions) keep pruning as before.
This commit is contained in:
@@ -170,20 +170,30 @@ class PersistentRecipeCache:
|
||||
recipes: List[Dict[str, Any]],
|
||||
json_paths: Optional[Dict[str, str]] = None,
|
||||
image_id_map: Optional[Dict[str, str]] = None,
|
||||
) -> None:
|
||||
skip_if_empty: bool = False,
|
||||
) -> bool:
|
||||
"""Save all recipes to SQLite cache.
|
||||
|
||||
Args:
|
||||
recipes: List of recipe dictionaries to persist.
|
||||
json_paths: Optional mapping of recipe_id -> json_path for file stats.
|
||||
image_id_map: Optional precomputed civitai image_id → recipe_id mapping.
|
||||
skip_if_empty: When True, refuse to replace a non-empty cache with an
|
||||
empty one. This is the storage-level backstop against a scan that
|
||||
silently loses every recipe (unavailable drive / mis-resolved
|
||||
recipes directory): overwriting both deletes the user's data and
|
||||
destroys their only record of it. Intentional full clears (manual
|
||||
rebuild) must pass ``skip_if_empty=False``.
|
||||
|
||||
Returns:
|
||||
``True`` when the write happened, ``False`` when it was skipped.
|
||||
"""
|
||||
if not self.is_enabled():
|
||||
return
|
||||
return False
|
||||
if not self._schema_initialized:
|
||||
self._initialize_schema()
|
||||
if not self._schema_initialized:
|
||||
return
|
||||
return False
|
||||
|
||||
try:
|
||||
with self._db_lock:
|
||||
@@ -192,6 +202,23 @@ class PersistentRecipeCache:
|
||||
conn.execute("PRAGMA foreign_keys = ON")
|
||||
conn.execute("BEGIN")
|
||||
|
||||
if skip_if_empty and not recipes:
|
||||
existing = conn.execute(
|
||||
"SELECT COUNT(*) FROM recipes"
|
||||
).fetchone()
|
||||
if existing and existing[0]:
|
||||
conn.rollback()
|
||||
logger.warning(
|
||||
"Refusing to persist an empty recipe cache: the "
|
||||
"stored cache still holds %d recipe(s). The scan "
|
||||
"found nothing, which usually means the recipes "
|
||||
"path was unavailable or resolved elsewhere; "
|
||||
"keeping the stored cache so the data stays "
|
||||
"recoverable.",
|
||||
existing[0],
|
||||
)
|
||||
return False
|
||||
|
||||
# Clear existing data
|
||||
conn.execute("DELETE FROM recipes")
|
||||
|
||||
@@ -225,10 +252,12 @@ class PersistentRecipeCache:
|
||||
|
||||
conn.commit()
|
||||
logger.debug("Persisted %d recipes to cache", len(recipe_rows))
|
||||
return True
|
||||
finally:
|
||||
conn.close()
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to persist recipe cache: %s", exc)
|
||||
return False
|
||||
|
||||
def get_file_stats(self) -> Dict[str, Tuple[float, int]]:
|
||||
"""Return stored file stats for all cached recipes.
|
||||
|
||||
Reference in New Issue
Block a user