Forums

Problem with background email queue handler

My application uses a background queue handler to dispatch customized emails to mailing lists. Currently in production on GAE where it uses taskqueue.

On PA, I have a scheduled job to start the queue handler:

#/usr/bin/env python
# for the PythonAnywhere environment:
#   /home/ocsnedb/web2py/applications/init/private/ocsnedb_mail_queue.py
import logging
import socket
import sys
import subprocess

lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
try:
    lock_id = "ocsnedb_mail_queue"   # this should be unique. using your username as a prefix is a convention
    lock_socket.bind('\0' + lock_id)
    logging.debug("Acquired lock %r" % (lock_id,))
except socket.error:
    # socket already locked, task must already be running
    logging.info("Failed to acquire lock %r" % (lock_id,))
    print("Failed to acquire lock, task must already be running")
    sys.exit()

subprocess.call(["python", "/home/ocsnedb/web2py/web2py.py", "-S", "init", "-M", "-R", "applications/init/mail_queue.py"])

The actual queue processor is:

## in file /applications/init/mail_queue.py
#this is used in both test environment on PC, where it is started using mail_queue.cmd,
#and in PythonAnywhere environment, where it is run by /applications/init/ocsnedb_mail_queue.py
#which in turn is run by the PythonAnywhere scheduler.
import time

mail=auth.settings.mailer
while True:
    queue = db(db.emailqueue.id > 0).select(orderby=db.emailqueue.Created)
    for row in queue:
        while len(row.targets) > 0:
            e = row.targets[0]
            email = db.Emails[e]
            message = emailrender(row.subject, row.body, email, row.list, row.event)
            mail.send(to=email.Email, bcc=row.bcc, reply_to=row.reply_to, subject=row.subject, message=message)
            row.update_record(targets=row.targets[1:])
        db(db.emailqueue.id==row.id).delete()

    db.commit()
    time.sleep(5)

On the PC, this process running in a cmd shell works perfectly. On PA, the existing queue processes just fine when the job starts, but apparently never resumes after the time.sleep(5) call, so new messages simply sit in the queue. Right now the PA account is a free account, and I realize this won't work due to the time limit on schedule jobs, but if/when I move production to PA I'll upgrade to a paid account and worst case the queue handler would be inactive for an hour until the next schedule job run. But why doesn't the background loop work?

are there any hints in the schedule task logs? also maybe add some logging?

eg does it stop after the first loop? or after a couple loops?

or maybe it's getting killed instead of freezing?

or maybe your queue isnt being updated until it is rerun?

No. Both main and subprocess continue to run. When the subprocess is killed, the main process also exits. All that appears in the logging is "2017-01-20 21:08:04 -- Completed task, took 9177.00 seconds, return code was 0". If I run the queue processor in a bash shell, the results are the same. When the python shell is interrupted with ctrl-C, it notes it was at the time.sleep(5) when interrupted. The queue is updated, I checked it using the DB admin. And if it wakes up, it re-does the db query, as you can see. The same code running on the PC wakes up and dispatches newly added emails as I would expect. Strictly speaking, I don't know if it is the first time.sleep(5) call that hangs. I'll try to find out.

Well, that's a weird one. I put in logging to confirm it was going around the loop, and the whole thing was working fine. To be sure, I went back to the original, no logging, version (re-uploaded mail_queue.py from the PC) and restarted the web app and schedule task, and it now works fine. I swear it didn't yesterday!

Anyway, all looks well now. Thanks!