Optimizing Performance on Sitecore Sites with Millions of Users


Sitecore item security can get very complex.  Sitecore secures sites, items, item fields, workflows, workflow actions, etc. On top of all that security can be assigned to an individual user, the roles that they are in, and even the roles that those roles are in.  Imaging all the overhead that can happen calculating all this on the fly for every request, every page item, every data source item, every field....The overhead is significant.

In the scaling documentation for Sitecore, Sitecore suggests disabling security on your content delivery nodes if it is not needed. https://doc.sitecore.com/developers/92/platform-administration-and-architecture/en/walkthrough--disable-the-security-database-on-a-content-delivery-instance.html  This is great BUT in my 10+ years working on Sitecore solutions every site I have worked on has required some sort of federated authentication (Single-Sign-On) and performance at the same time.  These are the tactics I apply.

Use Virtual Users

 Virtual users in Sitecore are users that do not persist to the user database, instead they are created in memory only and on the fly.  Federated authentication can be set up according to the Sitecore documentation here but when you set it up for your website users make sure your users are set up in the extranet domain and that the userbuilder configuration is set up with the isPersistentUser = false.  
**Also a note, try NOT to add roles to your virtual users, it will raise an event that will save to your EvenQueue table which will create a performance issue.


1
2
3
<externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication">
   <param desc="isPersistentUser">false</param>
</externalUserBuilder>

Disable User Profile

If you can avoid using the user profile for your public users you should.  This is controlled by the site definition and can be disabled using the following configuration.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?xml version="1.0"?> 
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/"
               xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    <sites>
      <site name="website">
        <patch:attribute name="disableClientData">true</patch:attribute>
        <patch:attribute name="domain">extranet</patch:attribute>
      </site>
    </sites>
  </sitecore>
</configuration>

Use Switching Providers

Buy using switching providers for users, roles, and profiles you can ensure that you do not share user data stores between your Sitecore accounts and your public website accounts.  In many cases you can disable these providers all together for your web users.

You do this by modifying the providers in your web.config in the root of your web sites and set the default providers all to the switcher providers shipped out-of-the-box.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    <membership defaultProvider="switcher" hashAlgorithmType="SHA1">
      <providers>
        <clear />
        <add name="sitecore" type="Sitecore.Security.SitecoreMembershipProvider, Sitecore.Kernel" realProviderName="sql" providerWildcard="%" raiseEvents="true" />
        <add name="sql" type="System.Web.Security.SqlMembershipProvider" connectionStringName="security" applicationName="sitecore" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" />
        <add name="disabled" type="Sitecore.Security.DisabledMembersipProvider, Sitecore.Kernel" applicationName="sitecore" />
        <add name="switcher" type="Sitecore.Security.SwitchingMembershipProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/membership" />
      </providers>
    </membership>
    <roleManager defaultProvider="switcher" enabled="true">
      <providers>
        <clear />
        <add name="sitecore" type="Sitecore.Security.SitecoreRoleProvider, Sitecore.Kernel" realProviderName="sql" raiseEvents="true" />
        <add name="sql" type="System.Web.Security.SqlRoleProvider" connectionStringName="security" applicationName="sitecore" />
        <add name="disabled" type="Sitecore.Security.DisabledRoleProvider, Sitecore.Kernel" applicationName="sitecore" />
        <add name="switcher" type="Sitecore.Security.SwitchingRoleProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/roleManager" />
      </providers>
    </roleManager>
    <profile defaultProvider="switcher" enabled="true" inherits="Sitecore.Security.UserProfile, Sitecore.Kernel">
      <providers>
        <clear />
        <add name="sql" type="System.Web.Profile.SqlProfileProvider" connectionStringName="security" applicationName="sitecore" />
        <add name="disabled" type="Sitecore.Security.DisabledProfileProvider, Sitecore.Kernel" applicationName="sitecore" />
        <add name="switcher" type="Sitecore.Security.SwitchingProfileProvider, Sitecore.Kernel" applicationName="sitecore" mappings="switchingProviders/profile" />
      </providers>
      <properties>
        <clear />
        <add type="System.String" name="SC_UserData" />
      </properties>
    </profile>

Now you can use a custom provider or, better yet, disable the providers altogether for your website users through the following patch:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="Standalone or ContentDelivery or ContentManagement">
    <switchingProviders>
      <membership>
        <provider providerName="sql" storeFullNames="true" wildcard="%" domains="*" 
                  patch:instead="*[@providerName='sql']" />
      </membership>
      <roleManager>
        <provider providerName="sql" storeFullNames="true" wildcard="%" domains="*" ignoredUserDomains="" allowedUserDomains="" 
                  patch:instead="*[@providerName='sql']" />
      </roleManager>
      <profile>
        <provider providerName="sql" storeFullNames="true" wildcard="%" domains="*" ignoredDomains="extranet" 
                  patch:instead="*[@providerName='sql']" />
        <provider providerName="disabled" storeFullNames="true" wildcard="%" domains="extranet" />
      </profile>
    </switchingProviders>
  </sitecore>
</configuration>

 Disable Security on Web Database on CD

A really impactful change is simple.  You can disable the security checks on the web/delivery databases if that is not required.  If you do this you must add your own security to stop access to items that users should not access but that work can be worth the performance gain that can be achieved.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore role:require="ContentDelivery">
    <databases>
      <database id="web">
         <securityEnabled>false</securityEnabled>
      </database>
    </databases>
  </sitecore>
</configuration>


I hope all this is helpful.  These things made huge impact for me.
2 comments

2 comments :

  1. Great post. On this point that we should avoid adding extranet roles to Virtual users, is there any alternative recommendations? In most of the time, we set the item restriction based on the role and these Virtual users need to be added with roles.

    ReplyDelete
    Replies
    1. Good question. If you "Disable Security on Web Database on CD" as I suggest, item restriction based on the role will be ignored. If you need that you will have to skip that step and add roles to the virtual user but you will need to use the EventDisabler when you add the roles to your virtual user (avoid tons of events in EventQueue) AND consider overriding the default behavior of the AuthorizationProvider. The AuthorizationProvider will create tons of entries to the AccessResultsCache and it will eventually fill up to the max size or if you let it get to big spike the CPU until your server stops responding.

      I hope that makes sense to you. Good Luck!

      Delete