mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Allow requests session customization, document use for automatic retries
* Refactor create_session() out of AnymailRequestsBackend * Document automatic retries with Requests
This commit is contained in:
@@ -23,3 +23,46 @@ transient ESP errors depends on your Django project:
|
||||
In addition to handling connectivity issues, either of these approaches also has the advantage
|
||||
of moving email sending to a background thread. This is a best practice for sending email from
|
||||
Django, as it allows your web views to respond faster.
|
||||
|
||||
Automatic retries
|
||||
-----------------
|
||||
|
||||
Backends that use :pypi:`requests` for network calls can configure its built-in retry
|
||||
functionality. Subclass the Anymail backend and mount instances of
|
||||
:class:`~requests.adapters.HTTPAdapter` and :class:`~urllib3.util.Retry` configured with
|
||||
your settings on the :class:`~requests.Session` object in `create_session()`.
|
||||
|
||||
Automatic retries aren't a substitute for sending emails in a background thread, they're
|
||||
a way to simplify your retry logic within the worker. Be aware that retrying `read` and `other`
|
||||
failures may result in sending duplicate emails. Requests will only attempt to retry idempotent
|
||||
HTTP verbs by default, you may need to whitelist the verbs used by your backend's API in
|
||||
`allowed_methods` to actually get any retries. It can also automatically retry error HTTP
|
||||
status codes for you but you may need to configure `status_forcelist` with the error HTTP status
|
||||
codes used by your backend provider.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import anymail.backends.mandrill
|
||||
from django.conf import settings
|
||||
import requests.adapters
|
||||
|
||||
|
||||
class RetryableMandrillEmailBackend(anymail.backends.mandrill.EmailBackend):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
retry = requests.adapters.Retry(
|
||||
total=settings.EMAIL_TOTAL_RETRIES,
|
||||
connect=settings.EMAIL_CONNECT_RETRIES,
|
||||
read=settings.EMAIL_READ_RETRIES,
|
||||
status=settings.EMAIL_HTTP_STATUS_RETRIES,
|
||||
other=settings.EMAIL_OTHER_RETRIES,
|
||||
allowed_methods=False, # Retry all HTTP verbs
|
||||
status_forcelist=settings.EMAIL_HTTP_STATUS_RETRYABLE,
|
||||
backoff_factor=settings.EMAIL_RETRY_BACKOFF_FACTOR,
|
||||
)
|
||||
self.retryable_adapter = requests.adapters.HTTPAdapter(max_retries=retry)
|
||||
|
||||
def create_session(self):
|
||||
session = super().create_session()
|
||||
session.mount("https://", self.retryable_adapter)
|
||||
return session
|
||||
|
||||
Reference in New Issue
Block a user