Mutating an object that was retrieved from LocalFileIdentifiableStore (or CouchDBIdentifiableStore) does not write the change back to the underlying storage. Once the object is evicted from the in-process weak-reference cache — for example when a second process or worker reads the same object — the stale on-disk/database value is returned.
This was caused by PR #370, which removed Referable.commit() and the LocalFileBackend/CouchDBBackend classes. Before the refactor, every mutation handler in the server called .commit() on the modified object, which triggered LocalFileBackend.commit_object() / CouchDBBackend.commit_object() to write the updated object back to storage. No replacement mechanism was introduced, so the write-back is now silently dropped.
We did not notice this, since the change is reflected in the WeakValueDictionary and we did not have a unittest for this (standard server) case. However, once this reference is garbage collected, it becomes an issue.
To reproduce, you need to empty the WeakValueDictionary (or simply read the JSON file):
import gc, shutil, tempfile
from basyx.aas import model
from basyx.aas.backend.local_file import LocalFileIdentifiableStore
store_dir = tempfile.mkdtemp()
store = LocalFileIdentifiableStore(store_dir)
submodel = model.Submodel(
id_='https://example.org/MutationTest',
submodel_element={
model.Property(id_short='Prop', value_type=model.datatypes.String, value='before')
}
)
store.add(submodel)
retrieved: model.Submodel = store.get_item('https://example.org/MutationTest')
prop: model.Property = retrieved.get_referable(['Prop'])
prop.update_from(model.Property(id_short='Prop', value_type=model.datatypes.String, value='after'))
print(f"After mutation (in-memory): {prop.value!r}") # 'after'
del submodel, retrieved, prop
gc.collect() # evict WeakValueDictionary cache
fresh: model.Submodel = store.get_item('https://example.org/MutationTest')
fresh_prop: model.Property = fresh.get_referable(['Prop']) # type: ignore[assignment]
print(f"After cache eviction (disk): {fresh_prop.value!r}") # 'before' => bug
shutil.rmtree(store_dir)
Mutating an object that was retrieved from
LocalFileIdentifiableStore(orCouchDBIdentifiableStore) does not write the change back to the underlying storage. Once the object is evicted from the in-process weak-reference cache — for example when a second process or worker reads the same object — the stale on-disk/database value is returned.This was caused by PR #370, which removed
Referable.commit()and theLocalFileBackend/CouchDBBackendclasses. Before the refactor, every mutation handler in the server called.commit()on the modified object, which triggeredLocalFileBackend.commit_object()/CouchDBBackend.commit_object()to write the updated object back to storage. No replacement mechanism was introduced, so the write-back is now silently dropped.We did not notice this, since the change is reflected in the
WeakValueDictionaryand we did not have a unittest for this (standard server) case. However, once this reference is garbage collected, it becomes an issue.To reproduce, you need to empty the
WeakValueDictionary(or simply read theJSONfile):