Throttling Policies and the EWSFindCountLimit

One of my favorite Exchange Web Services (EWS) methods is FindItem, primarily because it was the first Web method I wrote when I joined the EWS team long, long ago. Since then, it has undergone lots of optimizations, feature changes, and so on, to make it what it is today. One of the Exchange 2010 changes to FindItem (and FindFolder) is protective in nature – Exchange doesn’t want to drown a Client Access server just because someone decides to pull down the entire contents of their mailbox. This protective change is governed by the EWSFindCountLimit throttling policy parameter.

When a FindItem request comes in and the caller is authenticated, EWS obtains the caller’s budget. Every item (or folder in the case of FindFolder) that EWS processes for the current FindItem request is counted against the budget’s EWSFindCountLimit. As soon as the response is sent back to the caller, the find count charge for the current call is released. Why do we do this? Because items and folders that are waiting to be returned in a response take up memory on the Client Access server.

You will soon discover that budgets have a larger scope than a single request. If a caller makes two concurrent EWS FindItem requests that return 10 items each, the EWSFindCountLimit charge against that caller’s *single* budget is…20. When the first request returns, it will drop to 10 (because those items can now be garbage collected) and then when the second request returns it will drop to 0. It should be relatively obvious that we are trying to limit the caller’s memory footprint on the Client Access server.

What happens, though, when a caller reaches the limit? Well, that depends on whether the caller is being a “good EWS citizen”. And what makes an EWS citizen “good”? Well, in the context of EWSFindCountLimit, a good EWS citizen will do all their FindItem calls by using paging rather than by requesting the entire result set each time. Assume, for a moment, that the EWSFindCountLimit value is 1000 and a bad EWS citizen makes a FindItem call without paging. Let’s further say that the query will return 1001 items. EWS certainly can’t truncate the results, as the caller is expecting, to receive ALL the data back. And EWS can’t turn a non-paged FindItem call into a paged one, as the caller would not expect to check for such a response. So what does EWS do? If the call has a RequestServerVersion that is earlier than Exchange2010, you will receive a failure response with an error code of ErrorServerBusy. If your RequestServerVersion is Exchange2010, you will get back the following response:

<s:Body xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
  <m:FindItemResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
    <m:ResponseMessages>
      <m:FindItemResponseMessage ResponseClass="Error">
        <m:MessageText>You have exceeded the maximum number of objects that can be returned for the find operation. Use paging to reduce the result size and try your request again.</m:MessageText>
        <m:ResponseCode>ErrorExceededFindCountLimit</m:ResponseCode>
        <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
        <m:MessageXml>
          <t:Value Name="PolicyLimit">1000</t:Value>
        </m:MessageXml>
      </m:FindItemResponseMessage>
    </m:ResponseMessages>
  </m:FindItemResponse>
</s:Body>

Very interesting. For kicks, let’s reduce the EWSFindCountLimit and cycle IIS to pick up the changes (ah yes, the joys of having your own test box to beat upon). Open the Exchange 2010 Management Console as an Exchange Admin and run the following (New-ThrottlingPolicy is only available to administrators, for obvious reasons):

New-ThrottlingPolicy –Name “FooPolicy” –EWSFindCountLimit 1
Set-Mailbox JohnDoe –ThrottlingPolicy FooPolicy
iisreset

Now, on my test box, I created 6 email messages in John Doe’s Drafts folder.  Let’s call FindItem again, but use indexed paging.  Here is my request:

<soap:Header>
    <t:RequestServerVersion Version="Exchange2010"/>
</soap:Header>
<soap:Body>
<FindItem xmlns="https://schemas.microsoft.com/exchange/services/2006/messages"
           xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" Traversal="Shallow">
    <ItemShape>
        <t:BaseShape>IdOnly</t:BaseShape>
        <t:AdditionalProperties>
            <t:FieldURI FieldURI="item:Subject"/>
        </t:AdditionalProperties>
    </ItemShape>
    <IndexedPageItemView BasePoint="Beginning" Offset="0" MaxEntriesReturned="10000"/>
    <ParentFolderIds>
           <t:DistinguishedFolderId Id="drafts"/>              
        </ParentFolderIds>
</FindItem>

And the response…

<s:Body xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
  <m:FindItemResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
    <m:ResponseMessages>
      <m:FindItemResponseMessage ResponseClass="Success">
        <m:ResponseCode>NoError</m:ResponseCode>
        <m:RootFolder IndexedPagingOffset="1" TotalItemsInView="6" IncludesLastItemInRange="false">
          <t:Items>
            <t:Message>
              <t:ItemId Id="AAMkA…"/>
              <t:Subject>Message5</t:Subject>
            </t:Message>
          </t:Items>
        </m:RootFolder>
      </m:FindItemResponseMessage>
    </m:ResponseMessages>
  </m:FindItemResponse>
</s:Body>

Whoa – wait a second! There were 6 messages to return, I asked for 10000, but it only returned 1 of them to me! What happened? Well, given that the caller’s EWSFindCountLimit is 1, EWS will NOT return more than 1 record to the user (at a time). However, because the caller was a good Exchange citizen and made a paged request, EWS can “fix up” the result because the user will (read: should) understand that EWS might not return ALL the items. The caller will instead look at the IncludesLastItemInRange attribute and, if it is false, make another FindItem request with the new Offset and continue until IncludesLastItemInRange returns true. If this behavior seems odd to you, consider that this is exactly how the various network streams in the Microsoft .NET framework behave – you continue reading from the stream until the stream reports that it has no more data to read.

So, the moral of the story is: Always use a paging mechanism when calling FindItem or FindFolder. Wisely, the Exchange Web Services Managed API forces you to do so by requiring an ItemView to be passed into FindItem:

service.FindItems(new FolderId(WellKnownFolderName.Drafts), new ItemView(10000));

Now that you know, there is no excuse for failing to use a page view :)

David Sterling
Exchange Web Services
Inside Microsoft Exchange Server 2007 Web Services