Skip to content

Commit 846f805

Browse files
committed
Fix LocalFileDescriptorStore mutation persistence
Previously, `LocalFileDescriptorStore` (and `LocalFileIdentifiableStore` in the same iteration) had two compounding bugs: `get_item()` always re-read from disk even on a cache hit, and mutations were never written back to disk at all — so any change to a retrieved descriptor was lost once its cache entry was evicted. This change mirrors the fix already applied to `LocalFileIdentifiableStore` (SDK) and `repository.py`: - `get_descriptor_by_hash()` / `get_identifiable_by_hash()`: check the cache before inserting a freshly-deserialized object, so `__iter__` returns the same instance already in use rather than a new one. - `get_item()` in `LocalFileDescriptorStore`: return the cached object directly on a hit without going to disk. - Add `LocalFileDescriptorStore.commit()` to write the current in-memory state of a descriptor back to its file. - `registry.py`: replace all `descriptor.commit()` / `aas_descriptor.commit()` / `submodel_descriptor.commit()` calls (which were no-ops on the model object) with `self.object_store.commit(x)`.
1 parent 55e75dd commit 846f805

3 files changed

Lines changed: 31 additions & 17 deletions

File tree

sdk/basyx/aas/backend/local_file.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ def get_identifiable_by_hash(self, hash_: str) -> model.Identifiable:
7575
except FileNotFoundError as e:
7676
raise KeyError("No Identifiable with hash {} found in local file database".format(hash_)) from e
7777
with self._object_cache_lock:
78+
if obj.id in self._object_cache:
79+
return self._object_cache[obj.id]
7880
self._object_cache[obj.id] = obj
7981
return obj
8082

server/app/backend/local_file.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,15 @@ def get_descriptor_by_hash(self, hash_: str) -> _DESCRIPTOR_TYPE:
6767
6868
:raises KeyError: If the respective file could not be found
6969
"""
70-
# Try to get the correct file
7170
try:
7271
with open("{}/{}.json".format(self.directory_path, hash_), "r") as file:
7372
obj = json.load(file, cls=jsonization.ServerAASFromJsonDecoder)
7473
except FileNotFoundError as e:
7574
raise KeyError("No Descriptor with hash {} found in local file database".format(hash_)) from e
76-
# If we still have a local replication of that object (since it is referenced from anywhere else), update that
77-
# replication and return it.
7875
with self._object_cache_lock:
7976
if obj.id in self._object_cache:
80-
old_obj = self._object_cache[obj.id]
81-
old_obj.update_from(obj)
82-
return old_obj
83-
self._object_cache[obj.id] = obj
77+
return self._object_cache[obj.id]
78+
self._object_cache[obj.id] = obj
8479
return obj
8580

8681
def get_item(self, identifier: model.Identifier) -> _DESCRIPTOR_TYPE:
@@ -89,6 +84,9 @@ def get_item(self, identifier: model.Identifier) -> _DESCRIPTOR_TYPE:
8984
9085
:raises KeyError: If the respective file could not be found
9186
"""
87+
with self._object_cache_lock:
88+
if identifier in self._object_cache:
89+
return self._object_cache[identifier]
9290
try:
9391
return self.get_descriptor_by_hash(self._transform_id(identifier))
9492
except KeyError as e:
@@ -113,6 +111,20 @@ def add(self, x: _DESCRIPTOR_TYPE) -> None:
113111
with self._object_cache_lock:
114112
self._object_cache[x.id] = x
115113

114+
def commit(self, x: _DESCRIPTOR_TYPE) -> None:
115+
"""
116+
Write the current in-memory state of a stored descriptor back to its file.
117+
118+
:param x: The descriptor to persist
119+
:raises KeyError: If the descriptor is not present in the store
120+
"""
121+
if not os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(x.id))):
122+
raise KeyError("No AAS Descriptor object with id {} exists in local file database".format(x.id))
123+
with open("{}/{}.json".format(self.directory_path, self._transform_id(x.id)), "w") as file:
124+
serialized = json.loads(json.dumps(x, cls=jsonization.ServerAASToJsonEncoder))
125+
serialized["modelType"] = DESCRIPTOR_TYPE_TO_STRING[type(x)]
126+
json.dump(serialized, file, indent=4)
127+
116128
def discard(self, x: _DESCRIPTOR_TYPE) -> None:
117129
"""
118130
Delete an :class:`~app.model.descriptor.Descriptor` AAS object from the local file store

server/app/interfaces/registry.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def post_aas_descriptor(
180180
self.object_store.add(descriptor)
181181
except KeyError as e:
182182
raise Conflict(f"AssetAdministrationShellDescriptor with Identifier {descriptor.id} already exists!") from e
183-
descriptor.commit()
183+
self.object_store.commit(descriptor)
184184
created_resource_url = map_adapter.build(
185185
self.get_aas_descriptor_by_id, {"aas_id": descriptor.id}, force_external=True
186186
)
@@ -202,12 +202,12 @@ def put_aas_descriptor_by_id(
202202
request, server_model.AssetAdministrationShellDescriptor, is_stripped_request(request)
203203
)
204204
)
205-
descriptor.commit()
205+
self.object_store.commit(descriptor)
206206
return response_t()
207207
except NotFound:
208208
descriptor = HTTPApiDecoder.request_body(request, server_model.AssetAdministrationShellDescriptor, False)
209209
self.object_store.add(descriptor)
210-
descriptor.commit()
210+
self.object_store.commit(descriptor)
211211
created_resource_url = map_adapter.build(
212212
self.get_aas_descriptor_by_id, {"aas_id": descriptor.id}, force_external=True
213213
)
@@ -247,7 +247,7 @@ def post_submodel_descriptor_through_superpath(
247247
if any(sd.id == submodel_descriptor.id for sd in aas_descriptor.submodel_descriptors):
248248
raise Conflict(f"Submodel Descriptor with Identifier {submodel_descriptor.id} already exists!")
249249
aas_descriptor.submodel_descriptors.append(submodel_descriptor)
250-
aas_descriptor.commit()
250+
self.object_store.commit(aas_descriptor)
251251
created_resource_url = map_adapter.build(
252252
self.get_submodel_descriptor_by_id_through_superpath,
253253
{"aas_id": aas_descriptor.id, "submodel_id": submodel_descriptor.id},
@@ -269,14 +269,14 @@ def put_submodel_descriptor_by_id_through_superpath(
269269
submodel_descriptor.update_from(
270270
HTTPApiDecoder.request_body(request, server_model.SubmodelDescriptor, is_stripped_request(request))
271271
)
272-
aas_descriptor.commit()
272+
self.object_store.commit(aas_descriptor)
273273
return response_t()
274274
except NotFound:
275275
submodel_descriptor = HTTPApiDecoder.request_body(
276276
request, server_model.SubmodelDescriptor, is_stripped_request(request)
277277
)
278278
aas_descriptor.submodel_descriptors.append(submodel_descriptor)
279-
aas_descriptor.commit()
279+
self.object_store.commit(aas_descriptor)
280280
created_resource_url = map_adapter.build(
281281
self.get_submodel_descriptor_by_id_through_superpath,
282282
{"aas_id": aas_descriptor.id, "submodel_id": submodel_descriptor.id},
@@ -293,7 +293,7 @@ def delete_submodel_descriptor_by_id_through_superpath(
293293
if submodel_descriptor is None:
294294
raise NotFound(f"Submodel Descriptor with Identifier {submodel_id} not found in AssetAdministrationShell!")
295295
aas_descriptor.submodel_descriptors.remove(submodel_descriptor)
296-
aas_descriptor.commit()
296+
self.object_store.commit(aas_descriptor)
297297
return response_t()
298298

299299
# ------ Submodel REGISTRY ROUTES -------
@@ -321,7 +321,7 @@ def post_submodel_descriptor(
321321
self.object_store.add(submodel_descriptor)
322322
except KeyError as e:
323323
raise Conflict(f"Submodel Descriptor with Identifier {submodel_descriptor.id} already exists!") from e
324-
submodel_descriptor.commit()
324+
self.object_store.commit(submodel_descriptor)
325325
created_resource_url = map_adapter.build(
326326
self.get_submodel_descriptor_by_id, {"submodel_id": submodel_descriptor.id}, force_external=True
327327
)
@@ -335,14 +335,14 @@ def put_submodel_descriptor_by_id(
335335
submodel_descriptor.update_from(
336336
HTTPApiDecoder.request_body(request, server_model.SubmodelDescriptor, is_stripped_request(request))
337337
)
338-
submodel_descriptor.commit()
338+
self.object_store.commit(submodel_descriptor)
339339
return response_t()
340340
except NotFound:
341341
submodel_descriptor = HTTPApiDecoder.request_body(
342342
request, server_model.SubmodelDescriptor, is_stripped_request(request)
343343
)
344344
self.object_store.add(submodel_descriptor)
345-
submodel_descriptor.commit()
345+
self.object_store.commit(submodel_descriptor)
346346
created_resource_url = map_adapter.build(
347347
self.get_submodel_descriptor_by_id, {"submodel_id": submodel_descriptor.id}, force_external=True
348348
)

0 commit comments

Comments
 (0)