How Can I Throttle Down CFMAIL's Speed (v3.0)?
Several years ago we introduced one of our favorite tools, the mail trickler, which was used to fight off some of the problems ColdFusion had (back when it was made by Allaire and called Cold Fusion) with sending large volumes of mail on a shared server.
In 2004 ColdFusion MX, now a product of Macromedia, had largely eliminated those problems, but we found the mail trickler was still useful for entirely different reasons, so we kept it around and described how we beefed it up, adding a very necessary failover capability.
We also did something else shortly afterwards that we never wrote up: Built in the ability for the trickler to sense bad addresses and gracefully recover from trying to send an email with one. See, an undocumented change occurred in the CFMail tag in ColdFusion MX 6.0. In previous versions if you fed CFMail a badly formatted email address it would simply try to send it anyway. The mail server would then reject the attempt and the bad email would sit in the ColdFusion Administrator Undeliverable directory. It caused quite a stir at the time as one mailing routine after another collapsed into a puddle of goo.
Thats ancient history now, and ColdFusion developers have cleaned up their sloppy habits and test for inputs before they let something into their databases that purports to be an email address (right?). But what if something happens to creep in? It sure would be nice if the routine would collect bad addresses it comes across and notify the system administrator of them so the offending entries can be cleaned up. Particularly if it does so without falling on its face.
So we'll do that here. Once again, as with the 2004 update, we will make no attempt to cover old ground. Use the previous two articles to bring yourself up to speed on the process as it has evolved. We will only cover what has changed here.
First, an overview of the general process that we are adding is in order. We are going to surround the mail process with a try/catch block. That try/catch block will, if it encounters an error, log the error into a struct and allow the routine to continue. At the end of the process, all errors will be collected and emailed to the system administrator. Note that to keep this example simple we will not try to do much with the error process other than to identify the bad email address. It is up to you to use your imagination and expand on this process (hint: start by putting errors into a database instead of a struct).
At last its time to roll up our sleeves. Read the comments below to understand what is going on from this point forward.
Initialize the simple error array that will contain the
bad email addresses. In a production environment it is
probably smarter to use a database and collect more
information, such as #cfcatch.message#, since far more
can go wrong with a cfmail statement than a bad address.
set the email address and server of the poor sap who gets
the email listing all of the errors. A way to make this
more robust is to keep the data in a database and simply
notify the designated sap that errors exist to review.
There are MANY useful things you can do with this error
Run Rate is the number of seconds between refresh. To
optimize this make this setting about 2 seconds longer
than the ColdFusion Server's mail spool fetch rate.
Otherwise mail will pile up in the spooler and partially
defeat the purpose of trickling out the mail
Query Run is the number of query rows (email addresses)
that will be processed on each execution of this template
pull the ID field so we can get a record count.
myMessages.EmailAddr IS NOT NULL
Do some stuff to prevent page caching, which was found to
be a problem on some client browsers
value="Sun, 06 Nov 1994 08:49:37 GMT">
value="no-cache, no-store, must-revalidate">
Are we there yet?
<cfif MailList.RecordCount lt 1>
yes. Notify the email administrator if there are any errors
<cfif not ArrayIsEmpty(variables.errorArray)>
The array is not empty so there must be errors. Send the
error email. Note that if errors are in the mail server
itself this will never get sent, which is another reason
to make this routine more robust.
<cfdump var="#variables.errArray#" label="Error List">
finish the job by showing the "All Done" screen.
<b>Close this browser window!</b>
<p><b>Do NOT press your BACK button</b> to leave.</p>
display the "in-progress" page
This next line re-runs the template automatically at the run rate.
Note the UUID inserted into the url. This is another part of
ensuring the browser doesn't cache this page.
<head><title>Low Volume Mass Mailer with Failover</title></head>
Send the actual mail.
if the cfmail statement throws an error it gets dealt with
here. First we append the ID and email address to the error
now that the mail has either been sent or flagged as bad, delete
its database record
DELETE FROM myMessages
give the user a visual cue as to where the operation is at the moment.
You can get quite fancy here with pretty html and time-to-completion
calculations based on your record count, refresh rate etc.
<p><b>Total left to send: #variables.LeftToGo#</b>.</p>
<p>Each run is #variables.RunRate# seconds apart.</p>
<p>Leave this system alone and wait for this job to complete.</p>
<p>When it finishes, you will be notified.</p>