OUTBOX: Understanding and Fixing Slow Exchange Web Services Code (Part 2)

  • Comments 1

In part 1 of this series I showed how to and the importance of doing the minimal number of requests, emphasizing the use of the IdOnly shape until we really needed more properties.  I also illustrated the effect of batching requests and finding a balance between the number of items requested in a batch and the total time to retrieve a set of items...

Even after applying these techniques in the calendar folder performance can still suffer.  Doing an AllProperties request for even a single item in the calendar can be exponentially slower than an AllProperties request for a single item in the inbox (or other "non-calendar" folder).  Since Exchange's genesis the calendar folder has always been a special case when writing client applications - there is a ton of business logic and background processing that goes on to make everything work just right.  Because of this, it might be tempting to just write off this performance hit to the "crazy 'ole calendar folder" but there is an answer and it lies in exactly what properties are requested on each item during an AllProperties request on a CalendarItem...

Understand the AllProperties BaseShape and Schema Properties

It turns out that this is a key concept in Exchange Web Services (read more here).  There is no SELECT * or GetProps(NULL)  in EWS, clients should know what properties they want to request and request explicitly.  What about the AllProperties shape?  Doesn't that return "all properties"?  In fact it does not (not that the previously mentioned methods return absolutely every property on an item either) and that may be confusing to some but it is the way it is.  What AllProperties returns is the full EWS schema for the given item type - in other words the property list returned will change based on the message class of the item requested.  This decreases the value of this base shape, reducing it to more or less a lines-of-code-saver.

Many of the EWS schema properties do not correlate directly to a single column on the item in the Exchange database, instead they are calculated on the client access server based on the values of multiple columns.  Some of these calculations, especially ones in the calendar folder, can be quite expensive.  In the CalendarItem schema there are four properties whose names indicate they may take a significant amount of effort to calculate: AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount.  Calculating the values of these properties can be assumed to require knowing about all the other items in the calendar - it might be time consuming to do that for every item in the calendar.  What would performance be like if the request was for AllProperties minus these four properties?..

The Solution

The first question is, "How do I request AllProperties minus AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount?"  The question, as stated, has no solution.  However the reverse is possible, request IdOnly plus all the other CalendarItem schema properties by specifying the schema properties explicitly using their FieldURI adding them to the AdditionalProperties collection on the ItemShape.  The code looks something like this...

GetItemType git = new GetItemType();
git.ItemIds = itemIds.ToArray();
git.ItemShape = new ItemResponseShapeType();
git.ItemShape.BaseShape = DefaultShapeNamesType.IdOnly;

List<BasePathToElementType> props = new List<BasePathToElementType>();

// Only add these properties if we are querying the calendar folder...
if (folderId.Id == DistinguishedFolderIdNameType.calendar)
{
    //props.Add(GetProp(UnindexedFieldURIType.calendarAdjacentMeetingCount));
    //props.Add(GetProp(UnindexedFieldURIType.calendarAdjacentMeetings));
    props.Add(GetProp(UnindexedFieldURIType.calendarAllowNewTimeProposal));
    props.Add(GetProp(UnindexedFieldURIType.calendarAppointmentReplyTime));
...
}

git.ItemShape.AdditionalProperties = props.ToArray();

It might seem tedious to request each property explicitly, however this approach can ensure the efficiency of EWS client applications by eliminating unused data from requests and responses.  I'd consider this a best practice when requesting item properties.  Let's look at my test results:

First, I created a simple application which calls GetItem with AllProperties against every appointment in a test account's calendar.  I tested with individual requests for each item and batching items in the requests.  Here are the results...

Items per request Total # of Requests Time (seconds) Test 1 Time (seconds)
Test 2
Average Time (seconds)
1 1432 851 895 873
25 58 762 777 769.5
50 29 762 759 760.5

...And then I changed the application to call GetItem with IdOnly and added every schema property except AdjacentMeetings, AdjacentMeetingCount, ConflictingMeetings, and ConflictingMeetingCount...

Items per request Total # of Requests Time (seconds) Test 1 Time (seconds)
Test 2
Average Time (seconds)
1 1432 104 105 104.5
25 58 15 15 15
50 29 13 13 13

...As you can see the results aren't even close, those few extra properties really cost you.  In the end, it is best to request the properties needed explicitly.  The expense of calculated properties and the reality of what AllProperties actually returns greatly devalues this BaseShape and for most purposes it should be avoided.

Updated 8/15/08 - I forgot to include the code above...

Updated 1/22/2009 – Fixed broken GetProps link.

Updated 9/25/2009 - Code should use BaseShape of IdOnly instead of AllProperties.

  • Gartner issues caveats over enterprise iPhone use Considerations for virtualizing an Exchange Server

Page 1 of 1 (1 items)