Best Practices For Caching Sharepoint Group Membership Checks To Improve Performance

Checking a user’s group membership in SharePoint can be a frequent yet resource-intensive operation. Every call to check Active Directory group membership results in added load on the domain controllers. When apps make repeated access check calls, the accumulated load can cause severe performance issues.

Caching the results of group membership checks allows bypassing AD for frequent authorization and personalization lookups. Effective caching strategies are critical for performant SharePoint apps and pages that display personalized content.

Why Caching is Critical for SharePoint Apps

Complex SharePoint pages make multiple calls to determine the current user’s group assignments. Pages with personalized modules and access rules check if the user belongs to certain groups before rendering each component.

For example, a team site page may verify if the user is a member of the Visitors, Members, or Owners groups before showing tabs, links, and document libraries. Without caching, the page generates excessive traffic to the domain controllers.

Resource Load from Membership Checks

Every group membership check via the SharePoint API or LDAP results in queries to Active Directory. The domain controller handles each query individually to check the user’s group attribute for membership.

With a medium to large user base, hundreds of users accessing pages trigger a barrage of calls back to the domain. The accumulated load leads to slow response times from AD and bottlenecks app performance.

Membership Caching Cuts Down Calls

Caching group IDs and membership in the app layer avoids unnecessary calls to AD during page processing. Subsequent checks hit the cached lists instead of querying AD repeatedly.

This caching drops the number of calls to Active Directory by over 60% for busy portal sites with personalization for audiences. The reduced load results in faster page response and smoother user experience.

The Problem with Frequent Access Checks

Apps that customize content for different audiences end up with code that continually checks group membership. Every personalized module or component on a page can initiate one or more calls to verify the user’s groups or roles.

Performance Impact of AD Checks

The more membership checks in an app, the higher the cumulative impact on performance. Unoptimized apps generate frequent traffic to the domain controllers that hampers responsiveness for all SharePoint users.

If hundreds of users hit pages with multiple access rules, the queries start piling up at the DC. The rising load lags AD response times for lookup calls from across SharePoint.

Duplicate and Redundant Checks

Apps rarely optimize membership checks, so the same user keeps getting verified for the same groups and roles. These redundant checks waste resources both in app processing and AD queries.

Poorly coded apps also retrieve all group IDs on every page request instead of reusing cached data. Such duplicate access calls create unnecessary load without adding any value.

Best Practices for Caching Group Membership

Efficient caching requires optimizing both where group data is stored and when it is refreshed. Follow these coding best practices for caching groups and roles in SharePoint apps.

Store Group IDs in App Settings

Start by declaring string constants for AD groups used in the app’s authorization and personalization logic. Add these string ID values in the web.config appSettings section.

For example, define public constants for groups like ADMINISTRATORS_GROUP, APPROVERS GROUP. Set these constants to reference the values declared in app settings instead of hardcoded group IDs.

  <appSettings>   
    <add key="ADMINISTRATORS_GROUP" value="SharePoint Admins" />
  </appSettings>

  public const string ADMINISTRATORS_GROUP = 
          ConfigurationManager.AppSettings["ADMINISTRATORS_GROUP"];

This helps centralize group values used across app pages and components into config settings for easier management.

Cache Group IDs in Memory

When the app starts up, fetch all group IDs from app settings into in-memory cache objects such as dictionaries or lists. Page code can then check cached groups instead of querying AD repeatedly.

public static List<string> cachedGroups;

protected void Application_Start() 
{
  // Cache Groups on Startup
  cachedGroups = new List<string>();
  cachedGroups.Add(ConfigurationManager.AppSettings["ADMINISTRATORS_GROUP"]); 
  cachedGroups.Add(ConfigurationManager.AppSettings["APPROVERS_GROUP"]);
}  

This initialization code populates the cached groups only once per app domain restart. Pages can reuse the cache instead of reloading groups from AD on every request.

Limit Number of AD Calls

Wrap membership check methods in logic that checks the cached group lists first before calling AD. This coding pattern limits calls to the absolute minimum for each page.

public bool IsUserInAdminGroup(SPUser user)
{
  // Check AD only if not already cached
  if(cachedGroups.Contains(ADMINISTRATORS_GROUP)) {
    return true; 
  }
  else {
    // Call AD 
    return ADGroupManager.IsCurrentUserMemberOfGroup(ADMINISTRATORS_GROUP);
  }
}

Such selective AD calls fetch uncached data only when needed instead of every time. This principle applies equally to server-side code and client-side JavaScript when using the SharePoint REST API.

Set Short Cache Expiry Times

Group membership can change, so the cached lists should expire frequently. Configure a short sliding expiry of 5 to 15 minutes for group caches.

When the duration lapses, reset the cache and fetch the latest IDs from AD. This ensures apps show users their current group access without retaining stale data.

  private void CacheWithExpiry(List<string> groups) 
  {
    MemoryCache cache = MemoryCache.Default;
    CacheItemPolicy policy = new CacheItemPolicy();

    policy.SlidingExpiration = TimeSpan.FromMinutes(15);
    cache.Set(CACHE_GROUPS_KEY, groups, policy);
  }

Do not overuse sliding expiry as it keeps updating the cache lifetime. Set an absolute expiry instead if cached data is disposable after some time.

Example Code for Efficient Caching

Apply these snippets in SharePoint apps to optimize caching of group IDs queried for membership checks:

Cache Group IDs on Startup

Seed the cache from app settings or AD when the app domain starts.

  List<string> cachedGroups;
	
  void Application_Start()
  {
    cachedGroups = new List<string>();
    
    // Add admin & approver groups 
    cachedGroups.Add(ConfigurationManager.AppSettings["ADMIN_GROUP"]);
    cachedGroups.AddRange(GetApproverGroupIds());   

    // Expire every 15 minutes	
    CacheWithExpiry(cachedGroups, TimeSpan.FromMinutes(15));
  }

Check Cache Before Access Calls

  public bool IsAdminUser() 
  {
    if(cachedGroups.Contains(ADMIN_GROUP)) {
      return true; 
    }
    else {
      // Check AD only if cache missed 		
      return ADManager.IsCurrentUserInGroup(ADMIN_GROUP); 
    }
  }

Refresh Cache in Background

  // Refresh cache in a background thread every 15 minutes
  Timer cacheTimer = new Timer(RefreshCache, null, 900000, 900000);

  void RefreshCache(object state)
  {
    CacheWithExpiry(GetLatestGroups(), TimeSpan.FromMinutes(15));	
  } 

This separates cache rebuild from existing requests so performance impact is negligible during refreshes.

Conclusion

Checking group membership is unavoidable for personalized SharePoint apps. But optimized caching strategies easily minimize performance overheads of frequent AD calls.

Storing cached group data with short expiries provides performant authorization without compromising on accuracy.

With robust caching implemented, SharePoint apps can validate user access and customize content while avoiding domain bottlenecks.

Leave a Reply

Your email address will not be published. Required fields are marked *