How do deal with SMTP timeouts

5.6k views Asked by At

I am sending bulk emails to an corporation exchange server, using a client application written in C#.

It can happen, and it did, that the client application timeout (not the server). Since there is no way to know if the server completed the request, how to handle retrys for this case?

There are no ids involved that could be use to avoid duplicates. Setting long timeouts or even infinite timeouts is not a good policy.

I am using exponential backoff algorithm for retry. In this case it should only send only one duplicate because it will wait longer next time.

I think there is no bullet prof solution. Anyway, since it is first project of the kind, i need to check to see if someone has a solution that i miss looked.

Update: The exchange is doing the relay. I am using an SmtpClient to send e-mails. The problem is that the server can send the 250 Ok message, but the receiver never gets it, and then try again. That is the only problem i am trying to solve in this post.

In Rest services the recommended approach is to use an concurrency error. If the client post something and get "409 - conflict" status that means the message was already stored on the server. But for that to happen there as to be a key for the message that is created by the client and is part of the message. SMTP doesn't seem to have a mechanism that can prevent that.

2

There are 2 answers

8
BastianW On

Normally a SMTP server will give you an information if he accepted the email or not when using standard SMTP. Here is an example via telnet:

enter image description here enter image description here

So your application only need to track the response here and if a timesout happen you do not need to proceed with all emails and need to pick up where it stopped. By the way, your application should check every time the response as it could be that the sender or receiver isn´t accepted...

This is mentioned in RFC 5321 (you can scroll in that document):

When the receiver-SMTP accepts a piece of mail (by sending a "250 OK" message in response to DATA), it is accepting responsibility for delivering or relaying the message. It must take this responsibility seriously. It MUST NOT lose the message for frivolous reasons, such as because the host later crashes or because of a predictable resource shortage. [...] When the end of text is successfully received and stored, the SMTP-receiver sends a "250 OK" reply.

As you are sending emails I think section 4.5.4.1. might be important if you aren´t using some kind of framework which handle the outgoing emails RFC conform.

Example: Your client is generating an email but during the submit from the body (last part from the email transfer) the connection dropped. Then the server isn´t allowed to proceed the email. Technically he might got all infos, but the RFC didn´t allowed him to send that out as the client had a timeout and didn´t finished the whole process.

Update: The best way would be to use the C# SmtpClient method and then check the smtpStatusCode OK (= "The email was successfully sent to the SMTP service.") which you can see as well in the telnet examples. The methode isn´t doing anything else here (technically). The method is also compliance with RFC 5321 so you do not need to re-invent the smtp send wheel. If you do not get the OK something in the meantime might have had happen and you need to check the result and then perform an re-send (or need to give up if the error message is saying that the email address isn´t valid or something like that to avoid a infinite loop).

0
BastianW On

Another possible option might be to involve the Microsoft Exchange Webservices. You can use them as well to send out emails. Example from here:

// Create an email message and identify the Exchange service.
EmailMessage message = new EmailMessage(service);

// Add properties to the email message.
message.Subject = "Interesting";
message.Body = "The merger is finalized.";
message.ToRecipients.Add("[email protected]");

// Send the email message and save a copy.
message.SendAndSaveCopy();

They give you as well a response back:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="14" 
               MinorVersion="0" 
               MajorBuildNumber="639" 
               MinorBuildNumber="20" 
               Version="Exchange2010" 
               xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" 
               xmlns="http://schemas.microsoft.com/exchange/services/2006/types" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xm=""lns:xsd="http://www.w3.org/2001/XMLSchema" />
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:CreateItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" 
               xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:CreateItemResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:Items />
        </m:CreateItemResponseMessage>
      </m:ResponseMessages>
    </m:CreateItemResponse>
  </s:Body>
</s:Envelope>