Sitecore Commerce 10.2 adventures – SwitchOnRebuild SolrCloud

Happy Sitecore developers ๐Ÿ™‚
I hope you are all good out there and are enjoying life. If not, I suggest that you (at once) sign up for Sitecore User Group Conference – SUGCON Europe 2022. Very cool sessions and it’s located in the beautiful Budapest ๐Ÿ™‚

Today’s post will be about Sitecore Commerce 10.2 and implementing SwitchOnRebuild for Solrcloud. I’m currently working on an upgrade from 10 to 10.2. And I want to share my findings(pain) when implementing SwitchOnRebuild.

We have SwitchOnRebuild already implemented in our “current” Sitecore Commerce 10 setup and it’s working.

First of all, when upgrading to 10.2(Sitecore Commerce) a lot has happened. From now on you have to set up/build all the (docker)images by your selves. Don’t get me wrong the documentation is great and the commerce container setup provided by Sitecore is also great. But before(in previous versions) Sitecore spoiled us with already created images(like cm, cd, engine, SQL and so on). Now we have to create them all from scratch ๐Ÿ™‚

That was the first chock. But as I said the container setup and documentation from Sitecore are great. So it’s ok ๐Ÿ™‚

I had to add some stuff like the file watcher script(listens to a deploy folder) for the CM and CD(Sitecore Commerce). This was fixed by adding the great tooling image => sitecore-docker-tools-assets. There is more, let’s save it for a future post ๐Ÿ™‚

Anyway… To get the SwitchOnRebuild to work you have to create “rebuild” collections. This is not out of the box (except for the Commerce collections) when using the solr-init image(which you also have to build/create). You can extend the solr-init image like this great blog post suggests – Sitecore 10 Container with SolrCloud and SwitchOnRebuild. Or create your own(not using sitecore’s solr-init), like George Change’s great post – Deploy Your Own SolrCloud to Kubernetes for Fun and Profit. You decide what suits you best ๐Ÿ™‚

So, let’s say we have created all the necessary collections and set up a running solrcloud. Next is the SwitchOnRebuild config. Why not use Sitecore’s own example – Sitecore.ContentSearch.SolrCloud.SwitchOnRebuild.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:search="http://www.sitecore.net/xmlconfig/search/">
  <sitecore role:require="Standalone or ContentManagement" search:require="Solr">
    <contentSearch>
      <configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes hint="list:AddIndex">
          <!-- MASTER INDEX configuration -->
          <!-- Replaces the default SolrSearchIndex type which uses aliases with a swap command that allows Sitecore to start up even when a Solr connection is not available.
               Once the Solr connection comes back, the patch restarts the application and initializes the Solr index.
               A SwitchOnRebuild index expects 4 parameters to be passed to the constructor:
                  mainalias:          The alias name for the primary index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  rebuildalias:       The alias name for the rebuild index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  collection:         The primary index collection name.
                  rebuildcollection:            The rebuild index collection name.
          -->
          <index id="sitecore_master_index">
            <patch:attribute name="type" value="Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSolrCloudSearchIndex, Sitecore.ContentSearch.SolrProvider" />
            <param desc="core">
              <patch:delete />
            </param>
            <param desc="rebuildcore">
              <patch:delete />
            </param>
            <param patch:after="*[@desc='name']" desc="mainalias">$(id)MainAlias</param>
            <param patch:after="*[@desc='mainalias']" desc="rebuildalias">$(id)RebuildAlias</param>
            <param patch:after="*[@desc='rebuildalias']" desc="collection">$(id)</param>
            <param patch:after="*[@desc='collection']" desc="rebuildcollection">$(id)_rebuild</param>
          </index>
          <!-- WEB INDEX configuration -->
          <!-- Replaces the default SolrSearchIndex type which uses aliases with a swap command that allows Sitecore to start up even when a Solr connection is not available.
               Once the Solr connection comes back, the patch restarts the application and initializes the Solr index.
               A SwitchOnRebuild index expects 4 parameters to be passed to the constructor:
                  mainalias:          The alias name for the primary index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  rebuildalias:       The alias name for the rebuild index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  collection:         The primary index collection name.
                  rebuildcollection:            The rebuild index collection name.
          -->
          <index id="sitecore_web_index">
            <patch:attribute name="type" value="Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSolrCloudSearchIndex, Sitecore.ContentSearch.SolrProvider" />
            <param desc="core">
              <patch:delete />
            </param>
            <param desc="rebuildcore">
              <patch:delete />
            </param>
            <param patch:after="*[@desc='name']" desc="mainalias">$(id)MainAlias</param>
            <param patch:after="*[@desc='mainalias']" desc="rebuildalias">$(id)RebuildAlias</param>
            <param patch:after="*[@desc='rebuildalias']" desc="collection">$(id)</param>
            <param patch:after="*[@desc='collection']" desc="rebuildcollection">$(id)_rebuild</param>
          </index>
          <!-- CORE INDEX configuration -->
          <!-- Replaces the default SolrSearchIndex type which uses aliases with a swap command that allows Sitecore to start up even when a Solr connection is not available.
               Once the Solr connection comes back, the patch restarts the application and initializes the Solr index.
               A SwitchOnRebuild index expects 4 parameters to be passed to the constructor:
                  mainalias:          The alias name for the primary index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  rebuildalias:       The alias name for the rebuild index operations. 
                                      The ContentSearch.Solr.EnforceAliasCreation setting controls whether the alias should be created automatically. 
                                      It must have only one collection assigned to it.
                  collection:         The primary index collection name.
                  rebuildcollection:            The rebuild index collection name.
          -->
          <index id="sitecore_core_index">
            <patch:attribute name="type" value="Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSolrCloudSearchIndex, Sitecore.ContentSearch.SolrProvider" />
            <param desc="core">
              <patch:delete />
            </param>
            <param desc="rebuildcore">
              <patch:delete />
            </param>
            <param patch:after="*[@desc='name']" desc="mainalias">$(id)MainAlias</param>
            <param patch:after="*[@desc='mainalias']" desc="rebuildalias">$(id)RebuildAlias</param>
            <param patch:after="*[@desc='rebuildalias']" desc="collection">$(id)</param>
            <param patch:after="*[@desc='collection']" desc="rebuildcollection">$(id)_rebuild</param>
          </index>
        </indexes>
      </configuration>
    </contentSearch>
    <settings>
      <!--  ENFORCES ALIAS CREATION ON INDEX INITIALIZATION
            If enabled, index aliases will be created on Solr during the index initialization process.
            Default vaue: false
      -->
      <setting name="ContentSearch.Solr.EnforceAliasCreation" value="false" />
    </settings>
  </sitecore>
</configuration>

* Normally you have your own setup, like different naming of the aliases and the collections

So now it should work, right? Well… It did in our previous version(Sitecore Commerce 10), but not anymore. The CM kept restarting and the logs gave the error message:

2864 20:39:57 ERROR Error loading hook: <hook type="Sitecore.ContentSearch.Hooks.Initializer, Sitecore.ContentSearch" patch:source="Sitecore.ContentSearch.config" xmlns:patch="http://www.sitecore.net/xmlconfig/" />
Exception: Sitecore.Exceptions.ConfigurationException
Message: Could not create instance of type: Sitecore.ContentSearch.SolrProvider.SolrSearchIndex. No matching constructor was found. Constructor parameters: 
Source: Sitecore.Kernel
   at Sitecore.Configuration.DefaultFactory.CreateFromTypeName(XmlNode configNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert, IFactoryHelper helper)
   at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.DefaultFactory.GetInnerObject(XmlNode paramNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.DefaultFactory.AssignProperties(XmlNode configNode, String[] parameters, Object obj, Boolean assert, Boolean deferred, IFactoryHelper helper)
   at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert, IFactoryHelper helper)
   at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert)
   at Sitecore.Configuration.DefaultFactory.CreateObject(String configPath, String[] parameters, Boolean assert)
   at Sitecore.ContentSearch.ContentSearchManager.get_SearchConfiguration()
   at Sitecore.ContentSearch.Hooks.Initializer.Initialize()
   at Sitecore.Events.Hooks.HookManager.LoadAll()

I can mention we also have some custom indexes, which are also added to the SwitchOnRebuild config. The funny thing is that it all worked great when only the custom indexes were in place in the SwitchOnRebuild config.

But as soon as the “Sitecore indexes”(master or web) were added to the config, the error was back.

And notice the error, it’s not saying that Sitecore.ContentSearch.SolrProvider.SwitchOnRebuildSolrCloudSearchIndex is wrong. It’s the Sitecore.ContentSearch.SolrProvider.SolrSearchIndex

This was very frustrating, I really could not find a solution. I tried everything, I even set up a vanilla Sitecore Commerce 10.2(without our code) but still the same error. This was a nightmare and the Impostor Syndrome was coming…

So what to do? I went through everything and had a look(again) at how the CM image was built and I found this little guy in the docker file (This is from the Sitecore.Commerce.Container.SDK.3.0.243):

RUN New-Item -Path C:\inetpub\wwwroot\App_Config -Name Security-Shared -ItemType directory; `
    c:\tools\nuget.exe install Microsoft.Web.Xdt -Version 3.0.0 -OutputDirectory C:\tools -ExcludeVersion; ` 
    c:\tools\scripts\Invoke-XdtTransformations.ps1 -TargetPath c:\inetpub\wwwroot -XdtPath c:\xdts -XdtDllPath c:\tools\Microsoft.Web.Xdt\lib\netstandard2.0\Microsoft.Web.XmlTransform.dll;`
    Push-Location c:/inetpub/wwwroot/App_Config/Include/Y.Commerce.Engine; `
    Rename-Item Sitecore.Commerce.Engine.DataProvider.config.disabled Sitecore.Commerce.Engine.DataProvider.config; `
    Rename-Item Sitecore.Commerce.Engine.Connectors.Index.Common.config.disabled Sitecore.Commerce.Engine.Connectors.Index.Common.config; `
    Rename-Item Sitecore.Commerce.Engine.Connectors.Index.Solr.config.disabled Sitecore.Commerce.Engine.Connectors.Index.Solr.config; `
    Pop-Location; `
    Remove-Item -Path "./XConnectFiles/" -Recurse -Force;

Notice the line:

Rename-Item Sitecore.Commerce.Engine.Connectors.Index.Solr.config.disabled Sitecore.Commerce.Engine.Connectors.Index.Solr.config;

So I had a look at the config patch and found this at the bottom.

<configuration type="Sitecore.ContentSearch.ContentSearchConfiguration, Sitecore.ContentSearch">
        <indexes role:require="Standalone or (ContentManagement and Indexing)" hint="list:AddIndex">
          <index id="sitecore_master_index">
            <strategies hint="list:AddStrategy">
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/commerceEngineIntervalAsynchronousStrategyMaster" />
            </strategies>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.Commerce.Engine.Connect.Search.Crawlers.CommerceEngineCrawler, Sitecore.Commerce.Engine.Connect">
                <ListName>SitecoreItemIndexingMaster</ListName>
              </crawler>
            </locations>
          </index>
          <index id="sitecore_web_index">
            <strategies hint="list:AddStrategy">
              <strategy ref="contentSearch/indexConfigurations/indexUpdateStrategies/commerceEngineIntervalAsynchronousStrategyWeb" />
            </strategies>
            <locations hint="list:AddCrawler">
              <crawler type="Sitecore.Commerce.Engine.Connect.Search.Crawlers.CommerceEngineCrawler, Sitecore.Commerce.Engine.Connect">
                <ListName>SitecoreItemIndexingWeb</ListName>
              </crawler>
            </locations>
          </index>
        </indexes>
      </configuration>

The config patch is adding some extra stuff to the master and web index, and that is fine. But… Look at the location of the config – in folder Y.Commerce.Engine:

And suddenly it was all clear to me why(and how) the error occurred. We are following the HELIX approach which all sane Sitecore developers should do ๐Ÿ˜‰ So you could guess where we had the SwitchOnRebuild config placed, somewhere in the Foundation folder(some levels down). What does this mean? This means it was patched before the Sitecore.Commerce.Engine.Connectors.Index.Solr.config was patched and that is why the error occurred.

So by moving the SwitchOnRebuild config to the Z.Foundation.Overrides, meaning it will be patched after the Sitecore.Commerce.Engine.Connectors.Index.Solr config, then everything worked(As it should).

Lessons learned, always check the configs and especially what order they are executed/patched in.

Thatโ€™s all for now folksย ๐Ÿ˜Š



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.