Skip to content

Commit c5ae9b8

Browse files
committed
Update delete task to skip over already deleted objects
fixes: #7910 Generated by: cursor-grok-4.5
1 parent 5b5bd58 commit c5ae9b8

4 files changed

Lines changed: 68 additions & 9 deletions

File tree

CHANGES/7910.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed `general_delete`, `ageneral_delete`, and `general_multi_delete` to skip instances that no longer exist instead of failing with a bare `DoesNotExist`. The task result now reports skipped pks under `skipped` and per-model delete counts.

pulpcore/app/models/content.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,10 @@ def delete(self, *args, **kwargs):
171171
args (list): list of positional arguments for Model.delete()
172172
kwargs (dict): dictionary of keyword arguments to pass to Model.delete()
173173
"""
174-
super().delete(*args, **kwargs)
174+
delete_result = super().delete(*args, **kwargs)
175175
# In case of rollback, we want the artifact to stay connected with it's file.
176176
transaction.on_commit(partial(self.file.delete, save=False))
177+
return delete_result
177178

178179

179180
class ArtifactQuerySet(BulkTouchQuerySet):

pulpcore/app/models/repository.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,14 +1400,14 @@ def delete(self, **kwargs):
14001400
raise RuntimeError(
14011401
_("Some repo relations of this version were not translated.")
14021402
)
1403-
super().delete(**kwargs)
1403+
return super().delete(**kwargs)
14041404

14051405
else:
14061406
with transaction.atomic():
14071407
RepositoryContent.objects.filter(version_added=self).delete()
14081408
RepositoryContent.objects.filter(version_removed=self).update(version_removed=None)
14091409
CreatedResource.objects.filter(object_id=self.pk).delete()
1410-
super().delete(**kwargs)
1410+
return super().delete(**kwargs)
14111411

14121412
def _compute_counts(self):
14131413
"""

pulpcore/app/tasks/base.py

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from collections import defaultdict
2+
from logging import getLogger
3+
14
from asgiref.sync import sync_to_async
25
from django.db import transaction
36

@@ -6,6 +9,8 @@
69
from pulpcore.app.models import CreatedResource
710
from pulpcore.plugin.models import MasterModel
811

12+
log = getLogger(__name__)
13+
914

1015
def general_create_from_temp_file(app_label, serializer_name, temp_file_pk, *args, **kwargs):
1116
"""
@@ -93,37 +98,73 @@ def general_delete(instance_id, app_label, serializer_name, **kwargs):
9398
id (str): the id of the model
9499
app_label (str): the Django app label of the plugin that provides the model
95100
serializer_name (str): name of the serializer class for the model
101+
102+
Returns:
103+
dict: Task result. Skipped instances are listed under `skipped`; otherwise contains
104+
the per-model delete counts from `Model.delete()`.
96105
"""
97106
deprecation_logger.warning(
98107
"`pulpcore.app.tasks.base.general_delete` is deprecated and will be removed in Pulp 4. "
99108
"Use `pulpcore.app.tasks.base.ageneral_delete` instead."
100109
)
101110
serializer_class = get_plugin_config(app_label).named_serializers[serializer_name]
102-
instance = serializer_class.Meta.model.objects.get(pk=instance_id)
111+
model = serializer_class.Meta.model
112+
output = defaultdict(list)
113+
try:
114+
instance = model.objects.get(pk=instance_id)
115+
except model.DoesNotExist:
116+
log.info(
117+
"Skipping delete of %s pk=%s; it no longer exists.",
118+
model.__name__,
119+
instance_id,
120+
)
121+
output["skipped"].append(str(instance_id))
122+
return dict(output)
103123
if isinstance(instance, MasterModel):
104124
instance = instance.cast()
105-
instance.delete()
125+
output.update(instance.delete()[1])
126+
return dict(output)
106127

107128

108129
def general_multi_delete(instance_ids, **kwargs):
109130
"""
110131
Delete a list of model instances in a transaction
111132
112133
The model instances are identified using the id, app_label, and serializer_name.
134+
Instances that no longer exist are skipped.
113135
114136
Args:
115137
instance_ids (list): List of tupels of id, app_label, serializer_name
138+
139+
Returns:
140+
dict: Task result. Skipped instances are listed under `skipped`; otherwise contains
141+
the accumulated per-model delete counts from `Model.delete()`.
116142
"""
143+
output = defaultdict(list)
144+
counts = defaultdict(int)
117145
instances = []
118146
for instance_id, app_label, serializer_name in instance_ids:
119147
serializer_class = get_plugin_config(app_label).named_serializers[serializer_name]
120-
instance = serializer_class.Meta.model.objects.get(pk=instance_id)
148+
model = serializer_class.Meta.model
149+
try:
150+
instance = model.objects.get(pk=instance_id)
151+
except model.DoesNotExist:
152+
log.info(
153+
"Skipping delete of %s pk=%s; it no longer exists.",
154+
model.__name__,
155+
instance_id,
156+
)
157+
output["skipped"].append(str(instance_id))
158+
continue
121159
if isinstance(instance, MasterModel):
122160
instance = instance.cast()
123161
instances.append(instance)
124162
with transaction.atomic():
125163
for instance in instances:
126-
instance.delete()
164+
for model_label, count in instance.delete()[1].items():
165+
counts[model_label] += count
166+
output.update(counts)
167+
return dict(output)
127168

128169

129170
async def ageneral_update(instance_id, app_label, serializer_name, *args, **kwargs):
@@ -148,9 +189,25 @@ async def ageneral_update(instance_id, app_label, serializer_name, *args, **kwar
148189
async def ageneral_delete(instance_id, app_label, serializer_name, **kwargs):
149190
"""
150191
Async version of [pulpcore.app.tasks.base.general_delete][].
192+
193+
Returns:
194+
dict: Task result. Skipped instances are listed under `skipped`; otherwise contains
195+
the per-model delete counts from `Model.adelete()`.
151196
"""
152197
serializer_class = get_plugin_config(app_label).named_serializers[serializer_name]
153-
instance = await serializer_class.Meta.model.objects.aget(pk=instance_id)
198+
model = serializer_class.Meta.model
199+
output = defaultdict(list)
200+
try:
201+
instance = await model.objects.aget(pk=instance_id)
202+
except model.DoesNotExist:
203+
log.info(
204+
"Skipping delete of %s pk=%s; it no longer exists.",
205+
model.__name__,
206+
instance_id,
207+
)
208+
output["skipped"].append(str(instance_id))
209+
return dict(output)
154210
if isinstance(instance, MasterModel):
155211
instance = await instance.acast()
156-
await instance.adelete()
212+
output.update((await instance.adelete())[1])
213+
return dict(output)

0 commit comments

Comments
 (0)