From 61d212298773d0c49048fde9f0a1af9f1a2d1df1 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Fri, 2 May 2014 16:44:31 +0200 Subject: [PATCH] [FIX] mail.mail: avoid prefetching attachment data in memory while processing mail queue, could fail due to memory limit Specifically when there are many emails in the queue, prefetching all their attachments at once when processing the first one could fail, so not even one mail would be sent. Added explicit logging when an email triggers a MemoryError, as well as logging when each email is successfully sent. bzr revid: odo@openerp.com-20140502144431-r3brgagl4gel4wmt --- addons/mail/mail_mail.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index f984f8f3482..0bd15374ee2 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -240,13 +240,17 @@ class mail_mail(osv.Model): :return: True """ ir_mail_server = self.pool.get('ir.mail_server') + ir_attachment = self.pool['ir.attachment'] for mail in self.browse(cr, SUPERUSER_ID, ids, context=context): try: - # handle attachments - attachments = [] - for attach in mail.attachment_ids: - attachments.append((attach.datas_fname, base64.b64decode(attach.datas))) + # load attachment binary data with a separate read(), as prefetching all + # `datas` (binary field) could bloat the browse cache, triggerring + # soft/hard mem limits with temporary data. + attachment_ids = [a.id for a in mail.attachment_ids] + attachments = ((a['datas_fname'], base64.b64decode(a['datas'])) + for a in ir_attachment.read(cr, uid, attachment_ids, + ['datas_fname', 'datas'])) # specific behavior to customize the send email for notified partners email_list = [] if mail.email_to: @@ -295,10 +299,14 @@ class mail_mail(osv.Model): # /!\ can't use mail.state here, as mail.refresh() will cause an error # see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1 if mail_sent: + _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id) self._postprocess_sent_message(cr, uid, mail, context=context) except MemoryError: # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job # instead of marking the mail as failed + _logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\ + 'Consider raising the --limit-memory-hard startup option', + mail.id, mail.message_id) raise except Exception as e: _logger.exception('failed sending mail.mail %s', mail.id)