Improve performance of send_expire_warnings and expire_orders

This commit is contained in:
Raphael Michel
2019-11-24 15:27:28 +01:00
parent 2eb92bef9b
commit 24d6816dac

View File

@@ -7,6 +7,7 @@ from typing import List, Optional
from celery.exceptions import MaxRetriesExceededError from celery.exceptions import MaxRetriesExceededError
from django.conf import settings from django.conf import settings
from django.core.cache import cache
from django.db import transaction from django.db import transaction
from django.db.models import Exists, F, Max, OuterRef, Q, Sum from django.db.models import Exists, F, Max, OuterRef, Q, Sum
from django.db.models.functions import Greatest from django.db.models.functions import Greatest
@@ -857,14 +858,14 @@ def _perform_order(event: Event, payment_provider: str, position_ids: List[str],
@receiver(signal=periodic_task) @receiver(signal=periodic_task)
@scopes_disabled() @scopes_disabled()
def expire_orders(sender, **kwargs): def expire_orders(sender, **kwargs):
eventcache = {} event_id = None
expire = None
for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING, for o in Order.objects.filter(expires__lt=now(), status=Order.STATUS_PENDING,
require_approval=False).select_related('event'): require_approval=False).select_related('event').order_by('event_id'):
expire = eventcache.get(o.event.pk, None) if o.event_id != event_id:
if expire is None: expire = settings.get('payment_term_expire_automatically', as_type=bool)
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool) event_id = o.event_id
eventcache[o.event.pk] = expire
if expire: if expire:
mark_order_expired(o) mark_order_expired(o)
@@ -872,31 +873,34 @@ def expire_orders(sender, **kwargs):
@receiver(signal=periodic_task) @receiver(signal=periodic_task)
@scopes_disabled() @scopes_disabled()
def send_expiry_warnings(sender, **kwargs): def send_expiry_warnings(sender, **kwargs):
eventcache = {}
today = now().replace(hour=0, minute=0, second=0) today = now().replace(hour=0, minute=0, second=0)
days = None
event_id = None
for o in Order.objects.filter( for o in Order.objects.filter(
expires__gte=today, expiry_reminder_sent=False, status=Order.STATUS_PENDING, expires__gte=today, expiry_reminder_sent=False, status=Order.STATUS_PENDING,
datetime__lte=now() - timedelta(hours=2), require_approval=False datetime__lte=now() - timedelta(hours=2), require_approval=False
).only('pk'): ).only('pk', 'event_id').order_by('event_id'):
with transaction.atomic(): if event_id != o.event_id:
o = Order.objects.select_related('event').select_for_update().get(pk=o.pk) settings = o.event.settings
if o.status != Order.STATUS_PENDING or o.expiry_reminder_sent: days = cache.get_or_set('{}:{}:setting_mail_days_order_expire_warning'.format('event', o.event_id),
# Race condition default=lambda: settings.get('mail_days_order_expire_warning', as_type=int),
continue timeout=3600)
eventsettings = eventcache.get(o.event.pk, None) event_id = o.event_id
if eventsettings is None:
eventsettings = o.event.settings if days and (o.expires - today).days <= days:
eventcache[o.event.pk] = eventsettings with transaction.atomic():
o = Order.objects.select_related('event').select_for_update().get(pk=o.pk)
if o.status != Order.STATUS_PENDING or o.expiry_reminder_sent:
# Race condition
continue
days = eventsettings.get('mail_days_order_expire_warning', as_type=int)
if days and (o.expires - today).days <= days:
with language(o.locale): with language(o.locale):
o.expiry_reminder_sent = True o.expiry_reminder_sent = True
o.save(update_fields=['expiry_reminder_sent']) o.save(update_fields=['expiry_reminder_sent'])
email_template = eventsettings.mail_text_order_expire_warning email_template = settings.mail_text_order_expire_warning
email_context = get_email_context(event=o.event, order=o) email_context = get_email_context(event=o.event, order=o)
if eventsettings.payment_term_expire_automatically: if settings.payment_term_expire_automatically:
email_subject = _('Your order is about to expire: %(code)s') % {'code': o.code} email_subject = _('Your order is about to expire: %(code)s') % {'code': o.code}
else: else:
email_subject = _('Your order is pending payment: %(code)s') % {'code': o.code} email_subject = _('Your order is pending payment: %(code)s') % {'code': o.code}