78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
"""
|
||
Celery задачи для модуля communications
|
||
"""
|
||
|
||
import logging
|
||
from datetime import timedelta
|
||
|
||
from celery import shared_task
|
||
from django.db import transaction
|
||
from django.utils import timezone
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
@shared_task
|
||
def cleanup_orphaned_attachments():
|
||
"""
|
||
Очистка временных вложений без привязки к сообщению.
|
||
Удаляет MessageAttachment с message=null старше 1 часа.
|
||
"""
|
||
from communications.models import MessageAttachment
|
||
|
||
cutoff_time = timezone.now() - timedelta(hours=1)
|
||
|
||
orphaned = MessageAttachment.objects.filter(
|
||
message__isnull=True, uploaded_at__lt=cutoff_time
|
||
)
|
||
|
||
count = 0
|
||
# Candidate IDs are only hints. An upload may be claimed while this task is
|
||
# running, so recheck it under the same row lock used by message edits.
|
||
for attachment_id in orphaned.values_list("pk", flat=True).iterator(chunk_size=200):
|
||
try:
|
||
with transaction.atomic():
|
||
attachment = (
|
||
MessageAttachment.objects.select_for_update(skip_locked=True)
|
||
.filter(
|
||
pk=attachment_id,
|
||
message__isnull=True,
|
||
uploaded_at__lt=cutoff_time,
|
||
)
|
||
.first()
|
||
)
|
||
if attachment is None:
|
||
continue
|
||
files = [
|
||
(file.storage, file.name)
|
||
for file in (attachment.file, attachment.thumbnail)
|
||
if file
|
||
]
|
||
attachment.delete()
|
||
for storage, name in files:
|
||
transaction.on_commit(
|
||
lambda storage=storage, name=name: storage.delete(name),
|
||
robust=True,
|
||
)
|
||
count += 1
|
||
except Exception as exc:
|
||
logger.exception(
|
||
"Orphaned chat attachment deletion failed",
|
||
extra={
|
||
"event": "chat.attachment_cleanup.item_failed",
|
||
"attachment_id": attachment_id,
|
||
"error_type": type(exc).__name__,
|
||
},
|
||
)
|
||
|
||
if count:
|
||
logger.info(
|
||
"Orphaned chat attachment cleanup completed",
|
||
extra={
|
||
"event": "chat.attachment_cleanup.completed",
|
||
"attachment_count": count,
|
||
},
|
||
)
|
||
|
||
return count
|