Skip to content

Commit 05a9288

Browse files
sazauvipy
authored andcommitted
delete expired results in batches to avoid running OOM
1 parent 96e8311 commit 05a9288

File tree

3 files changed

+26
-4
lines changed

3 files changed

+26
-4
lines changed

django_celery_results/managers.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,16 @@ def get_all_expired(self, expires):
8484
"""Get all expired results."""
8585
return self.filter(date_done__lt=now() - maybe_timedelta(expires))
8686

87-
def delete_expired(self, expires):
87+
def delete_expired(self, expires, batch_size=100000):
8888
"""Delete all expired results."""
89-
with transaction.atomic(using=self.db):
90-
self.get_all_expired(expires).delete()
89+
qs = self.get_all_expired(expires).order_by("id")
90+
91+
while True:
92+
ids = list(qs.values_list("id", flat=True)[:batch_size])
93+
if not ids:
94+
break
95+
with transaction.atomic(using=self.db):
96+
self.filter(id__in=ids).delete()
9197

9298

9399
class TaskResultManager(ResultManager):

t/integration/benchmark_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,4 @@ def test_taskresult_delete_expired(self):
6767
'setup time: {setup:.2f}\n'
6868
'bench time: {bench:.2f}\n'
6969
).format(setup=after_setup - start, bench=done - after_setup))
70-
assert self.benchmark.stats.stats.max < 1
70+
assert self.benchmark.stats.stats.max < 5

t/unit/test_models.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,22 @@ class TransactionError(Exception):
210210
except TransactionError:
211211
pass
212212

213+
def test_result_batch_deletion(self):
214+
# Create 200 expired records
215+
TaskResult.objects.bulk_create(
216+
[
217+
TaskResult(task_id=uuid(), date_done=now() - timedelta(days=1))
218+
for i in range(200)
219+
]
220+
)
221+
assert TaskResult.objects.get_all_expired(0).count() == 200
222+
223+
# Run deletion in small batches
224+
TaskResult.objects.delete_expired(0, batch_size=25)
225+
226+
# All expired records should be gone
227+
assert TaskResult.objects.get_all_expired(0).count() == 0
228+
213229

214230
@pytest.mark.usefixtures('depends_on_current_app')
215231
class test_ModelsWithoutDefaultDB(TransactionTestCase):

0 commit comments

Comments
 (0)