Wednesday, July 26, 2017

Iterative approach for making repeatable Sharepoint Online CSOM and Graph API calls more robust

In one of our project we used Azure web job which ran by scheduler and performed some periodic actions. They included Sharepoint Online CSOM calls for updating list item in Sharepoint list and Graph API calls for getting members of Azure AD group. From time to time these calls failed randomly, e.g. attempt to update list item via CSOM failed with “Version conflict” exception and Graph API calls failed with service exception without any clear error message. Both APIs are continuously evolved and calls (especially repeated calls which run frequently) may still fail with such inconsistent errors.

As workaround to make process more robust the following approach was used: make API calls in several attempts. If 1st attempt failed, wait some time and repeat call again – max number of attempts is also specified. Here is the code which demonstrates the idea:

   1: private static void UpdateListItemSafely(ClientContext ctx, List list, ListItem item,
   2:     List<User> users, out bool isError)
   3: {
   4:     isError = false;
   5:     UpdateListItem(ctx, list, item, users, out isError);
   6:     if (isError)
   7:     {
   8:         int numOfAttempts = 4;
   9:         for (int i = 0; i < numOfAttempts; i++)
  10:         {
  11:             Thread.Sleep(1000);
  12:             UpdateListItem(ctx, list, item, users, out isError);
  13:             if (!isError)
  14:             {
  15:                 break;
  16:             }
  17:         }
  18:     }
  19: }
  20:  
  21: private static void UpdateListItem(ClientContext ctx, List list, ListItem item,
  22:     List<User> users, out bool isError)
  23: {
  24:     isError = false;
  25:     try
  26:     {
  27:         var listItem = list.GetItemById(item.Id);
  28:         ctx.Load(listItem);
  29:         ctx.ExecuteQueryRetry();
  30:  
  31:         listItem["Users"] = users != null ?
  32:             users.Select(u => new FieldUserValue { LookupId = u.Id }).ToArray() :
  33:             new FieldUserValue[0];
  34:         listItem.Update();
  35:         ctx.ExecuteQueryRetry();
  36:     }
  37:     catch (Exception x)
  38:     {
  39:         isError = true;
  40:     }
  41: }

Here we make 5 attempts to update list item – set users to User field type with multiple values (lines 21-41) with 1 sec delay between attempts (lines 5-18). In code we use it like this:

   1: bool isError = false;
   2: UpdateListItemSafely(ctx, list, item, users, out isError);
   3: if (isError)
   4: {
   5:     // error handling
   6: }

And similar approach was used for Graph API calls. After that web job runs became much more robust.

No comments:

Post a Comment