Showing posts with label performance. Show all posts
Showing posts with label performance. Show all posts

Saturday, March 22, 2025

Using Sitecore Indexes in PowerShell-Driven Multilist Datasources


If you didn't know, you can point a Sitecore field's datasource to a PowerShell script. It's a super clean way to make dynamic picklists, filtering based on the current item, tags, templates, relationships, you name it.

But if you're pulling a large set of items, you really want to use the search index.

That's where things get weird.

The Find-Item command gives you fast results… but they aren't real Sitecore items. They're search result objects; great for speed, not so great for populating a multilist. Sitecore expects actual items, and when it doesn't get them, your field ends up looking empty.

Luckly, you don't have to abandon the approach completely. The fix is actually a pretty straightforward.


Some Context

For context, you can set the datasource for a multilist to be driven via a script in SPE like this:

In the script definition itself, you can write PowerShell to obtain some set of items from the tree. The resulting list is what shows as applicable for selection on the field.

In theory, you should be able to also utilize the Find-Item commandlet to obtain a list of items from the index. In my case was necessary due the performance implications of running a Get-ChildItem against a massive subtree.

First attempt looked something like this:

So far, so good. You get back a list of items. Or do you?


The Catch

The objects in the $list variable returned by Find-Item are not Sitecore items. They're dynamic search result objects that look like items, walk like items, but won't work in your multilist unless they quack like items.

If you try to return them as-is from your script, you'll find that, even if items were found in the index, the multilist fails to render the items for selection.

The fix? Pretty simple actuallu: Transform the search results back into legit Sitecore items.

Put it all together and you're golden

Now your multilist knows what to do. The search is lightning fast thanks to the index, and authors can pick from relevant matches without sifting through the entire tree.


Why This Matters

This pattern shines when you're working with large content trees or complex tagging structures where traditional item traversal would be painfully slow. By using the index, you're offloading the heavy lifting to Solr, gaining serious performance without sacrificing editor experience.

But more importantly, it calls out a subtle, easy-to-miss SPE gotcha: not all objects returned from PowerShell helpers are Sitecore items. If you're using Find-Item, you'll almost always need a second pass to resolve those results into actual items before they'll work in a field context.

Fail to do that, and you might spend hours wondering why your multilist is coming up empty, despite the index finding exactly what you wanted.

Happy datasourcing! 🚀

Friday, July 31, 2020

Generate Google Lighthouse Reports with Docker using PowerShell



While browsing Docker Hub, I came across this nifty Google Lighthouse Docker image (by Jay Moulin) which allows you to execute a Lighthouse audit against a given URL in a containerized application - made possible by the Google Chrome Headless Docker base image.  From a practical standpoint, this feels more reliable than running Lighthouse in the Chrome browser where extensions and other variables can easily interfere with the results of the audit. 

You can check out the Dockerfile for this image here: 

Consuming it is pretty straightforward.  With Docker installed and running while switched to Linux containers, two commands are all you need:


Additional options for the Lighthouse audit, like controlling the emulated device form factor (mobile vs. desktop), controlling the throttling method (devtools, provided, simulate), or defining specific categories (Accessibility, Best Practices, Performance, PWA, or SEO) can be included after the URL.
 
However, that's quite a bit of text to remember, and memorizing a bunch of Lighthouse CLI options are not something I see myself doing.  😋 

BUT - we can make this tool more approachable by wrapping it in a PowerShell script. 😍

The name of the game is simplicity: execute .\LighthouseReport.ps1 from a PowerShell terminal, pass in a URL/standard Lighthouse options, and let it run. 


👨‍💻 A Little PowerShell

In a new PowerShell file, we'll add a mandatory string parameter called $Url
We'll also include non-mandatory string parameters:
  • $FormFactor
    • Valid options for the '--emulated-form-factor=' flag are 'none', 'desktop', or 'mobile'. 

    • Default value when no parameter is provided will be 'desktop'

  • $Throttling
    • Valid options for the '--throttling-method=' flag are 'devtools', 'provided', or 'simulate'. 

    • Default value when no parameter is provided will be 'provided.'

  • $Categories (array of strings)
    • Valid options for the '--only-categories=' flag are 'accessibility', 'best-practices', 'performance', 'pwa', 'seo'. 

    • Default value when no parameter is provided will a comma-delimited string of all applicable categories 'accessibility,best-practices,performance,pwa,seo'

  • $DestinationPath
    • The local path to where the report will be 'dropped.' (used as a volume mapping to the container's '/home/chrome/reports' directory)

    • Default value when no parameter is provided will be "C:/lighthouse"

We'll add the docker pull command for femtopixel/google-lighthouse first.  During the initial execution of the script,  all required images will be downloaded from Docker Hub.  If your image becomes stale or a newer version is available, this will automatically update the image. 

Then add the docker run command with the -v flag to mount a volume to map the local $DestinationPath to the /home/chrome/reports directory on the container. Include the $URL parameter at the end, and all options following:


When the docker run command is executed, Docker will take over, and Lighthouse will begin to execute on the container. Once completed, a .html file will be available in the $DestinationPath

To take it a step further, we can open the $DestinationPath in Windows Explorer by using an Invoke-Item command:


If we want to open the .html report, we can set the PowerShell location to the $DestinationPath, followed by an Invoke-Item where we pass in Get-ChildItem latest .html file.


Simple - yet effective!

🏁 Final Script


⌨ Example Usage

Desktop form factor auditing all categories:


Desktop form factor auditing Best Practices, Performance and SEO only:


 
Mobile form factor auditing Performance only:


 
  

💡 TIP: When setting a parameter (-FormFactor, -Throttling, -Categories), you can use Ctrl+Space to display valid options and hit enter to select it.



👍 Result



🙌 Feel free to grab a copy and modify it to your liking.

Friday, February 22, 2019

Redis Dead Redemption: Redis Cache Timeouts with Sitecore Azure PaaS


Our production CD environments (v9.0 Initial Release) began experiencing significant performance issues to the point where the application kept restarting our site would hang for several minutes at a time.  When the site finally came to, a few minutes later, it'd go right back down.

With the site having been stable for months, it was odd that this issue surfaced out of the blue.

Our logs revealed the following error (one of many):

What is Redis? 

In a nutshell, Azure Redis cache is the default, OOB session provider Sitecore 9.0+ for PaaS instances.
I think George Tucker's StackOverflow answer summed it up best:

Sitecore uses this session provider as a means of managing out of process (ie. distributed) session state to share contact data across browser sessions and devices. This is required to support base functionality in Sitecore XP (Analytics, XDB etc). Even if you are using a custom session provider for other purposes, you will likely still need the Sitecore sessions configured to get full value out of the solution.  
Redis is designed as a means for fast, distributed access to a key/value store. If you're digging into the redis instance directly, you're likely to see all of the keys of your current sessions (private and shared) with non-readable values (usually stored as binary vals). These values are usually managed and accessed via the Sitecore Session API abstractions. It's rare you would need to access Redis directly.

 Initial Troubleshooting

We attempted a few things in attempts to re-stabilize the environment.
  1. Standard procedures to restart applications: generally no effect
  2. Ran 'flushdb' command against Redis Console: errors cleared initially, but the issue resurfaced soon after.
  3. Reboot Redis: Site restores, goes down soon after
  4. Scaled up Redis: Site restores - crashes soon after with the same Redis errors
  5. Applied Support fix outlined in KB464570: Redis driver timeout issues to both CM and CD environments.
To no avail, we decided to take it up with the Sitecore Support team. 

Sitecore Support's Take

Based on the reported errors, Sitecore support identified the following
  • - The "in: 7923" parameter means that there is an amount of data in the Redis queue that sill need to be processed
  • - The "WORKER: (Busy=85,Free=32682,Min=150,Max=32767)" shows that there is some pressure on the Redis side (even if it is not visible as the CPU is not stressed)

You can read more into what your specific Redis errors may mean from this handy Microsoft blog bost documentation.

Support sanctioned our use of KB464570: Redis driver timeout issues and also recommended applying solutions outlined in a separate KB article: KB858026: Excessive load on ASP.NET Session State store.

Additional suggestions included:
- Scale up the Redis instance.  "You may need extra resources to process all the items  "in: 7923")"
- Increase the timeout until you could see the application running.

Among several solutions offered in KB858026: Excessive load on ASP.NET Session State store. (mostly configuration changes to the Web.config and Sitecore.Analytics.Tracking.config),  one included splitting the Sitecore.Sessions databases into dedicated instances - one for private and another for shared.



Splitting the two databases meant creating a new Redis instance in Azure and use different connections strings for the Shared and Private session definitions.

The documentation articles for adjusting the configuration can be found in two separate documents:

Diagnosis

Since the site was stable for months before this began occurring, Support concluded that the most possible cause is that the amount of data in the Redis' queue slowly built up until the CDs could not keep up with the timeouts.
Splitting the sessions into Private and Shared should allow more control over which Redis instance needs more or less processing power and avoid obscure locks.  Also, increasing the timeout values to higher values allows the queue more time to process.

The Split

After creating a new Redis instance in Azure:

1) Create a new redis.sessions.shared entry in the ConnectionString.config under the existing redis.sessions entry.
2) Leave the original (Private) <sessionState> Redis provider as-is in the Web.config
3) Replace the existing redis.sessions connectionString parameter to redis.sessions.shared.

A Note on Timeout Values

Regarding the timeout parameters (connectionTimeoutInmillisecondsoperationTimeoutInMillisecondsretryTimeoutInMilliseconds) there is no exact value to set.  Like Sitecore caches, it must be tuned until your instances do not experience timeouts.

Sitecore's rule of thumb here is to:
- double the timeout until the environment stabilizes
- set a high value (30 minutes) and then adjust it if it needs further tuning

You can find out what each timeout is designed for here: Redis Provider Settings 


After making these changes, the site has since appeared to have stabilized. Of course, we anticipate to further monitor the logs for Redis errors, but for now, it seems we're in the clear.  

Happy Redis-ing!


Thursday, March 9, 2017

Sitecore Performance & Troubleshooting Tools

As a consultant within a Managed Services practice, I am often tasked to dig into ancient developer code in search of bottlenecks. Discovering performance issues on Sitecore builds can be tricky because each site you may work on follow different development patterns.

I found that the most common issues only surface after accumulating data, where some components may not have been tested using more than a few items of data.  The fix often ends up being some modification to specific Sitecore queries, or perhaps changing the way data is retrieved from Sitecore (switching to pull from a Lucene index).  Whatever it is, finding the bottleneck is just the first step.

Luckily for us, there are plenty of tools available that make troubleshooting and pin-pointing these issues a thousand times easier.  Regardless of Sitecore version you're working with, here are four performance / troubleshooting tools I often fire up from my arsenal to get to the bottom of slow load times.

SITECORE LOG ANALYZER

This is a given!  If something odd is going on in your Sitecore website, one of the first places to look for clues is the Sitecore logs.  This handy tool developed by Sitecore loads the entire Sitecore log folder and allows you to filter by date, string, message type (ERROR, INFO, DEBUG, etc).    This is often much easier than sifting through flat .txt files.



https://marketplace.sitecore.net/en/Modules/Sitecore_Log_Analyzer.aspx

SITECORE DEBUG TOOL

Built right into Sitecore - I only started utilizing this tool a few years into my career working with Sitecore, but it's been a life saver in a few instances.  My guess is that devs often forget that this exists.  Some features include full trace information for each rendering and profiling - which comes in handy when attempting to identify bottlenecks.  If you're trying to identify which rendering on a page are being pulled from the Sitecore cache, this is one of the best way to find out.

To enable the debugger, the following prerequisites must be satisfied:

  1. Ensure the "allowDebug" and "enableDebugger" attributes for the site are set to true in the site node of the Sites.config.
  2. Ensure you're logged into Sitecore with a user member utilizing the 'sitecore\Sitecore Client Developing' role.



Additional information on how to use the Sitecore debugger can still be found here:
The Sitecore Debugger

APACHE JMETER

Apache JMeter is an open-source Java application which (among other handy features) is great for running load/stress tests and providing the calculated results for analysis.  When configured properly can send multiple HTTP requests to a designated URL location and provides an average load time throughout the increased traffic.  For me, this has been a great way to obtain visualizations of page load time behaviors before and after making caching or other performance enhancement changes.



For more: https://www.digitalocean.com/community/tutorials/how-to-use-apache-jmeter-to-perform-load-testing-on-a-web-server 

 http://jmeter.apache.org/

TELERIK FIDDLER

I'm a big fan of Telerik's suite of developer tools.    Fiddler is one of those tools I always make sure to have installed on my dev box.  If you don't already know, Fiddler logs all HTTP/S traffic between your computer and the web.  This comes in handy when gathering statistics of internal or third-party calls, viewing client headers and/or cookies, viewing results of an API call and more.



http://www.telerik.com/fiddler

TELERIK JUSTTRACE

While JustTrace isn't free, it's definitely one the of easiest .NET profiling tools I've worked with.   Its intuitive tracing mechanism which allows us to identify which .NET components take the most CPU and memory resources which ultimately affect page load times.  In most cases the snapshots provide exact methods to look into and tweak until a more reasonable load time is obtained.




If you haven't tried these tools, I'd recommend playing around with them at some point.  They may become an integral part of your own troubleshooting whether during development or post-production support.  

Have you used any of these in the past?  What are your favorite performance troubleshooting tools?

Let me know in the comments!