Skip to content

Commit bebe52d

Browse files
committed
fix-aurora-clean-task
1 parent abf642f commit bebe52d

File tree

2 files changed

+71
-8
lines changed

2 files changed

+71
-8
lines changed

src/hope/contrib/aurora/celery_tasks.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import timedelta
12
import logging
23
from typing import TYPE_CHECKING, Any, Optional
34

@@ -125,14 +126,30 @@ def automate_rdi_creation_task(
125126
@app.task
126127
@log_start_and_end
127128
@sentry_tags
128-
def clean_old_record_files_task() -> None:
129-
"""Task to clean (sets to null) Record's files field."""
130-
from datetime import timedelta
131-
129+
def clean_old_record_files_task(batch_size: int = 100) -> None:
130+
"""Task to remove old imported aurora records."""
132131
try:
133132
time_threshold = timezone.now() - timedelta(config.CLEARING_RECORD_FILES_TIMEDELTA)
134-
Record.objects.filter(timestamp__lt=time_threshold, status=Record.STATUS_IMPORTED).delete()
135-
logger.info("Record's files have benn successfully cleared")
136-
except Exception as e:
137-
logger.warning(e)
133+
qs = Record.objects.filter(timestamp__lt=time_threshold, status=Record.STATUS_IMPORTED)
134+
total = qs.count()
135+
total_batches = (total + batch_size - 1) // batch_size
136+
137+
batch_num = 0
138+
total_deleted = 0
139+
140+
while True:
141+
batch = list(qs.values_list("pk", flat=True)[:batch_size])
142+
if not batch:
143+
break
144+
145+
deleted_count, _ = Record.objects.filter(pk__in=batch).delete()
146+
batch_num += 1
147+
total_deleted += deleted_count
148+
149+
logger.info(f"Batch {batch_num}/{total_batches} ({deleted_count} deleted, total: {total_deleted}/{total})")
150+
151+
logger.info("Record files have been successfully cleared")
152+
153+
except Exception: # noqa
154+
logger.exception("Error cleaning old record files")
138155
raise

tests/unit/apps/aurora/test_clean_old_record_files_task.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import timedelta
2+
from typing import TYPE_CHECKING
23

34
from django.utils import timezone
45
import pytest
@@ -7,6 +8,9 @@
78
from hope.contrib.aurora.celery_tasks import clean_old_record_files_task
89
from hope.contrib.aurora.models import Record
910

11+
if TYPE_CHECKING:
12+
from pytest_mock import MockerFixture
13+
1014
pytestmark = pytest.mark.django_db
1115

1216

@@ -50,3 +54,45 @@ def test_clean_old_record_files_task(record_set: dict[str, Record]) -> None:
5054
assert record_set["recent_imported"].id in remaining_ids
5155
assert record_set["old_error"].id in remaining_ids
5256
assert record_set["old_to_import"].id in remaining_ids
57+
58+
59+
def test_clean_old_record_files_task_empty() -> None:
60+
# No records at all
61+
clean_old_record_files_task()
62+
assert Record.objects.count() == 0
63+
64+
65+
def test_clean_old_record_files_task_batching(mocker: "MockerFixture") -> None:
66+
now = timezone.now()
67+
# Create 5 records that should be deleted
68+
RecordFactory.create_batch(5, status=Record.STATUS_IMPORTED, timestamp=now - timedelta(days=100))
69+
70+
# Run with batch_size=2, should take 3 batches (2+2+1)
71+
clean_old_record_files_task(batch_size=2)
72+
73+
assert Record.objects.count() == 0
74+
75+
76+
def test_clean_old_record_files_task_logging(mocker: "MockerFixture") -> None:
77+
now = timezone.now()
78+
RecordFactory.create_batch(3, status=Record.STATUS_IMPORTED, timestamp=now - timedelta(days=100))
79+
mock_logger = mocker.patch("hope.contrib.aurora.celery_tasks.logger")
80+
81+
clean_old_record_files_task(batch_size=2)
82+
83+
# Should log 2 batches and one final message
84+
assert mock_logger.info.call_count == 3
85+
mock_logger.info.assert_any_call("Batch 1/2 (2 deleted, total: 2/3)")
86+
mock_logger.info.assert_any_call("Batch 2/2 (1 deleted, total: 3/3)")
87+
mock_logger.info.assert_any_call("Record files have been successfully cleared")
88+
89+
90+
def test_clean_old_record_files_task_error_handling(mocker: "MockerFixture") -> None:
91+
mock_logger = mocker.patch("hope.contrib.aurora.celery_tasks.logger")
92+
# Force an exception by mocking timezone.now to raise something
93+
mocker.patch("hope.contrib.aurora.celery_tasks.timezone.now", side_effect=Exception("Database error"))
94+
95+
with pytest.raises(Exception, match="Database error"):
96+
clean_old_record_files_task()
97+
98+
mock_logger.exception.assert_called_once_with("Error cleaning old record files")

0 commit comments

Comments
 (0)