Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

> It's probably time to overhaul your processes
Cover Image for Tips for Applying Cumulative Sitecore XM/XP Patches and Hotfixes

Overview

A while back, Sitecore changed their patch model to be cumulative rather than iterative. This makes perfect sense because it greatly reduces complexity and the overhead of supporting each historical / outdated patch. This approach allows Sitecore to be more focused and effective. For example, and to Sitecore's credit, they recently started doing more security auditing of their codebase and released a flurry of security patches over the past year. This has posed challenges for partners in terms of how they manage / apply / test patches. From what I've seen and heard, the Sitecore community has been struggling with this change.

This post covers some challenges, approaches, and learnings with regards to applying cumulative patches for Sitecore XM/XP, be they hotfixes or not.

Challenge: Cumulative Patches Are Ephemeral

One unique challenge I encountered was an error related to a cumulative patch which was difficult to troubleshoot because the patch was not documented anywhere.

In my case, on my local site, the Content Editor search was broken. The following error message was displayed:

Error Message


_3
An error occurred while searching. Rephrase the query.
_3
_3
Could not find property 'MaxNumberOfQueries' on object of type: Sitecore.ContentSearch.Client.Pipelines.Search.SearchContentSearchIndex

In the Sitecore logs:


_15
2023-08-03 10:14:01 608 10:13:34 ERROR An error occurred while searching with Sitecore.Search.FullTextQuery
_15
2023-08-03 10:14:01 Exception: System.InvalidOperationException
_15
2023-08-03 10:14:01 Message: Could not find property 'MaxNumberOfQueries' on object of type: Sitecore.ContentSearch.Client.Pipelines.Search.SearchContentSearchIndex
_15
2023-08-03 10:14:01 Source: Sitecore.Kernel
_15
2023-08-03 10:14:01 at Sitecore.Configuration.DefaultFactory.AssignProperties(Object obj, Object[] properties)
_15
2023-08-03 10:14:01 at Sitecore.Configuration.DefaultFactory.AssignProperties(XmlNode configNode, String[] parameters, Object obj, Boolean assert, Boolean deferred, IFactoryHelper helper)
_15
2023-08-03 10:14:01 at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, String[] parameters, Boolean assert, IFactoryHelper helper)
_15
2023-08-03 10:14:01 at Sitecore.Configuration.DefaultFactory.CreateObject(XmlNode configNode, Boolean assert)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.CorePipelineFactory.GetObjectFromType(XmlNode processorNode)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.CorePipelineFactory.GetProcessorObject(XmlNode processorNode)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.CoreProcessor.GetMethod(Object[] parameters)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain, Boolean failIfNotExists)
_15
2023-08-03 10:14:01 at Sitecore.Pipelines.DefaultCorePipelineManager.Run(String pipelineName, PipelineArgs args, String pipelineDomain)
_15
2023-08-03 10:14:01 at Sitecore.Shell.Applications.ContentManager.ContentEditorForm.TreeSearch_Click()

The offending code:


_19
<pipelines>
_19
<search>
_19
<!-- EXTEND SEARCH PIPELINE
_19
Processes Content Tree Search and Quick Search (Instant Search) queries.
_19
-->
_19
<processor patch:before="*[@type='Sitecore.Pipelines.Search.DatabaseResolver, Sitecore.Kernel']" type="Sitecore.ContentSearch.Client.Pipelines.Search.SearchContentSearchIndex, Sitecore.ContentSearch.Client">
_19
<!-- The maximum number of search requests to the search engine.
_19
Default value: 5
_19
-->
_19
<MaxNumberOfQueries>5</MaxNumberOfQueries>
_19
<!-- The maximum number of search results retrieved in one request to the search engine.
_19
If the page size is set to zero then the page size is determined based on the maximum number of search results,
_19
see settings "Search.ContentTreeSearch.MaxResults", "Search.InstantSearch.MaxResults", "Search.ClassicSearch.MaxResults".
_19
Default value: 0
_19
-->
_19
<PageSize>0</PageSize>
_19
</processor>
_19
</search>
_19
</pipelines>

As far as I could tell, this code was not part of the default Sitecore installation. I did some investigating, and this config appears to have originated from the Sitecore.Support.008435 package. Support packages such as this are often "ephemeral" in the sense that since Sitecore now only does cumulative fixes, historical patches lack public documentation because they are replaced as new cumulative patches are released; that is, for a given version of XM/XP, Sitecore maintains a centralized SharePoint directory containing the cumulative patch, and updates it in real time. The changes and documentation are also not crawlable because the SharePoint directory requires authentication. Case in point:

In my case, the issue seems to have been at least partially caused by the fact that the CloudCumulative version of the patch was installed on all environments, including local environments. Presumably, a step in the right direction would be to apply the OnPremCumulative version on local environments and to continue troubleshooting from there, if needed. This then begs the question: how should one manage patches across different environments/architectures?

Generally speaking, don't expect much help or documentation when it comes to patches. Make sure you have solid processes in place.

How to Manage Patches?

Some interesting discussions have taken place within the Sitecore community about how to manage patches. I don't take credit for any of the following discussion points that took place in the Sitecore Slack, but I wanted to surface them for the public:

🥸 said:

DLLs in Hotfixes vs NuGet references: I logged a support ticket asking exactly the same question (whether Sitecore plans to release NuGet packages for the latest security patch) and the answer was that "Sitecore does not provide NuGet packages for hotfixes". That then begs the question - what's the best way to maintain your solution structure. You'll need to remove the NuGet references for these updated DLLs - right? If someone can expand on this a bit - I would greatly appreciate it.

😺 said:

I've generally deployed hotfixes over top as part of Cl/CD, as there shouldn't be anything added in a hotfix that your code relies on (ie for adding as NuGet in a solution)

🥷 said:

i follow the other path. reference the hotfix DLLs in the project especially if Sitecore.Kernel is in the hotfix package to avoid mysterious errors.

👨‍🚒 said:

I prefer to go the route that 😺 posted, as it is rare that the changes impact development. If you add them to a project, then this can create issues with upgrades, as well as potential issues when someone makes a new project and uses a NuGet package instead of the hot fix DLL.

💂‍♀️ said:

Been following a similar route to 🥷, but create a separate Support project in the solution. The NuGet packages in the other projects are left alone (we use .wpp.target files to prevent Sitecore DLLs being published). We can then delete this "feature" during an upgrade.

👨‍🚒 said:

That works with wpp and makes sense.

To summarize, some of the high level approaches include:

  • Templated CI/CD deploy step that installs files on top of the solution
  • Include patch files directly in solution
  • Install (patches which are not hotfixes) as NuGet packages
  • Apply changes directly in the affected environments and don't include in code or CI/CD (not recommended)

CI/CD Approach

Diving in a little more to this approach, there are pros and cons.

One con that comes to mind is that patches are only applied upstream, so there may be environment deltas between local and upstream installs. This can result in local issues that don't appear in upstream environments, thus potentially resulting in wasted time if a developer is trying to troubleshoot an issue that isn't an actual issue. In this case, you should probably indicate in the README that a patch will need to be manually applied in local environments. Better yet, you can automate this via Docker install scripts.

One pro is that the CI/CD approach is highly scalable if you are managing many sites. You get a lot of oversight and the ability to quickly deploy patches to all of your sites.

Code in Solution Approach

The code in solution approach can certainly be appropriate for some implementations. The big question is how to best do it. Some things to consider:

  • Where should the files actually live within the solution, and how do you differentiate and reconcile the interactions and deploy order of the patches?
  • How to deploy the patches in local environments?
  • How to deploy the patches in upstream environments? Recall that local and cloud installs can have different architectures.
  • How to prevent atrophy of documentation within the code?

You will also want to consider upgrade implications.

Conclusion and Learnings

  • Sitecore does not provide NuGet packages for hotfixes.
  • There are various approaches for applying cumulative patches. The one you choose will depend on your team's preferences and your CI/CD pipeline. Personally, I like the CI/CD approach.
  • Keep documentation (who, what, where, when, why) for when you apply cumulative patches. Including this documentation in the code has the benefit of being version controlled and easily accessible by multiple teams who may not have access to the programs where you may store other documentation (Confluence, Notion, etc.).
  • Know the differences between packages for different types of deployments and understand that you must apply the patch that applies to the specific environment you are working in (example: don't install OnPrem patches on cloud environments, and vice versa). Come up with processes to ensure that the correct versions are installed on the correct environments:
    • Cloud
    • CloudCumulative
    • OnPrem
    • OnPremCumulative
  • Keep track of how and where you applied previous patches.
  • Apply the latest cumulative patch before contacting Sitecore Support.
  • If the issue persists, contact Sitecore Support.
  • Compare your code deltas with the latest cumulative patch and also the default Sitecore installation to identify deltas.
  • Automate regression testing where possible.

Or, migrate to XM Cloud and let Sitecore handle the patching for you. 😎

KEEP PATCHING,

MG


More Stories

Cover Image for On Mentorship and Community Contributions

On Mentorship and Community Contributions

> Reflections and what I learned as an MVP mentor

Cover Image for JSS: Reducing Bloat in Multilist Field Serialization

JSS: Reducing Bloat in Multilist Field Serialization

> Because: performance, security, and error-avoidance

Cover Image for Ideas For Docker up.ps1 Scripts

Ideas For Docker up.ps1 Scripts

> Because Docker can be brittle

Cover Image for NextJS: Access has been blocked by CORS policy

NextJS: Access has been blocked by CORS policy

> CORS is almost as much of a nuisance as GDPR popups

Cover Image for Content Editor Search Bar Not Working

Content Editor Search Bar Not Working

> Sometimes it works, sometimes not

Cover Image for Add TypeScript Type Checks to RouteData fields

Add TypeScript Type Checks to RouteData fields

> Inspired by error: Conversion of type may be a mistake because neither type sufficiently overlaps with the other.

Cover Image for NextJS: Unable to Verify the First Certificate

NextJS: Unable to Verify the First Certificate

> UNABLE_TO_VERIFY_LEAF_SIGNATURE

Cover Image for Script: Boost SIF Certificate Expiry Days

Script: Boost SIF Certificate Expiry Days

> One simple script that definitely won't delete your system32 folder

Cover Image for SPE Script Performance & Troubleshooting

SPE Script Performance & Troubleshooting

> Script never ends or runs too slow? Get in here.

Cover Image for Azure PaaS Cache Optimization

Azure PaaS Cache Optimization

> App Services benefit greatly from proper configuration

Cover Image for Year in Review: 2022

Year in Review: 2022

> Full steam ahead

Cover Image for NextJS/JSS Edit Frames Before JSS v21.1.0

NextJS/JSS Edit Frames Before JSS v21.1.0

> It is possible. We have the technology.

Cover Image for Tips for New Sitecore Developers

Tips for New Sitecore Developers

> Because learning Sitecore can be hard

Cover Image for Super Fast Project Builds with Visual Studio Publish

Super Fast Project Builds with Visual Studio Publish

> For when solution builds take too long

Cover Image for Tips for Forms Implementations

Tips for Forms Implementations

> And other pro tips

Cover Image for Hello World

Hello World

> Welcome to the show

Cover Image for Sitecore Symposium 2022

Sitecore Symposium 2022

> What I'm Watching 👀

Cover Image for On Sitecore Stack Exchange (SSE)

On Sitecore Stack Exchange (SSE)

> What I've learned, what I see, what I want to see

Cover Image for NextJS: Short URL for Viewing Layout Service Response

NextJS: Short URL for Viewing Layout Service Response

> Because the default URL is 2long4me

Cover Image for JSS + TypeScript Sitecore Project Tips

JSS + TypeScript Sitecore Project Tips

> New tech, new challenges

Cover Image for Don't Ignore the HttpRequestValidationException

Don't Ignore the HttpRequestValidationException

> Doing so could be... potentially dangerous

Cover Image for Symposium 2022 Reflections

Symposium 2022 Reflections

> Sitecore is making big changes

Cover Image for How to Run Old Versions of Solr in a Docker Container

How to Run Old Versions of Solr in a Docker Container

> Please don't make me install another version of Solr on my local...

Cover Image for Early Returns in React Components

Early Returns in React Components

> When and how should you return early in a React component?