Speed up catalog routing if you have multiple children under catalog

A normal catalog structure is like this: you have a few high level categories under the catalog, then each high level category has a few lower level categories under it, then each lower level category has their children, so on and so forth until you reach the leaves – catalog entries.

However it is not uncommon that you have multiple children (categories and entries) directly under catalog. Even though that is not something you should do, it happens. 

But that is not without drawbacks. You might notice it is slow to route to a product. It might not be visible to naked eyes, but if you use some decent profilers (which I personally recommend dotTrace), it can be fairly obvious that your site is not routing optimally.

Why?

To route to a specific catalog content, for example http://commerceref/en/fashion/mens/mens-shirts/p-39101253/, the default router have to figure out which content is mapped to an url segment. So with default registration where the catalog root is the default routing root, we will start with the catalog which maps to the first part of route (fashion ). How do it figure out which content to route for the next part (mens ) ? 

Until recently, what it does it to call GetChildren on the catalog ContentReference . Now you can see the problem. Even with a cached result, that is still too much – GetChildren with a big number of children is definitely expensive.

We noticed this behavior, thanks to Erik Norberg. An improvement have been made in Commerce 12.10 to make sure even with a number of children directly under Catalog, the router should perform adequately efficient.

If you can’t upgrade to 12.10 or later (you should!), then you might have a workaround that improve the performance. By adding your own implementation of HierarchicalCatalogPartialRouter, you can override how you would get the children content – by using a more lightweight method (GetBySegment)

    public class CustomHierarchicalCatalogPartialRouter : HierarchicalCatalogPartialRouter
    {
        private readonly IContentLoader _contentLoader;

        public CustomHierarchicalCatalogPartialRouter(Func<ContentReference> routeStartingPoint, CatalogContentBase commerceRoot, bool enableOutgoingSeoUri) : base(routeStartingPoint, commerceRoot, enableOutgoingSeoUri)
        {
        }

        public CustomHierarchicalCatalogPartialRouter(Func<ContentReference> routeStartingPoint, CatalogContentBase commerceRoot, bool supportSeoUri, IContentLoader contentLoader, IRoutingSegmentLoader routingSegmentLoader, IContentVersionRepository contentVersionRepository, IUrlSegmentRouter urlSegmentRouter, IContentLanguageSettingsHandler contentLanguageSettingsHandler, ServiceAccessor<HttpContextBase> httpContextAccessor) : base(routeStartingPoint, commerceRoot, supportSeoUri, contentLoader, routingSegmentLoader, contentVersionRepository, urlSegmentRouter, contentLanguageSettingsHandler, httpContextAccessor)
        {
            _contentLoader = contentLoader;
        }

        protected override CatalogContentBase FindNextContentInSegmentPair(CatalogContentBase catalogContent, SegmentPair segmentPair, SegmentContext segmentContext, CultureInfo cultureInfo)
        {
            return _contentLoader.GetBySegment(catalogContent.ContentLink, segmentPair.Next, cultureInfo) as CatalogContentBase;
        }
    }

And then instead of using CatalogRouteHelper.MapDefaultHierarchialRouter , you register your router directly

 var referenceConverter = ServiceLocator.Current.GetInstance<ReferenceConverter>();
            var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
            var commerceRootContent = contentLoader.Get<CatalogContentBase>(referenceConverter.GetRootLink());
            routes.RegisterPartialRouter(new HierarchicalCatalogPartialRouter(startingPoint, commerceRootContent, enableOutgoingSeoUri));

(ServiceLocator is just to make it easier to understand the code. You should do this in an IInitializationModule, so use context.Locate.Advanced instead.

This is applicable from 9.2.0 and newer versions. 

Moral of the story:

  • Catalog structure can play a big role when it comes to performance.
  • You should do profiling whenever you can
  • We do that too, and we make sure to include improvements in later versions, so keeping your website up to date is a good way to tune performance.

Refactoring Commerce catalog code, a story

It is not a secret that I am a fan of refactoring. Clean. shorter, simpler code is always better. It’s always a pleasure to delete some code while keeping all functionalities: less code means less possible bugs, and less places to change when you have to change.

However, while refactoring can bring a lot of enjoying to the one who actually does it, it’s very hard to share the experience: most of the cases it’s very specific and the problem itself is not that interesting to the outside world. This story is an exception because it might be helpful/useful for other Commerce developer.

Continue reading “Refactoring Commerce catalog code, a story”

Commerce batching performance – part 2: Loading prices and inventories

UPDATE: When looked into it, I realize that I have a lazy loading collection of entry codes, so each test had to spent time to resolve the entry code(s) from the content links. That actually costs quite a lot of time, and therefore causing the performance tests to return incorrect results. That was corrected and the results are now updated.

In previous post we talked about how loading orders in batch can actually improve your website performance, and we came to a conclusion that 1000-3000 orders per batch probably yields the best performance result.

But orders are not the only thing you would need to load on your website. A more common scenario is to load prices and inventories for product. So If you are displaying a product listing page, it’s quite common to load prices and inventories for all products in that page. How should it be loaded?

Continue reading “Commerce batching performance – part 2: Loading prices and inventories”

Speed up your catalog indexing performance – part 2

Almost two years ago I wrote part 1 here: https://vimvq1987.com/speed-catalog-entries-indexing/ on how to speed up your catalog indexing performance. If you have a fairly big catalog with frequent changes, it might take longer time than necessary to build the index incrementally. (Rebuild index, in other hands, just delete everything and rebuild from scratch, so it is not affected by the long queue in ApplicationLog). I have seen some cases where rebuilding the entire index, is actually faster than waiting for it to build incrementally.

The tip in previous blog post should work very well if you are using anything lower than Commerce 11.6, but that is no longer the case!

Continue reading “Speed up your catalog indexing performance – part 2”

Getting all non published variations

I got a question from a colleague today: A customer has multiple languages (8 of them). They need to make sure all variants are published in all languages. That is of course a reasonable request, but there is no feature builtin for such requirement. But good news is that can be done with ease. If you want to try this as practice, go ahead – I think it’s a good exercise for your Episerver Commerce-fu skills.

To do this task, we need the snippet to traverse the catalog from here https://leanpub.com/epicommercerecipes/read_sample

Continue reading “Getting all non published variations”

Multiple catalogs: Without catalog name(s)

My previous blog posts about multiple catalogs in multiple sites setting here and here has helped one customer (hopefully more) to address their problem. They tried the approach and it works for them. Now they came back and ask if they can remove the catalog name from the URL entirely. Is that something that can be done. Fortunately, yes.

Before moving on, let’s be clear: this is obviously custom development and is not officially supported by Episerver. I.e. it’s neither tested and documented. Normally you would have to go to Expert Services for such requirement. For this blog, I provide those samples for free (you don’t even have to buy my book 😉 ), but remember I can only do that much. You have to test to see if it works for you (I of course do the basic testing), and if you run into problems later, I might not be able/available to help.

Now get back to the problem. Previously the router looks like this

    
protected override CatalogContentBase FindNextContentInSegmentPair(CatalogContentBase catalogContent, SegmentPair segmentPair,
        SegmentContext segmentContext, CultureInfo cultureInfo)
    {
        if (catalogContent.ContentType == CatalogContentType.Root)
        {
            CatalogContent definedCatalogContent;
            var definedCatalogLink = _contentLoader.Get(RouteStartingPoint).CatalogLink;
            if (_contentLoader.TryGet(definedCatalogLink, cultureInfo, out definedCatalogContent))
            {
                return definedCatalogContent;
            }
        }
        return base.FindNextContentInSegmentPair(catalogContent, segmentPair, segmentContext, cultureInfo);
    }

And it works great with  http://commerceref/en/fashion/mens/mens-shoes/p-36127195/ or http://commerceref/en/products/mens/mens-shoes/p-36127195/. But now they want to completely remove the catalog name from the url. They can’t make it works with http://commerceref/en/mens/mens-shoes/p-36127195/.

Why? Because the router is returning the catalog for /mens/ segment. In next segment (mens-shoes), we fallback to the default implementation, which can’t find any matching content as children of that definedCatalogContent .

The solution: Because we want the first-level category to appear as direct child of the catalog root, we would have to return it ourselves. Instead of returning the catalog, we return the first node that matches /mens/ segment.

        protected override CatalogContentBase FindNextContentInSegmentPair(CatalogContentBase catalogContent, SegmentPair segmentPair,
            SegmentContext segmentContext, CultureInfo cultureInfo)
        {
            if (catalogContent.ContentType == CatalogContentType.Root)
            {
                CatalogContent definedCatalogContent;
                var definedCatalogLink = _contentLoader.Get<StartPage>(RouteStartingPoint).CatalogLink;
                if (_contentLoader.TryGet<CatalogContent>(definedCatalogLink, cultureInfo, out definedCatalogContent))
                {
                    var nodes = _contentLoader.GetChildren<NodeContent>(definedCatalogLink);
                    return nodes.FirstOrDefault(n => n.RouteSegment.Equals(segmentPair.Next, StringComparison.OrdinalIgnoreCase));
                }

                return null;
            }
            return base.FindNextContentInSegmentPair(catalogContent, segmentPair, segmentContext, cultureInfo);
        }

And it just works:

Now that’s just one part of the solution. We still need to make sure outgoing URLs respect that. I’ll leave that to you, as practice.

Multiple sites: Building the outgoing URLs

In previous recipe we talked about multiple catalogs with same “UriSegment” – which we had a working implementation for incoming URL, i.e. when a customer visit a product url, we know which catalog we should choose from. But we still need to cover the generation of outgoing URL. I.e. when we link a product (For example, from a campaign page), we need to generate an URL which take the “catalog-less” pattern into account.

We need to understand how the outgoing URL is built. The hierarchical router builds the URL by the `RouteSegment` of contents. However, we want to the urls appear to have same catalog, so the `RouteSegment` part for the catalogs must be the same, regardless of the true catalogs. Because all catalogs are on same level, their `RouteSegment` must be unique – and this is enforced from Framework level (which is understandable, otherwise, how can it know which content to choose).

Continue reading “Multiple sites: Building the outgoing URLs”

Multiple catalogs with same url

This is an excerpt from my second book . The first chapter is available to read for free.

A business is having an Episerver Commerce instance with multiple sites and multiple catalogs set up. They want to make sure each site will use one catalog, and all of them will share the same url for catalog structure. So it’ll be “https://site-a.com/products/category/”, and “https://site-b.com/products/category/”. Site A and site B are using different catalogs.

Is this doable? Yes! It’s just a matter of magic with the routing. This time, we would need to do an implementation of HierarchicalCatalogPartialRouter ourselves. First, let’s create a template for it:

    
public class MultipleSiteCatalogPartialRouter : HierarchicalCatalogPartialRouter
    {
        private readonly IContentLoader _contentLoader;

        public MultipleSiteCatalogPartialRouter(Func routeStartingPoint, CatalogContentBase commerceRoot, bool enableOutgoingSeoUri, IContentLoader contentLoader) 
            : base(routeStartingPoint, commerceRoot, enableOutgoingSeoUri)
        {
            _contentLoader = contentLoader;
        }
    }

Continue reading “Multiple catalogs with same url”

Speed up your Catalog incremental indexing

As your products are being constantly updated, you would naturally want them to be properly (and timely) indexed – as that’s crucial to have the search results that would influence your customers into buying stuffs. For example, if you just drop the prices of your products , you would want those products to appear in new price segment as soon as possible.

This should be very easy with Find.Commerce – so if you are using Find (which you should) – stop reading, nothing for you here. Things, however, can be more complicated if you are using the more “traditional” SearchProvider.

Continue reading “Speed up your Catalog incremental indexing”

Mass update catalog entries

This is something you don’t do daily, but you will probably need one day, so it might come in handy.

Recently we got a question on how to update the code of all entries in the catalog. This is interesting, because even thought you don’t update the codes that often (if at all, as the code is the identity to identify the entries with external system, such as ERPs or PIMs), it raises a question on how to do mass update on catalog entries.

    • Update the code directly via database query. It is supposedly the fastest to do such thing. If you have been following my posts closely, you must be familiar with my note regarding how Episerver does not disclose the database schema. I list it here because it’s an option, but not the good one. It easily goes wrong (and cause catastrophes), you have to deal with versions and cache, and those can be hairy to get right. Direct data manipulation should be only used as the last resort when no other option is available.

Continue reading “Mass update catalog entries”