Tag: throttling


How to handle a "Throttling – Maximum sending rate exceeded" error

by Valentin Munteanu | on | in How-To | Permalink | Comments |  Share

Calls to Amazon SES are limited by the account’s maximum send rate. When you call Amazon SES faster than your maximum allocated send rate, Amazon SES will reject your over the limit requests with a “Throttling – Maximum sending rate exceeded” error. Depending on which Amazon SES interface you call, the error is passed back to you differently:

  1. With the Amazon SES HTTP Query interface the error will be received as a “Throttling” error response with a “Maximum sending rate exceeded.” message.
  2. With the SMTP interface, Amazon SES responds with “454 Throttling failure: Maximum sending rate exceeded” after the DATA command.
You can read more about Amazon SES sending limits and what happens when you reach these limits in the
Developer Guide.

A “Throttling – Maximum sending rate exceeded” error is retriable. This error is different than other errors returned by Amazon SES, such as sending from an email address that is not verified or sending to an email address that is blacklisted. Those errors indicate that the request will not be accepted in its current form. A request rejected with a “Throttling” error can be retried at a later time and is likely to succeed.

As an Amazon SES customer, be aware that any send email call can be rejected with a “Throttling” error. Here are a couple of things you can do to deal with this error condition:

Downscale – reduce the rate at which you call Amazon SES by scaling back your application. You can achieve this by introducing delays between calls or by reducing the number of threads/processes that call Amazon SES. Keep in mind that your sending capacity is not the only factor that drives the send rate; it is still possible to achieve a higher than desired send rate if, for example, the emails you send become smaller or if the network latency between your fleet and the Amazon SES endpoint is reduced.

Rate limiting – limit the number of concurrent calls to Amazon SES such that you will never exceed the maximum send rate. For example, in Java applications you can use the RateLimiter utility that comes with the Google Guava library to cap the rate at which your application calls Amazon SES. Take a look at the code snippet below to see how you can use this class.

AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(
      new BasicAWSCredentials(
        "ACCESS_KEY", //replace with your access key
        "SECRET_KEY"  //replace with your secret key
        ));
//initialize a RateLimiter that will limit your requests rate to 2 requests per second
RateLimiter rateLimiter = RateLimiter.create(2);
List destinations = Arrays.asList("EMAIL_ADDRESS_1", "EMAIL_ADDRESS_2", "EMAIL_ADDRESS_3"); //replace with your TO email addresses
for (String destination : destinations) {
  int maxRetries = 10;
  while(maxRetries-->0) {
    try {
      SendEmailRequest request  = new SendEmailRequest()
          .withSource("SOURCE_EMAIL_ADDRESS"); //replace with your FROM email address

      //wait for a permit to become available
      rateLimiter.acquire();

      //call Amazon SES to send the message
      SendEmailResult result = client.sendEmail(request);
      System.out.println("sent "+result.getMessageId());
      break;
    } catch (AmazonServiceException e) {
      //retries only throttling errors
      if ("Throttling".equals(e.getErrorCode() 
            && "Maximum sending rate exceeded.".equals(e.getMessage())) {
        System.out.println("Maximum send rate exceeded when sending email to "+destination+". "
          +(maxRetries>1?"Will retry.":"Will not retry.") );
      } else {
        System.out.println("Unable to send email to: "+destination+". " +e.toString());
        break;
      }
    } catch(Exception e) {
      System.out.println("Unable to send email to: "+destination+". " +e.toString());
      break;
    }
  }
}
Exponential backoff – back off when Amazon SES responds with a “Throttling – Maximum sending rate exceeded” error. The idea behind this kind of algorithm is to reduce the rate at which you are executing an operation by introducing delays, so when you are “backing off” you are waiting for a period of time before attempting to execute the operation again. A common backoff algorithm is ”
exponential backoff“. With exponential backoff, you exponentially increase the backoff duration on each consecutive failure. The following Java snippet shows a simple implementation of the exponential backoff algorithm and how to use it to call Amazon SES.

public static long getSleepDuration(int currentTry, long minSleepMillis, long maxSleepMillis) {
  currentTry = Math.max(0, currentTry);
  long currentSleepMillis = (long) (minSleepMillis*Math.pow(2, currentTry));
  return Math.min(currentSleepMillis, maxSleepMillis);
}
The code sample below shows how you can use the getSleepDuration function.

AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(
  new BasicAWSCredentials(
    "ACCESS_KEY", //replace with your access key
    "SECRET_KEY"  //replace with your secret key
    ));
List destinations = Arrays.asList("EMAIL_ADDRESS_1", "EMAIL_ADDRESS_2", "EMAIL_ADDRESS_3"); //replace with your TO email addresses
for (String destination : destinations) {
  SendEmailRequest request  = new SendEmailRequest()
      .withSource("SOURCE_EMAIL_ADDRESS"); //replace with your FROM email address
  int currentTry = 0;
  int maxRetries = 10;
  while(maxRetries-- > 0) {
    try {
      currentTry++;
      //call Amazon SES to send the message
      SendEmailResult result = client.sendEmail(request);
      System.out.println("sent "+result.getMessageId());
      break;
    } catch (AmazonServiceException e) {
      //retries only throttling errors
      if ("Throttling".equals(e.getErrorCode()) && "Maximum sending rate exceeded.".equals(e.getMessage())) {
        long backoffDuration = getSleepDuration(currentTry, 10, 5000);
        System.out.println("Maximum send rate exceeded when sending email to "+destination+". "
          +(maxRetries>1?"Will retry after backoff.":"Will not retry after backoff.") );
        try {
          Thread.sleep(backoffDuration);
        } catch (InterruptedException e1) {
          return;
        }
      } else {
        System.out.println("Unable to send email to: "+destination+". " +e.toString());
        break;
      }
    } catch(Exception e) {
      System.out.println("Unable to send email to: "+destination+". " +e.toString());
      break;
    }
  }
}
A variety of factors can affect your send rate, e.g. message size, network performance or Amazon SES availability. The advantage of the exponential backoff approach is that your application will self-tune and it will call Amazon SES at close to the maximum allowed rate. Also in practice you might consider over scaling your application to accommodate fluctuations in the send rate and let the application back off when it goes over the sending limit.

Finally, remember that you can apply for extended access if your business needs a larger sending quota.