0

I have a need to send multiple emails using Microsoft Graph from Windows Service.
I'm using Microsoft.Graph NuGet package.
I'm creating GraphServiceClient and sending mail like so:

IGraphServiceClient graphClient = new GraphServiceClient("https://graph.microsoft.com/v1.0", authenticationProvider);
var email = new Message
{
    Body = new ItemBody
    {
        Content = "Works fine!",
        ContentType = BodyType.Html,
    },
    Subject = "Test",
    ToRecipients = recipientList
};

await graphClient.Users["test@example.onmicrosoft.com"].SendMail(email, true).Request().WithMaxRetry(5).PostAsync();

When I send emails one-by-one:

for (var j = 0; j < 20; j++)
{
    await graphClient.Users["test@example.onmicrosoft.com"].SendMail(email, true).Request().WithMaxRetry(5).PostAsync();
    progressBar1.PerformStep();
}

everything works fine, but when I use Parallel.For:

var res = Parallel.For(0, 20, async (i, state) =>
{
    var email = new Message
    {
        Body = new ItemBody
        {
            Content = "Works fine!",
            ContentType = BodyType.Html,
        },
        Subject = "Test",
        ToRecipients = recipientList
    };

    await graphClient.Users["test@example.onmicrosoft.com"].SendMail(email, true).Request().WithMaxRetry(5).PostAsync();
});

i get errors, because I get Too Many Requests (429) and then Unsupported Media Type (415).

This is the error code:

Code: RequestBodyRead Message: A missing or empty content type header was found when trying to read a message. The content type header is required.

This is how it looks in Fiddler:

enter image description here

My question is: can I use and how I should use Graph with Parallel.For to avoid this kind of errors. I'm already setting WithMaxRetry(5) for each request.

I'm aware of usage limits, but I thought WithMaxRetry(5) will help.

Misiu
  • 4,738
  • 21
  • 94
  • 198
  • All calls to Microsoft Graph should be developed with an expectation that they could be throttled. I'd advise you consider incorporating a [retry library](https://learn.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific) in your code. – Kalyan Krishna Apr 14 '19 at 05:00
  • @KalyanKrishna I thought that if I add `WithMaxRetry` (https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/c9a30939b3e79a6e070598db2fc6ea1982304b7b/src/Microsoft.Graph.Core/Extensions/BaseRequestExtensions.cs#L96) MS Graph SDK will handle retry. Take a look at this PR: https://github.com/microsoftgraph/msgraph-sdk-dotnet/pull/301 – Misiu Apr 16 '19 at 06:29

2 Answers2

1

It doesn’t have to do with the threads. It has to do with throtteling a.k.a. you can only do x amount of requests in a certain time period.

The dotnet graph api client doesn’t support batching (sadly enough). But batching those requests yourself is easily implemented. Then you can send 15 mails with one request.

Stephan
  • 2,356
  • 16
  • 38
  • This is correct, but the throttling limit for Exchange is `10` requests per mailbox. So you would need to batch in blocks of 10, not 15. Even then, your code needs to account for potential throttling. You can be throttled for any number of reasons, including the overall tenant load. – Marc LaFleur Apr 14 '19 at 22:27
  • @Stephan, @Marc I'm aware of throttling, but that's what `WithMaxRetry` is for (https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/c9a30939b3e79a6e070598db2fc6ea1982304b7b/src/Microsoft.Graph.Core/Extensions/BaseRequestExtensions.cs#L96). I assumed that this method will handle retry in case of throttling. The weird this is that I got `429` but then started getting `415`. – Misiu Apr 16 '19 at 06:28
1

The reason why you saw this is because of the missing content type header. It wasn't being cloned when we cloned the httprequestmessage. This has been fixed and will be in the next release of the client. With regards to parallel threads, we plan on implementing a resource based shared retry queue so that we use a single retry scheme across multiple requests that target the same resource (and the same throttling policy).

Michael Mainer
  • 3,387
  • 1
  • 13
  • 32
  • Looking forward to next release :) Shared retry queue would be an awesome feature, especially when we want to send requests in parallel. Ideally, an example showing best practices should be added to show how to do parallel requests and how to batch them. – Misiu Apr 19 '19 at 07:23
  • @Misiu, be the first to download it: https://www.nuget.org/packages/Microsoft.Graph.Core/1.15.0-preview.1 – Michael Mainer Apr 19 '19 at 14:37