Summary
After modifying an object and saving it, re-fetching the same object via from_id(use_cache=True) (the default) returns the original cached document, not the post-save state. As a result, the next save() can be a silent no-op even when a modification was made locally — most visibly when trying to unlink subjects/specimens by setting a property to None. [Bug reported by Lyuba Zehl]
Minimal example
from fairgraph import KGClient
from fairgraph.openminds.core import DatasetVersion, Subject
client = KGClient(host="core.kg.ebrains.eu")
dsv_uuid = "..." # an existing DatasetVersion you own
sub_uri = "..." # any existing Subject URI
# 1. Load, link a subject, save.
dsv = DatasetVersion.from_id(dsv_uuid, client, scope="any")
dsv.studied_specimens = [Subject(id=sub_uri)]
dsv.save(client, recursive=False)
# KG now has studiedSpecimen pointing at sub_uri. ✓
# 2. Re-fetch via from_id (same client, default use_cache=True).
dsv = DatasetVersion.from_id(dsv_uuid, client, scope="any")
# 3. Try to unlink the subject and save.
dsv.studied_specimens = None
dsv.save(client, recursive=False)
# Expected: studiedSpecimen cleared in KG.
# Actual: subject is still linked. No PATCH was sent.
Running the same studied_specimens = None; save() on the original dsv from step 1 (without the intervening re-fetch) does correctly unlink — that path works.
Cause
KGClient.cache[uri] is populated on first fetch in instance_from_full_uri (client.py) and never invalidated or refreshed by writes. When save() calls update_instance / replace_instance / create_new_instance, the server is updated but client.cache[uri] retains the original document.
On the second from_id call:
instance_from_full_uri returns the stale cached document.
- The freshly-constructed
DatasetVersion therefore has studied_specimens = None and remote_data with no studiedSpecimen key.
- Setting
studied_specimens = None is a no-op (already None).
modified_data() compares current=None against remote=None → empty diff.
save() enters the "unchanged, not updating" branch and never sends a PATCH.
The user-facing symptom is the same for any other property that was added by an earlier save in the same session: the second save will silently drop the change because the local view of "what the server has" is wrong.
Workaround until fixed: pass use_cache=False to from_id (or client.cache.clear()) between saves.
Related
Part of the broader caching review tracked in #80.
Summary
After modifying an object and saving it, re-fetching the same object via
from_id(use_cache=True)(the default) returns the original cached document, not the post-save state. As a result, the nextsave()can be a silent no-op even when a modification was made locally — most visibly when trying to unlink subjects/specimens by setting a property toNone. [Bug reported by Lyuba Zehl]Minimal example
Running the same
studied_specimens = None; save()on the originaldsvfrom step 1 (without the intervening re-fetch) does correctly unlink — that path works.Cause
KGClient.cache[uri]is populated on first fetch ininstance_from_full_uri(client.py) and never invalidated or refreshed by writes. Whensave()callsupdate_instance/replace_instance/create_new_instance, the server is updated butclient.cache[uri]retains the original document.On the second
from_idcall:instance_from_full_urireturns the stale cached document.DatasetVersiontherefore hasstudied_specimens = Noneandremote_datawith nostudiedSpecimenkey.studied_specimens = Noneis a no-op (alreadyNone).modified_data()comparescurrent=Noneagainstremote=None→ empty diff.save()enters the "unchanged, not updating" branch and never sends a PATCH.The user-facing symptom is the same for any other property that was added by an earlier save in the same session: the second save will silently drop the change because the local view of "what the server has" is wrong.
Workaround until fixed: pass
use_cache=Falsetofrom_id(orclient.cache.clear()) between saves.Related
Part of the broader caching review tracked in #80.