From 72d0f86a1df34067fc1bea0569fd64a4128ed7fb Mon Sep 17 00:00:00 2001 From: Simon Pinfold Date: Tue, 7 Jul 2026 08:59:46 +1200 Subject: [PATCH] fix(assets): refresh loader_path when re-ingesting an existing reference upsert_reference only wrote loader_path on the INSERT branch, so re-ingesting an existing reference (an output overwritten in place, or a file re-registered after its loader_path derivation changed) kept the stale or NULL value forever. Write it on the UPDATE branch too, with a null-safe change guard so a loader_path difference alone is enough to trigger the update, and identical values stay a no-op. --- .../database/queries/asset_reference.py | 5 +-- .../assets_test/queries/test_cache_state.py | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/app/assets/database/queries/asset_reference.py b/app/assets/database/queries/asset_reference.py index 26c75ad93..967b0e43a 100644 --- a/app/assets/database/queries/asset_reference.py +++ b/app/assets/database/queries/asset_reference.py @@ -688,13 +688,14 @@ def upsert_reference( AssetReference.asset_id != asset_id, AssetReference.mtime_ns.is_(None), AssetReference.mtime_ns != int(mtime_ns), + AssetReference.loader_path.is_distinct_from(loader_path), AssetReference.is_missing == True, # noqa: E712 AssetReference.deleted_at.isnot(None), ) ) .values( - asset_id=asset_id, mtime_ns=int(mtime_ns), is_missing=False, - deleted_at=None, updated_at=now, + asset_id=asset_id, mtime_ns=int(mtime_ns), loader_path=loader_path, + is_missing=False, deleted_at=None, updated_at=now, ) ) res2 = session.execute(upd) diff --git a/tests-unit/assets_test/queries/test_cache_state.py b/tests-unit/assets_test/queries/test_cache_state.py index ead60e570..49d0e8d4c 100644 --- a/tests-unit/assets_test/queries/test_cache_state.py +++ b/tests-unit/assets_test/queries/test_cache_state.py @@ -176,6 +176,39 @@ class TestUpsertReference: ref = session.query(AssetReference).filter_by(file_path=file_path).one() assert ref.mtime_ns == final_mtime + def test_upsert_refreshes_loader_path_on_existing_reference(self, session: Session): + """Re-ingesting an existing reference writes the loader_path computed + by that ingest, healing NULL or stale values even when nothing else + about the row changed.""" + asset = _make_asset(session, "hash1") + file_path = "/models/checkpoints/sub/model.safetensors" + + upsert_reference( + session, asset_id=asset.id, file_path=file_path, name="model", + mtime_ns=100, loader_path=None, + ) + session.commit() + + created, updated = upsert_reference( + session, asset_id=asset.id, file_path=file_path, name="model", + mtime_ns=100, loader_path="sub/model.safetensors", + ) + session.commit() + + assert created is False + assert updated is True + ref = session.query(AssetReference).filter_by(file_path=file_path).one() + assert ref.loader_path == "sub/model.safetensors" + + # Identical loader_path is a no-op, not a spurious update. + created, updated = upsert_reference( + session, asset_id=asset.id, file_path=file_path, name="model", + mtime_ns=100, loader_path="sub/model.safetensors", + ) + session.commit() + assert created is False + assert updated is False + def test_upsert_restores_missing_reference(self, session: Session): """Upserting a reference that was marked missing should restore it.""" asset = _make_asset(session, "hash1")