Orchard site is dog-slow! Moved to a new host still dog-slow. Client is hounding me!

Topics: Administration, Customizing Orchard, General, Installing Orchard
Apr 10, 2014 at 12:27 AM
The stage site is : http://barossa.sa.gov.au.web7.tempdomain.com.au/

Please wait for the mini profiler stats to load - the html is 6mb!! :/
http://stage-barossa-council-c3.dbgtechnologies.com/results.htm

OK there's the mini profiler stats, can't figure out why this site is so poorly performing. Can anyone give me some advice, I've been through the Web.Config etc.

Built on Orchard v.1.7.1.0.

Thanks.
Coordinator
Apr 10, 2014 at 12:33 AM
Can't see those 6MB you are talking about ... Would probably be because of Shape Tracing which you should disable.

Then you should look at what sql queries are done and if they make sense.
Apr 10, 2014 at 12:38 AM
Edited Apr 10, 2014 at 12:46 AM
Hi sebastien, the mini-profiler results file is 7mb!

Shape tracing isn't even enabled it kills the site with 'out of memory' errors.

So I have to rewrite the all the slow/duplicated queries? This site has visibility aged me! ha

This Orchard instance even runs slow locally.
Apr 10, 2014 at 12:50 AM
Edited Apr 10, 2014 at 12:52 AM
Hmm I'm thinking that it might be this Taxonomy Content Menu (custom) I'm using for navigation - a megamenu

.. any suggestions on how I can improve this please? It's taking a lot of time around the sorted dictionary...
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using C3.TaxonomyContentMenu.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
using Orchard.Core.Containers.Models;
using Orchard.Localization;
using Orchard.Taxonomies.Models;
using Orchard.Taxonomies.Services;
using Orchard.UI.Navigation;

namespace C3.TaxonomyContentMenu.Navigation
{
    /// <summary>
    /// Dynamically injects query results as menu items on NavigationQueryMenuItem elements
    /// </summary>
    public class TaxonomyContentMenuProvider : INavigationFilter
    {
        private readonly IContentManager _contentManager;
        private readonly ITaxonomyService _taxonomyService;

        public TaxonomyContentMenuProvider(
            IContentManager contentManager,
            ITaxonomyService taxonomyService)
        {
            _contentManager = contentManager;
            _taxonomyService = taxonomyService;
        }

        public IEnumerable<MenuItem> Filter(IEnumerable<MenuItem> items)
        {

            foreach (var item in items)
            {
                if (item.Content != null && item.Content.ContentItem.ContentType == "TaxonomyContentMenuItem")
                {
                    // expand query
                    var taxonomyNavigationPart = item.Content.As<TaxonomyContentMenuPart>();

                    var rootTerm = _taxonomyService.GetTerm(taxonomyNavigationPart.TermId);
                    
                    var allTerms = rootTerm != null
                                       ? _taxonomyService.GetChildren(rootTerm).ToList()
                                       : _taxonomyService.GetTerms(taxonomyNavigationPart.TaxonomyId).ToList().Where(x => x.Name.ToUpper() != "NONE"); //hide the None category

                    var menuPosition = item.Position;
                    var rootPath = rootTerm == null ? "" : rootTerm.FullPath;

                    var rootItemClasses = item.Classes;
                    rootItemClasses.Add("taxonomy-content-menu");

                    // Root term is taxonomy
                    if (rootTerm == null && taxonomyNavigationPart.DisplayRootTerm)
                    {
                        var taxonomy = _taxonomyService.GetTaxonomy(taxonomyNavigationPart.TaxonomyId);
                        yield return new MenuItem
                        {
                            Text = item.Text,
                            IdHint = item.IdHint,
                            Classes = rootItemClasses,
                            Url = item.Url,
                            Href = item.Href,
                            LinkToFirstChild = false,
                            RouteValues = _contentManager.GetItemMetadata(taxonomy.ContentItem).DisplayRouteValues,
                            LocalNav = item.LocalNav,
                            Items = new MenuItem[0],
                            Position = item.Position,
                            Permissions = item.Permissions,
                            Content = taxonomy.ContentItem
                        };
                        // Push terms into sub-menu
                        menuPosition += ".1";
                    }

                    foreach (var contentItem in allTerms.ToArray())
                    {
                        if (contentItem != null)
                        {
                            var part = contentItem;
                            var menuText = _contentManager.GetItemMetadata(part).DisplayText;
                            var routes = _contentManager.GetItemMetadata(part).DisplayRouteValues;
                            var positions = contentItem.FullPath.Substring(rootPath.Length).Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries).Select(x => allTerms.First(t => t.Id == Int32.Parse(x)).Weight).ToArray();

                            // Get all items for Term
                            var childItems = _taxonomyService.GetContentItemsQuery(part)
                                .Where(x => x.Terms.Any(t => t.TermRecord.Id == part.Id))
                                .List()
                                .ToArray();

                            var childItemsMeta = childItems.Select(childItem => _contentManager.GetItemMetadata(childItem)).ToArray();

                            var childItemsDictionary = new Dictionary<IContent, ContentItemMetadata>();

                            for (var i = 0; i < childItems.Length; i++)
                            {
                                childItemsDictionary.Add(childItems[i], childItemsMeta[i]);
                            }

                            var sortedDict = childItemsDictionary.ToList();
                            sortedDict.Sort((firstPair, nextPair) =>
                            {
                                var firstPairVal = 1;
                                var nextPairVal = 1;
                                try
                                {
                                    firstPairVal = Convert.ToInt32(((dynamic)firstPair.Key.ContentItem).Page.Weight.Value);
                                }
                                catch (Exception e) { }
                                try
                                {
                                    firstPairVal = Convert.ToInt32(((dynamic)firstPair.Key.ContentItem).Event.Weight.Value);
                                }
                                catch (Exception e) { }

                                try
                                {
                                    nextPairVal = Convert.ToInt32(((dynamic)nextPair.Key.ContentItem).Page.Weight.Value);
                                }
                                catch (Exception e) { }
                                try
                                {
                                    nextPairVal = Convert.ToInt32(((dynamic)nextPair.Key.ContentItem).Event.Weight.Value);
                                }
                                catch (Exception e) { }

                                return nextPairVal.CompareTo(firstPairVal);
                                //return firstPairVal.CompareTo(nextPairVal);
                            }
                            );

                            // Build Sub Menu
                            var childItemsMenu = new List<MenuItem>();
                            foreach (var child in sortedDict)
                            {
                                var childMeta = child.Value;
                                childItemsMenu.Add(new MenuItem
                                {
                                    Text = new LocalizedString(childMeta.DisplayText),
                                    RouteValues = childMeta.DisplayRouteValues,
                                    LinkToFirstChild = false,
                                    LocalNav = true,
                                    Items = new MenuItem[0],
                                    Position = menuPosition + ":" + String.Join(".", positions.Select(x => x.ToString()).ToArray()),
                                    Permissions = item.Permissions,
                                    Content = child.Key
                                });
                            }
                            var inserted = new MenuItem
                            {
                                Text = new LocalizedString(menuText),
                                Classes = item.Classes,
                                IdHint = item.IdHint,
                                Url = item.Url,
                                Href = item.Href,
                                LinkToFirstChild = false,
                                RouteValues = routes,
                                LocalNav = item.LocalNav,
                                Items = childItemsMenu.ToArray(),
                                Position = menuPosition + ":" + String.Join(".", positions.Select(x => x.ToString()).ToArray()),
                                Permissions = item.Permissions,
                                Content = part
                            };

                            yield return inserted;
                        }
                    }
                }
                else
                {
                    yield return item;
                }
            }
        }
    }
}
Coordinator
Apr 10, 2014 at 1:09 AM
Yeah custom code is usually a good think to look at first, no offense, we've all been there.

Why don't you like with the default navigation providers for taxonomy ?

Another optimization might be to actually load all Terms in memory first, then NHibernate would not issue SELECT N+1.

And the ultimate solution is to cache your menu, or just enable the output cache module.
Apr 10, 2014 at 1:44 AM
Edited Apr 10, 2014 at 5:04 AM
The menu brings back custom content types like events as well as pages. This code is a throwback from 1.5... I inherited this site and this code is in a custom module : TaxonomyContentMenu

Output cache module is already enabled.

I wasn't aware that Orchard can taxonomy content menus like this out of the box?
Apr 30, 2014 at 2:11 AM
OK I've resolved this issue by installing this module:

https://navigationcache.codeplex.com/

Great work to the guys who created this navigation cache - I would recommend it to anyone using menus driven by taxonomies - reduced my page loads by about 10x

This has really helped out.. cheers!
Marked as answer by beebul on 4/29/2014 at 6:17 PM
Coordinator
Apr 30, 2014 at 6:57 PM
I don't disagree with local widget caching (partial caching), but it's usually not an issue if you can do a full page cache.

In some cases though you still need to get dynamic content then partial caching is the way to go.