Performance testing nopCommerce 3.70 on Azure using Redis + Blob + Azure SQL

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
7 years ago
Not sure what is the problem with multiple repositories. Until you work with non-materialized LINQ queries returning IQueriable<T> you can call Include() methods to preload (don't forget to add "usnig using System.Data.Entity;").
Moreover, I checked shortly the source code and it seems nop already has build it helpers to call this preload:


namespace Nop.Data
{
    /// <summary>
    /// Queryable extensions
    /// </summary>
    public static class QueryableExtensions
    {
        /// <summary>
        /// Include
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="queryable">Queryable</param>
        /// <param name="includeProperties">A list of properties to include</param>
        /// <returns>New queryable</returns>
        public static IQueryable<T> IncludeProperties<T>(this IQueryable<T> queryable,
            params Expression<Func<T, object>>[] includeProperties)
        {
            if (queryable == null)
                throw new ArgumentNullException("queryable");

            foreach (Expression<Func<T, object>> includeProperty in includeProperties)
                queryable = queryable.Include(includeProperty);

            return queryable;
        }

    }
}


So you can easily optimize your queries with something like:


var query = (from pc in _productCategoryRepository.Table
                            join p in _productRepository.Table on pc.ProductId equals p.Id
                            where pc.CategoryId == categoryId &&
                                  !p.Deleted &&
                                  (showHidden || p.Published)
                            orderby pc.DisplayOrder, pc.Id
                            select pc)
            .IncludeProperties(x => x => x.Product.ProductPictures);


which is equivalent of native LINQ to Entities

var query = (from pc in _productCategoryRepository.Table
                            join p in _productRepository.Table on pc.ProductId equals p.Id
                            where pc.CategoryId == categoryId &&
                                  !p.Deleted &&
                                  (showHidden || p.Published)
                            orderby pc.DisplayOrder, pc.Id
                            select pc)
            .Include(x => x.Product)
            .Include(x => x.Product.Select(y => y.ProductPictures))
;



pdunleavy75 wrote:
The category listings and home page would be the main ones to optimise. I'm not sure if eager loading will work considering the repository per entity design pattern used. I think custom LINQ queries or stored procedures that project the data directly into a viewmodel is the way to go. Entity framework is great for line of business CRUD applications (admin), the front end of an e-commerce site is not one of these situations.
7 years ago
They also have a nice feature in IRepository


/// <summary>
/// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
/// </summary>

IQueryable<T> TableNoTracking { get; }


which sould also help load entities faster (for read only / browsing purposes) but I don't see they use it! I could only find 3 places they access with property.

I assume there is big work ahead rewriting all read only queries using this property + calling Include method to make pages less DB chatty. I don't see big problem to optimize existing code to use these two features if you already identified pain points.

Edit: just noticed the thread is about 3.70 version when it was announced big performance improvement in 3.80. I've looked source code from v3.90
7 years ago
mrx wrote:
They also have a nice feature in IRepository


/// <summary>
/// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
/// </summary>

IQueryable<T> TableNoTracking { get; }


which sould also help load entities faster (for read only / browsing purposes) but I don't see they use it! I could only find 3 places they access with property.

I assume there is big work ahead rewriting all read only queries using this property + calling Include method to make pages less DB chatty. I don't see big problem to optimize existing code to use these two features if you already identified pain points.

Edit: just noticed the thread is about 3.70 version when it was announced big performance improvement in 3.80. I've looked source code from v3.90


Was it improved for 3.8\3.9 then?
7 years ago
From what I see in git history these improvements there are for ages:
.IncludeProperties() since 10/10/2014
.NoTrackingTable even few months longer

I really wonder why they don't rewrite existing queries to take advantages of this features and don't even use them for new functionality (in past 3 years only 3 references to nontracking tables and ZERO to IncludeProperties)

I guess the performance is just not the priority of development team. It's funny - people ask them for async support when they don't even use ready to facilitate performance optimizations.
7 years ago
And we still lack a response from the dev team in this thread. Why is that?

I did contact MS azure about the performance, and they were more than willing to help optimize nopCommerce for azure if the nop team would contact them.
7 years ago
mrx wrote:
It's funny - people ask them for async support when they don't even use ready to facilitate performance optimizations.


Async and performance improvements are two separate things. Using async won't improve performance. What it'll do is increase the number of potential concurrent requests. But it's true that at this point, increasing a lot the performance of a single request would help increase the number of concurrent users, basically because the number of concurrent requests would be reduced a lot too.
7 years ago
If you are hosting Azure SQL DB data tier switch to SQL hosted on the server. Unless you want to spend a fortune on much higher Azure SQL tier switching to SQL hosted on the VM increased our performance 7 fold.
7 years ago
So, where there is a "_cacheManager.Get(key, () =>" (cache use) the table access should be .TableNoTracking instead .Table, right?
7 years ago
Greg Smyth wrote:
If you are hosting Azure SQL DB data tier switch to SQL hosted on the server. Unless you want to spend a fortune on much higher Azure SQL tier switching to SQL hosted on the VM increased our performance 7 fold.


Greg, so do you mean to host both the nopcommerce Web app and SQL server on the same VM?

We did this and yes, the performance is much better, but you miss out on scaling flexibility like autoscaling.
7 years ago
iob2000 wrote:
So, where there is a "_cacheManager.Get(key, () =>" (cache use) the table access should be .TableNoTracking instead .Table, right?


I propose using TableNoTracking at least inside so called "services" located in Nop.Services. Of course you need to proof that returned sets are only used for displaying data on a page, not editing. If it used for modifications, then you need a copy of query, using Table with tracked changes.

for instance inside calls like here:


_aclService.GetCustomerRoleIdsWithAccess(category).ToList();


If you open implementation, you see it reads data using tracking changes enabled:

return _cacheManager.Get(key, () =>
            {
                var query = from ur in _aclRecordRepository.Table
                            where ur.EntityId == entityId &&
                            ur.EntityName == entityName
                            select ur.CustomerRoleId;
                return query.ToArray();
            });


so, you can replace this Table propertty call. And even use preloads if you know what you'll need in advance.
But i repeat - you may change it to non tracking only after investigating all calls of this method and making sure nobody uses it for updates.

Considering there are known few pain points like home and categories/catalogs/acls even creating duplicated functionality for queries with and without tracking changes doesn't seem a big deal.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.