[2/2] parsemail: Make parsing an email atomic operation

Message ID 1445342923-6310-3-git-send-email-damien.lespiau@intel.com
State Rejected
Headers show

Commit Message

Damien Lespiau Oct. 20, 2015, 12:08 p.m.
With the series support in production, I realized that postfix did not
serialize the spawing of parsemail.sh. I couldn't find clear
documentation about that specific case: serializing mail delivery to a
mailbox is possible, not sure when postfix is piping the mail to a
another process.

Instead of digging further and look at postfix code, implementing the
serialization in parsemail.py itself seemed like a good idea: this will
work independently to the MTA used. Not only that, but it'd also work if
we do crazy things like allowing to submit patches through another entry
point.

Signed-off-by: Damien Lespiau <damien.lespiau@intel.com>
---
 patchwork/bin/parsemail.py | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

Patch

diff --git a/patchwork/bin/parsemail.py b/patchwork/bin/parsemail.py
index ba0b148..b41475d 100755
--- a/patchwork/bin/parsemail.py
+++ b/patchwork/bin/parsemail.py
@@ -25,12 +25,15 @@  import datetime
 import time
 import operator
 import codecs
+import weakref
 from email import message_from_file
 from email.header import Header, decode_header
 from email.parser import HeaderParser
 from email.utils import parsedate_tz, mktime_tz
 import logging
 
+from patchwork import lock as lockmod
+from patchwork.lock import release
 from patchwork.parser import parse_patch
 from patchwork.models import Patch, Project, Person, Comment, State, Series, \
         SeriesRevision, SeriesRevisionPatch, get_default_initial_patch_state, \
@@ -701,11 +704,26 @@  def setup_error_handler():
 
     return logger
 
+_lockref = None
+def lock():
+    global _lockref
+
+    l = _lockref and _lockref()
+    if l is not None and l.held:
+        l.lock()
+        return l
+
+    l = lockmod.lock("/tmp/patchwork.parsemail.lock", timeout=30)
+    _lockref = weakref.ref(l)
+    return l
+
 def main(args):
     django.setup()
     logger = setup_error_handler()
     mail = message_from_file(sys.stdin)
+    parse_lock = None
     try:
+        parse_lock = lock()
         return parse_mail(mail)
     except:
         if logger:
@@ -713,6 +731,8 @@  def main(args):
                 'mail': mail.as_string(),
             })
         raise
+    finally:
+        release(parse_lock)
 
 if __name__ == '__main__':
     sys.exit(main(sys.argv))