Skip to content

Commit

Permalink
Merge pull request #164 from jaap3/retry-limit
Browse files Browse the repository at this point in the history
Retry limit
  • Loading branch information
spookylukey authored Jun 28, 2023
2 parents d7223b0 + 7dc337a commit 5f4944a
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Change log
* Add fix for email address that is a NoneType
* Stop testing on unsupported Python (<3.7) and Django (<2.2) versions
* Start testing on Python 3.11 and Django 4.1/4.2
* Add setting to limit the amount of retries for deferred messages
(``MAILER_EMAIL_MAX_RETRIES``), defaults to ``None`` (unlimited)
(See Issue `#161 <https://github.com/pinax/django-mailer/issues/161>`_)

2.2 - 2022-03-11
----------------
Expand Down
6 changes: 6 additions & 0 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ value more suitable for you. This value, which defaults to ``None``, will be pas
`Django's bulk_create method <https://docs.djangoproject.com/en/stable/ref/models/querysets/#bulk-create>`_
as the ``batch_size`` parameter.

To limit the amount of times a deferred message is retried, you can set
``MAILER_EMAIL_MAX_RETRIES`` to an integer value. The default is ``None``, which means
that the message will be retried indefinitely. If you set this to a value of ``0``,
the message will not be retried at all, any number greater than ``0`` will be the
maximum number of retries (excluding the initial attempt).

Using the DontSendEntry table
=============================

Expand Down
2 changes: 1 addition & 1 deletion src/mailer/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def plain_text_body(self, instance):

class MessageAdmin(MessageAdminMixin, admin.ModelAdmin):

list_display = ["id", show_to, "subject", "when_added", "priority"]
list_display = ["id", show_to, "subject", "when_added", "priority", "retry_count"]
readonly_fields = ['plain_text_body']
date_hierarchy = "when_added"

Expand Down
18 changes: 18 additions & 0 deletions src/mailer/migrations/0006_message_retry_count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.1 on 2023-05-25 13:45

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mailer', '0005_id_bigautofield'),
]

operations = [
migrations.AddField(
model_name='message',
name='retry_count',
field=models.IntegerField(default=0),
),
]
8 changes: 7 additions & 1 deletion src/mailer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import six

from django.conf import settings

try:
from django.utils.encoding import python_2_unicode_compatible
except ImportError:
Expand Down Expand Up @@ -89,7 +91,10 @@ def deferred(self):
return self.filter(priority=PRIORITY_DEFERRED)

def retry_deferred(self, new_priority=PRIORITY_MEDIUM):
return self.deferred().update(priority=new_priority)
qs = self.deferred()
if getattr(settings, 'MAILER_EMAIL_MAX_RETRIES', None) is not None:
qs = qs.filter(retry_count__lt=settings.MAILER_EMAIL_MAX_RETRIES)
return qs.update(priority=new_priority, retry_count=models.F('retry_count') + 1)


base64_encode = base64.encodebytes if hasattr(base64, 'encodebytes') else base64.encodestring
Expand Down Expand Up @@ -133,6 +138,7 @@ class Message(BigAutoModel):
message_data = models.TextField()
when_added = models.DateTimeField(default=datetime_now)
priority = models.PositiveSmallIntegerField(choices=PRIORITIES, default=PRIORITY_MEDIUM)
retry_count = models.IntegerField(default=0)

objects = MessageManager()

Expand Down
48 changes: 48 additions & 0 deletions tests/test_mailer.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,54 @@ def test_retry_deferred(self):
engine.send_all()
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(Message.objects.count(), 0)
self.assertEqual(MessageLog.objects.count(), 2)

def test_max_retry_deferred(self):
with self.settings(MAILER_EMAIL_BACKEND="tests.FailingMailerEmailBackend", MAILER_EMAIL_MAX_RETRIES=2): # noqa
mailer.send_mail("Subject", "Body", "[email protected]", ["[email protected]"])
engine.send_all()
# First try fails, message is deferred
self.assertEqual(Message.objects.count(), 1)
self.assertEqual(MessageLog.objects.count(), 1)
self.assertEqual(Message.objects.deferred().count(), 1)
for n in range(4):
with self.subTest(tries=n):
# Re-que the deferred message
Message.objects.retry_deferred()
# Retry count is updated, unless the max is reached
self.assertEqual(
Message.objects.values_list('retry_count', flat=True).get(),
n + 1 if n < 2 else 2,
msg="Expected retry_count to be at most 2, got %d" % n
)
# Send all messages
engine.send_all()
# Message is retried (log entry is added), unless the max is reached
self.assertEqual(
MessageLog.objects.count(), 1 + (n + 1) if n < 2 else 3,
msg="Expected at most 3 attempts (log entries), got %d" % n
)
# Message remain deferred
self.assertEqual(Message.objects.deferred().count(), 1)

def test_max_retry_zero(self):
with self.settings(MAILER_EMAIL_BACKEND="tests.FailingMailerEmailBackend", MAILER_EMAIL_MAX_RETRIES=0): # noqa
mailer.send_mail("Subject", "Body", "[email protected]", ["[email protected]"])
engine.send_all()
# First try fails, message is deferred
self.assertEqual(Message.objects.count(), 1)
self.assertEqual(MessageLog.objects.count(), 1)
self.assertEqual(Message.objects.deferred().count(), 1)
# Re-que the deferred message
Message.objects.retry_deferred()
# Retry count remains at 0, the message is not retried
self.assertEqual(Message.objects.values_list('retry_count', flat=True).get(), 0)
# Send all messages
engine.send_all()
# Message is not retried (log entry is not added)
self.assertEqual(MessageLog.objects.count(), 1)
# Message remain deferred
self.assertEqual(Message.objects.deferred().count(), 1)

def test_purge_old_entries(self):

Expand Down

0 comments on commit 5f4944a

Please sign in to comment.