Skip to content

Commit 87ddbb5

Browse files
committed
Fix: Resolved issue #53 by removing alias if make_persistent fails.
1 parent fbf578e commit 87ddbb5

File tree

2 files changed

+66
-46
lines changed

2 files changed

+66
-46
lines changed

src/dataclay/runtime.py

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
""" Class description goes here. """
2-
31
from __future__ import annotations
42

53
import asyncio
@@ -123,52 +121,58 @@ async def make_persistent(
123121
alias, instance._dc_meta.dataset_name, instance._dc_meta.id
124122
)
125123

126-
# If called inside backend runtime, default is to register in the current backend
127-
# unles another backend is explicitly specified
128-
if self.is_backend and (backend_id is None or backend_id == self.backend.id):
129-
logger.debug("(%s) Registering the object in this backend", instance._dc_meta.id)
130-
instance._dc_meta.master_backend_id = self.backend_id
131-
await self.metadata_service.upsert_object(instance._dc_meta)
132-
instance._dc_is_registered = True
133-
self.inmemory_objects[instance._dc_meta.id] = instance
134-
self.data_manager.add_hard_reference(instance)
135-
return self.backend_id
136-
137-
# Called from client runtime, default is to choose a random backend
138-
elif backend_id is None:
139-
logger.debug(
140-
"(%s) Choosing a random backend to register the object", instance._dc_meta.id
141-
)
142-
# If there is no backend client, update the list of backend clients
143-
if not self.backend_clients:
144-
await self.backend_clients.update()
124+
try:
125+
# If called inside backend runtime, default is to register in the current backend
126+
# unles another backend is explicitly specified
127+
if self.is_backend and (backend_id is None or backend_id == self.backend.id):
128+
logger.debug("(%s) Registering the object in this backend", instance._dc_meta.id)
129+
instance._dc_meta.master_backend_id = self.backend_id
130+
await self.metadata_service.upsert_object(instance._dc_meta)
131+
instance._dc_is_registered = True
132+
self.inmemory_objects[instance._dc_meta.id] = instance
133+
self.data_manager.add_hard_reference(instance)
134+
return self.backend_id
135+
136+
# Called from client runtime, default is to choose a random backend
137+
elif backend_id is None:
138+
logger.debug(
139+
"(%s) Choosing a random backend to register the object", instance._dc_meta.id
140+
)
141+
# If there is no backend client, update the list of backend clients
145142
if not self.backend_clients:
146-
raise RuntimeError(
147-
f"({instance._dc_meta.id}) No backends available to register the object"
148-
)
149-
# Choose a random backend
150-
backend_id, backend_client = random.choice(tuple(self.backend_clients.items()))
151-
else:
152-
backend_client = await self.backend_clients.get(backend_id)
143+
await self.backend_clients.update()
144+
if not self.backend_clients:
145+
raise RuntimeError(
146+
f"({instance._dc_meta.id}) No backends available to register the object"
147+
)
148+
# Choose a random backend
149+
backend_id, backend_client = random.choice(tuple(self.backend_clients.items()))
150+
else:
151+
backend_client = await self.backend_clients.get(backend_id)
153152

154-
# Serialize instance with a recursive Pickle
155-
visited_objects: dict[UUID, DataClayObject] = {}
156-
serialized_objects = await recursive_dcdumps(
157-
instance, local_objects=visited_objects, make_persistent=True
158-
)
159-
# Register the object in the backend
160-
await backend_client.make_persistent(serialized_objects)
161-
162-
# Update the object metadata
163-
for dc_object in visited_objects.values():
164-
dc_object._clean_dc_properties()
165-
dc_object._dc_is_registered = True
166-
dc_object._dc_is_local = False
167-
dc_object._dc_is_loaded = False
168-
dc_object._dc_meta.master_backend_id = backend_id
169-
self.inmemory_objects[dc_object._dc_meta.id] = dc_object
170-
171-
return instance._dc_meta.master_backend_id
153+
# Serialize instance with a recursive Pickle
154+
visited_objects: dict[UUID, DataClayObject] = {}
155+
serialized_objects = await recursive_dcdumps(
156+
instance, local_objects=visited_objects, make_persistent=True
157+
)
158+
# Register the object in the backend
159+
await backend_client.make_persistent(serialized_objects)
160+
161+
# Update the object metadata
162+
for dc_object in visited_objects.values():
163+
dc_object._clean_dc_properties()
164+
dc_object._dc_is_registered = True
165+
dc_object._dc_is_local = False
166+
dc_object._dc_is_loaded = False
167+
dc_object._dc_meta.master_backend_id = backend_id
168+
self.inmemory_objects[dc_object._dc_meta.id] = dc_object
169+
170+
return instance._dc_meta.master_backend_id
171+
except Exception as e:
172+
# If there is an error, delete the alias
173+
if alias:
174+
await self.metadata_service.delete_alias(alias, instance._dc_meta.dataset_name)
175+
raise e
172176

173177
##################
174178
# Object methods #

tests/functional/test_alias.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import uuid
2+
13
import pytest
24

35
from dataclay.contrib.modeltest.family import Person
@@ -105,3 +107,17 @@ async def test_get_aliases_async(client):
105107
await Person.a_delete_alias("test_get_aliases_async")
106108
await Person.a_delete_alias("test_get_aliases_1_async")
107109
await Person.a_delete_alias("test_get_aliases_2_async")
110+
111+
112+
def test_error_alias_creation(client):
113+
"""
114+
If the make_persistent method fails, the alias should not be created
115+
"""
116+
person = Person("Marc", 24)
117+
with pytest.raises(KeyError):
118+
# Force an error by using an invalid backend_id
119+
invalid_backend_id = uuid.uuid4()
120+
person.make_persistent(alias="test_error_alias_creation", backend_id=invalid_backend_id)
121+
with pytest.raises(DataClayException) as excinfo:
122+
Person.get_by_alias("test_error_alias_creation")
123+
assert "does not exist" in str(excinfo.value)

0 commit comments

Comments
 (0)