fix(metadata-sync): persist not-found flags to SQLite cache on deleted-provider path

When a model is already classified as civitai_deleted=True via
.metadata.json but re-enters the failure block through the
civarchive/sqlite provider path (not the default provider),
needs_save was never set to True because civitai_api_not_found
and sqlite_attempted were both False. The flags were never
persisted to SQLite, causing the model to be re-fetched on
every restart.

Also demoted duplicate INFO/ERROR logging in fetch_and_update_model
to DEBUG (the use case already logs at WARNING), and added
exc_info=True to the fetch_all_civitai error handler.
This commit is contained in:
Will Miao
2026-06-17 08:22:24 +08:00
parent 0906c484e9
commit 8c4b9a1e70
3 changed files with 17 additions and 7 deletions

View File

@@ -1861,7 +1861,9 @@ class ModelCivitaiHandler:
return web.json_response(result) return web.json_response(result)
except Exception as exc: except Exception as exc:
self._logger.error( self._logger.error(
"Error in fetch_all_civitai for %ss: %s", self._service.model_type, exc "Error in fetch_all_civitai for %ss: %s",
self._service.model_type, exc,
exc_info=True,
) )
return web.Response(text=str(exc), status=500) return web.Response(text=str(exc), status=500)

View File

@@ -264,6 +264,14 @@ class MetadataSyncService:
model_data["last_checked_at"] = datetime.now().timestamp() model_data["last_checked_at"] = datetime.now().timestamp()
needs_save = True needs_save = True
# When the model was already classified as "not on CivitAI" via
# .metadata.json (civitai_deleted=True) but the SQLite cache is
# stale (because the pre-fix code never persisted these flags),
# ensure the flags are written to the scanner cache + SQLite.
if not needs_save and model_data.get("civitai_deleted") is True:
model_data["last_checked_at"] = datetime.now().timestamp()
needs_save = True
# Save metadata if any state was updated # Save metadata if any state was updated
if needs_save: if needs_save:
data_to_save = model_data.copy() data_to_save = model_data.copy()
@@ -272,6 +280,7 @@ class MetadataSyncService:
if "last_checked_at" not in data_to_save: if "last_checked_at" not in data_to_save:
data_to_save["last_checked_at"] = datetime.now().timestamp() data_to_save["last_checked_at"] = datetime.now().timestamp()
await self._metadata_manager.save_metadata(file_path, data_to_save) await self._metadata_manager.save_metadata(file_path, data_to_save)
await update_cache_func(file_path, file_path, data_to_save)
default_error = ( default_error = (
"CivitAI model is deleted and metadata archive DB is not enabled" "CivitAI model is deleted and metadata archive DB is not enabled"
@@ -291,11 +300,9 @@ class MetadataSyncService:
f"Error fetching metadata: {resolved_error} " f"Error fetching metadata: {resolved_error} "
f"(file={os.path.basename(file_path)}, sha256={sha256})" f"(file={os.path.basename(file_path)}, sha256={sha256})"
) )
is_model_not_found = "Model not found" in resolved_error # Use case layer (BulkMetadataRefreshUseCase) logs failed models at WARNING level,
if is_expected_offline_error(resolved_error) or is_model_not_found: # so this level is demoted to DEBUG to avoid duplicate user-visible logging.
logger.info(error_msg) logger.debug(error_msg)
else:
logger.error(error_msg)
return False, error_msg return False, error_msg
model_data["from_civitai"] = True model_data["from_civitai"] = True

View File

@@ -293,7 +293,8 @@ async def test_fetch_and_update_model_respects_deleted_without_archive():
assert "metadata archive DB is not enabled" in error assert "metadata archive DB is not enabled" in error
helpers.default_provider_factory.assert_not_awaited() helpers.default_provider_factory.assert_not_awaited()
helpers.metadata_manager.hydrate_model_data.assert_not_awaited() helpers.metadata_manager.hydrate_model_data.assert_not_awaited()
update_cache.assert_not_awaited() # Now update_cache_func IS called to persist the not-found flags to SQLite
update_cache.assert_awaited_once()
@pytest.mark.asyncio @pytest.mark.asyncio