Fix gaps in our email error handling

This commit is contained in:
Raphael Michel
2021-03-31 10:05:15 +02:00
parent 8131ecf378
commit a08272571b

View File

@@ -15,6 +15,7 @@ import pytz
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from celery import chain from celery import chain
from celery.exceptions import MaxRetriesExceededError
from django.conf import settings from django.conf import settings
from django.core.mail import ( from django.core.mail import (
EmailMultiAlternatives, SafeMIMEMultipart, get_connection, EmailMultiAlternatives, SafeMIMEMultipart, get_connection,
@@ -389,11 +390,25 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
try: try:
backend.send_messages([email]) backend.send_messages([email])
except smtplib.SMTPResponseException as e: except (smtplib.SMTPResponseException, smtplib.SMTPSenderRefused) as e:
if e.smtp_code in (101, 111, 421, 422, 431, 442, 447, 452): if e.smtp_code in (101, 111, 421, 422, 431, 442, 447, 452):
self.retry(max_retries=5, countdown=2 ** (self.request.retries * 3)) # max is 2 ** (4*3) = 4096 seconds = 68 minutes # Most likely temporary, retry again (but pretty soon)
logger.exception('Error sending email') try:
self.retry(max_retries=5, countdown=2 ** (self.request.retries * 3)) # max is 2 ** (4*3) = 4096 seconds = 68 minutes
except MaxRetriesExceededError:
if order:
order.log_action(
'pretix.event.order.email.error',
data={
'subject': 'SMTP code {}, max retries exceeded'.format(e.smtp_code),
'message': e.smtp_error.decode() if isinstance(e.smtp_error, bytes) else str(e.smtp_error),
'recipient': '',
'invoices': [],
}
)
raise e
logger.exception('Error sending email')
if order: if order:
order.log_action( order.log_action(
'pretix.event.order.email.error', 'pretix.event.order.email.error',
@@ -405,10 +420,51 @@ def mail_send_task(self, *args, to: List[str], subject: str, body: str, html: st
} }
) )
raise SendMailException('Failed to send an email to {}.'.format(to))
except smtplib.SMTPRecipientsRefused as e:
smtp_codes = [a[0] for a in e.recipients.values()]
if not any(c >= 500 for c in smtp_codes):
# Not a permanent failure (mailbox full, service unavailable), retry later, but with large intervals
try:
self.retry(max_retries=5, countdown=2 ** (self.request.retries * 3) * 4) # max is 2 ** (4*3) * 4 = 16384 seconds = approx 4.5 hours
except MaxRetriesExceededError:
# ignore and go on with logging the error
pass
logger.exception('Error sending email')
if order:
message = []
for e, val in e.recipients.items():
message.append(f'{e}: {val[0]} {val[1].decode()}')
order.log_action(
'pretix.event.order.email.error',
data={
'subject': 'SMTP error',
'message': '\n'.join(message),
'recipient': '',
'invoices': [],
}
)
raise SendMailException('Failed to send an email to {}.'.format(to)) raise SendMailException('Failed to send an email to {}.'.format(to))
except Exception as e: except Exception as e:
if isinstance(e, (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, ssl.SSLError, OSError)): if isinstance(e, (smtplib.SMTPServerDisconnected, smtplib.SMTPConnectError, ssl.SSLError, OSError)):
self.retry(max_retries=5, countdown=2 ** (self.request.retries * 3)) # max is 2 ** (4*3) = 4096 seconds = 68 minutes try:
self.retry(max_retries=5, countdown=2 ** (self.request.retries * 3)) # max is 2 ** (4*3) = 4096 seconds = 68 minutes
except MaxRetriesExceededError:
if order:
order.log_action(
'pretix.event.order.email.error',
data={
'subject': 'Internal error',
'message': 'Max retries exceeded',
'recipient': '',
'invoices': [],
}
)
raise e
if order: if order:
order.log_action( order.log_action(
'pretix.event.order.email.error', 'pretix.event.order.email.error',