Web Development & Execution
David Addison
by David Addison
share this
?fl
« Back to the Blog

SMTP Fallback Methods

03/22/2017
SMTP Fallback Methods

Does "I didn't receive a confirmation email, did my order go through" ... or "I havn’t received a tracking number, has my order shipped" sound familiar?  Missing transactional email is the #1 source of a customer service calls for most online businesses. Not good!

Did you know that transactional email like order confirmations are your most valuable messages. The open rate on transactional email is typically near 100% and upselling in transactional emails has six times the ROI as bulk email. Transactional email is intricate, and for most development teams, it’s a dull afterthought rather than a first-class citizen. That’s too bad.

Dirigo runs a pair of PowerMTA servers configured as a failover cluster as part of our Konvey messaging & marketing automation platform and for SMTP relay traffic from our hosts. In layman's terms we mean 'sending e-mail from the webserver'. Uptime over the past 18 months at Dirigo has been at or above 99.99%. As we prepare for the roll-out of Dirigo Ecommerce Services we recognize the importance of delivering every single outbound email. Our Clients and their End Users rely on:

  • Order confirmations, receipts, invoices, statements, and renewals
  • Welcome messages
  • Account verifications, updates and notices
  • Password delivery
  • E-Tickets
  • Shipping notifications and tracking numbers
  • Email alerts and updates
  • Reminders and cancellations
  • Product updates and further instructions
  • Contact and web form data

This is important stuff. You’d think that planning for STMP failure would be standard in most applications. I’m here to tell you that it’s not as commonplace as you might think. The vast majority of websites just assume that the SMTP connection is always accessible. Top tier or 'enterprise' websites and apps typically layer-in a fallback plan. In an effort to drive our up-time to triple nines (99.999) we have deployed the methods described below.

Dirigo has installed the Microsoft SMTP Service on all Web and App servers, in all environments, except for client-specific environments. Client environments should use the methods described here if time and budget permits. We now have the choice of delivering to the local machine via SMTP, delivering to PowerMTA via SMTP, delivering to a local pickup folder, or delivering to the PowerMTA pickup folder (via UNC share). To ensure that all email is delivered we recommend attempting all four methods.

ASP.NET C# Code Example

1:  <?xml version="1.0" encoding="utf-8" ?>  
2:  <configuration>  
3:   <startup>  
4:    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />  
5:   </startup>  
6:   <appSettings>  
7:    <add key=""/>  
8:    <add key="smtpFromAddress" value="postmaster@domain.com" />  
9:    <add key="smtpTimeoutSeconds" value="20" />  
10:    <add key="smtpFailoverHost" value="***Web Address Value***" />  
11:    <add key="smtpFailoverPickup" value="***UNC Path to Pickup***" />  
12:   </appSettings>  
13:   <system.net>  
14:    <mailSettings>  
15:     <smtp>  
16:      <!-- Use 127.0.0.1 instead of localhost, because the latter might resolve to the IPv6 address ::1, and the local mail server won't be bound to IPv6. -->  
17:      <network host="127.0.0.1" />  
18:      <specifiedPickupDirectory pickupDirectoryLocation="C:\inetpub\mailroot\Pickup" />  
19:     </smtp>  
20:    </mailSettings>  
21:   </system.net>  
22:  </configuration>  
23:  using System;  
24:  using System.Configuration;  
25:  using System.Net.Mail;  
26:  namespace ConsoleApp2  
27:  {  
28:    class Program  
29:    {  
30:      static void Main(string[] args)  
31:      {  
32:        // Here's a sample HTML-formatted email. I'm not sure what disposing of the MailMessage object accomplishes, but hey.  
33:        using (var msg = new MailMessage(ConfigurationManager.AppSettings["smtpFromAddress"], "name@sampleaddress.com"))  
34:        {  
35:          msg.Subject = "Test";  
36:          msg.Body = "<!doctype html><html><body>Hello, world!</body></html>";  
37:          msg.IsBodyHtml = true;  
38:          // This Dirigo-proprietary SMTP header is very important so that the message doesn't get stuck in the PowerMTA queue along with millions of marketing emails.  
39:          msg.Headers.Add("X-High-Priority", "true");  
40:          // There should really be an async version of the SendEmail method, since sending an email (especially in the case of failure) ties up the active thread for a while.  
41:          SendEmail(msg);  
42:        }  
43:      }  
44:      // There's enough code in here (including four levels of try-catch) that this really needs to be a separate method.  
45:      static void SendEmail(MailMessage msg)  
46:      {  
47:        // Don't skip the using block, or the TCP connection(s) will stay open for quite a while.  
48:        using (var client = new SmtpClient())  
49:        {  
50:          // Allow override of the default timeout: 100 seconds is too long to wait since we have redundancy.  
51:          client.Timeout = int.Parse(ConfigurationManager.AppSettings["smtpTimeoutSeconds"]) * 1000;  
52:          try  
53:          {  
54:            try  
55:            {  
56:              // For the first attempt, target the server that's specified in the <system.net> section of the config file.  
57:              client.Send(msg);  
58:            }  
59:            catch (SmtpException)  
60:            {  
61:              client.DeliveryMethod = SmtpDeliveryMethod.PickupDirectoryFromIis;  
62:              try  
63:              {  
64:                // For the second attempt, target the pickup directory that's specified in <system.net>.  
65:                // In the Development environment (where presumably no SMTP server is running), mail will be saved to that directory   
66:                // if it exists and has appropriate permissions; so create that directory manually if that's the behavior you prefer.  
67:                client.Send(msg);  
68:              }  
69:              catch (SmtpException)  
70:              {  
71:                client.DeliveryMethod = SmtpDeliveryMethod.Network;  
72:                client.Host = ConfigurationManager.AppSettings["smtpFailoverHost"];  
73:                try  
74:                {  
75:                  // For the third attempt, target the SMTP host that's specified in <appSettings>.  
76:                  client.Send(msg);  
77:                }  
78:                catch (SmtpException)  
79:                {  
80:                  client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;  
81:                  client.PickupDirectoryLocation = ConfigurationManager.AppSettings["smtpFailoverPickup"];  
82:                  try  
83:                  {  
84:                    // For the third attempt, target the pickup directory that's specified in <appSettings>.  
85:                    client.Send(msg);  
86:                  }  
87:                  catch  
88:                  {  
89:                    throw;  
90:                  }  
91:                }  
92:              }  
93:            }  
94:          }  
95:          catch (Exception ex)  
96:          {  
97:            // TO DO: Provide some sort of logging.  
98:            // Optionally, re-throw the exception.  
99:            throw;  
100:          }  
101:        }  
102:      }  
103:    }  
104:  }  

 

Thanks!

Thank you for contacting us!

We'll be in touch!

Back Home ×