Hi Everyone,
Previously on assorted forums, the question has been asked "does Moodle queue outgoing emails for background processing", and the answer generally is "no". Couple of threads for reference:
https://moodle.org/mod/forum/discuss.php?d=367633
https://moodle.org/mod/forum/discuss.php?d=322747#p1484226
We have observed that various Moodle features - such as the messaging system between users, or notifications when a student submits an assignment, end up attempting to send one or more emails synchronously within the user's browser thread via email_to_user() in lib/moodlelib.php. If the email can be sent out quickly, it's not really a problem. However, if the target mail server happens to take some time to respond (5, 10, 30, 120 seconds or more!) the user's browser is just completely hung until the process completes. We have observed this a number of time and fielded complaints from teachers and students.
Best architectural practice in this situation is, of course, to always queue potentially long-running tasks for background execution and not try to complete them in the user's foreground thread. I would like to propose the following solution - we have implemented this locally in a test environment and it seems to work, but I appreciate any opinions on hidden "gotchas" that are not obvious.
1. We have created a new Moodle database table (e.g. email_queue) that replicates the essential parameters fed to email_to_user(), plus a couple of other things.
2. We have modified lib/moodlelib.php to REPLACE the existing email_to_user() function with a version that simply creates a new record in the email_queue table and returns immediately. The original email_to_user() function was renamed to email_to_user_sync(). Now, by definition, any Moodle process that tries to call the original email_to_user() function will end up queuing the message and returning immediately.
3. We have created a new local plugin that does nothing more than launch a scheduled task every minute. That scheduled task will read the new email_queue table for un-sent messages. Upon finding them, it will call email_to_user_sync() to actually send the messages. There is also some minor tweaking to try and maintain the mailer object persistently, in case that lets us preserve SMTP sessions and reduce connection churn. We'll also purge the email_queue table of sent messages every couple of days to keep it tidy.
Can anyone spot any major architectural flaws with this approach, such as some feature that demands an email actually be sent by the time email_to_user() returns?
Thanks for your time!