Commit 08eb44e6 by jsa

generate_and_send_digests should retry on comments service error

parent 22e48bd7
...@@ -12,7 +12,7 @@ from django.core.mail import EmailMultiAlternatives ...@@ -12,7 +12,7 @@ from django.core.mail import EmailMultiAlternatives
from notifier.connection_wrapper import get_connection from notifier.connection_wrapper import get_connection
from notifier.digest import render_digest from notifier.digest import render_digest
from notifier.pull import generate_digest_content from notifier.pull import generate_digest_content, CommentsServiceException
from notifier.user import get_digest_subscribers, UserServiceException from notifier.user import get_digest_subscribers, UserServiceException
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -31,39 +31,33 @@ def generate_and_send_digests(users, from_dt, to_dt): ...@@ -31,39 +31,33 @@ def generate_and_send_digests(users, from_dt, to_dt):
of the time window for which to generate a digest. of the time window for which to generate a digest.
""" """
users_by_id = dict((str(u['id']), u) for u in users) users_by_id = dict((str(u['id']), u) for u in users)
with closing(get_connection()) as cx: msgs = []
msgs = [] try:
for user_id, digest in generate_digest_content(users_by_id, from_dt, to_dt): with closing(get_connection()) as cx:
user = users_by_id[user_id] for user_id, digest in generate_digest_content(users_by_id, from_dt, to_dt):
# format the digest user = users_by_id[user_id]
text, html = render_digest( # format the digest
user, digest, settings.FORUM_DIGEST_EMAIL_TITLE, settings.FORUM_DIGEST_EMAIL_DESCRIPTION) text, html = render_digest(
# send the message through our mailer user, digest, settings.FORUM_DIGEST_EMAIL_TITLE, settings.FORUM_DIGEST_EMAIL_DESCRIPTION)
msg = EmailMultiAlternatives( # send the message through our mailer
settings.FORUM_DIGEST_EMAIL_SUBJECT, msg = EmailMultiAlternatives(
text, settings.FORUM_DIGEST_EMAIL_SUBJECT,
settings.FORUM_DIGEST_EMAIL_SENDER, text,
[user['email']] settings.FORUM_DIGEST_EMAIL_SENDER,
) [user['email']]
msg.attach_alternative(html, "text/html") )
msgs.append(msg) msg.attach_alternative(html, "text/html")
if not msgs: msgs.append(msg)
return if not msgs:
try: return
cx.send_messages(msgs) cx.send_messages(msgs)
except SESMaxSendingRateExceededError as e: except (CommentsServiceException, SESMaxSendingRateExceededError) as e:
# we've tripped the per-second send rate limit. we generally # only retry if no messages were successfully sent yet.
# rely on the django_ses auto throttle to prevent this, if not any((getattr(msg, 'extra_headers', {}).get('status') == 200 for msg in msgs)):
# but in case we creep over, we can re-queue and re-try this task raise generate_and_send_digests.retry(exc=e)
# - if and only if none of the messages in our batch were else:
# sent yet. # raise right away, since we don't support partial retry
# this implementation is also non-ideal in that the data will be raise
# fetched from the comments service again in the event of a retry.
if not any((getattr(msg, 'extra_headers', {}).get('status') == 200 for msg in msgs)):
raise generate_and_send_digests.retry(exc=e)
else:
# raise right away, since we don't support partial retry
raise
def _time_slice(minutes, now=None): def _time_slice(minutes, now=None):
""" """
......
...@@ -13,7 +13,7 @@ from django.test.utils import override_settings ...@@ -13,7 +13,7 @@ from django.test.utils import override_settings
from mock import patch, Mock from mock import patch, Mock
from notifier.tasks import generate_and_send_digests, do_forums_digests from notifier.tasks import generate_and_send_digests, do_forums_digests
from notifier.pull import process_cs_response from notifier.pull import process_cs_response, CommentsServiceException
from notifier.user import UserServiceException, DIGEST_NOTIFICATION_PREFERENCE_KEY from notifier.user import UserServiceException, DIGEST_NOTIFICATION_PREFERENCE_KEY
from .utils import make_user_info from .utils import make_user_info
...@@ -120,7 +120,7 @@ class TasksTestCase(TestCase): ...@@ -120,7 +120,7 @@ class TasksTestCase(TestCase):
for message in djmail.outbox: for message in djmail.outbox:
self.assertEqual(message.to, ['rewritten-address@domain.org']) self.assertEqual(message.to, ['rewritten-address@domain.org'])
def test_generate_and_send_digests_retry_limit(self): def test_generate_and_send_digests_retry_ses(self):
""" """
""" """
data = json.load( data = json.load(
...@@ -149,6 +149,25 @@ class TasksTestCase(TestCase): ...@@ -149,6 +149,25 @@ class TasksTestCase(TestCase):
# should have raised # should have raised
self.fail('task did not retry twice before giving up') self.fail('task did not retry twice before giving up')
def test_generate_and_send_digests_retry_cs(self):
"""
"""
with patch(
'notifier.tasks.generate_digest_content',
side_effect=CommentsServiceException('timed out')
) as mock_cs_call:
# setting this here because override_settings doesn't seem to
# work on celery task configuration decorators
expected_num_tries = 1 + settings.FORUM_DIGEST_TASK_MAX_RETRIES
try:
generate_and_send_digests.delay(
[usern(n) for n in xrange(2, 11)], datetime.datetime.now(), datetime.datetime.now())
except CommentsServiceException as e:
self.assertEqual(mock_cs_call.call_count, expected_num_tries)
else:
# should have raised
self.fail('task did not retry twice before giving up')
@override_settings(FORUM_DIGEST_TASK_BATCH_SIZE=10) @override_settings(FORUM_DIGEST_TASK_BATCH_SIZE=10)
def test_do_forums_digests(self): def test_do_forums_digests(self):
# patch _time_slice # patch _time_slice
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment