If you're sending email via Exchange Web Services, cache the service URL for a while!
In my recent series of posts about sending emails through Office 365 from an Azure Function, triggered by a blob being uploaded to Azure storage, I noted that:
because of the amount of time Office 365 autodiscover takes, it's not a particularly quick process
There's a few references that a quick search returned that back this up:
- EWS Best Practices: Use Autodiscover… - "It’s good practice to refresh the cached URL for a given mailbox every 24 hours or so"
- How to: Set the EWS service URL by using the EWS Managed API - "you might want to cache the URL value you get from Autodiscover and manually set the EWS service URL with this cached value"
- Stack Overflow: How to cache Exchange web service API autodiscoverurl? - "please make AutoDiscover request at least once every 24 hours" (in an answer from a Program Manager @ Microsoft for Mail, calendar, & contact APIs for Office 365, Exchange, Outlook.com, Outlook Outlook/Exchange connectivity)
By instrumenting the console app I put together in my post Creating an Azure function that triggers when a blob is uploaded - Sending an email via Office 365 (very quickly and lazily!) to find out how long each email takes to send when run in a loop over 10 iterations:
var durations = new List(); for (int i = 0; i < 10; i++) { Stopwatch watch = new Stopwatch(); watch.Start(); SendEmail(credentials, username, "<RECIPIENT_EMAIL_ADDRESS>", "Subject Line", $"<h1>Content of email generated at {DateTime.Now}</h1>"); watch.Stop(); durations.Add(watch.ElapsedMilliseconds); } var average = durations.Average();
I got an average duration of 12,246.2 milliseconds, or just over 12 seconds per email. By tweaking the code inside the SendEmail method so that it caches the URL, by adding a property to store it in and tweaking the code that calls the AutodiscoverUrl method:
if (EWSUrl == null) { service.AutodiscoverUrl(fromAddress, RedirectionUrlValidationCallback); EWSUrl = service.Url; } else { service.Url = EWSUrl; }
All of a sudden, the average time taken drops down to 1,578.9 milliseconds. Being very lazy (i.e. not going back and checking the values), I can extrapolate that the ten requests took a total of 15,789 milliseconds, so deducting the average from the previous test (time taken when calling Autodiscover) of 12,246 milliseconds gives a remainder of 3,543 milliseconds.
Finally (and stretching the numbers even further into potential inaccuracy) dividing by 9 gives a result of 393 milliseconds. So, by caching the service url, the average time taken to send an email will reduce from c. 12 seconds to c. 1/3 of a second (when the time taken to autodiscover is stripped out from the latter). That's a pretty significant win.
Next up: How do I cache the service URL in an Azure function?