Performance Tuning and Optimization of EPiServer

There are many factors to look at when you want to tune the performance of your EPiServer site. This is the first post about common pitfalls and how to optimize EPiServer for performance.

EPiServer and Memory

First thing to check is that you have enough memory. EPiServer needs a lot of memory to cache the PageData object for each of all active pages on the site. The larger site (more pages) you have, the more memory you will need.

The DataFactory class will first look if the requested page or list of pages is in the cache when you call GetPage() or GetChildren(). If it is not in the cache, the page or list will be loaded from the database and added to the cache. The default implementation (read more about pluggable runtime cache) uses ASP.NET’s built-in public cache to store pages and lists in memory.

EPiServer Database Cache UsageThe public cache in ASP.NET is also used to store other data in addition to EPiServer PageData and Lists. Periodically ASP.NET will use a Least Recently Used (LRU) algorithm to scavenge the cache for more space. If the memory pressure is very high the lifetime of cached items can be very short or even zero (not added at all). This is not good for EPiServer’s performance.

It is easy to see what’s inside your cache:

protected void Page_Load(object sender, EventArgs e)
{
    foreach (DictionaryEntry d in this.Cache)
    {
        Response.Write(d.Key);
        Response.Write(" - ");
        Response.Write(d.Value);
        Response.Write("<hr>");
    }
}

A very good indicator if Memory is an issue is to look at some statistics from the DataFactory class. You can see the Cache hit rate if you go to admin mode. It should be close to 100% or maybe a few percent below. (It is normal that it drops to zero when you restart the site but it should climb fast.) A constant low value is one indicator that you need more memory because PageData objects are thrown out of the cache and then they must be reloaded from the database.Add EPiServer Performance CountersYou can also see the Cache Hit Ratio (and some other counters) through Windows Performance Monitor. This is a good value to monitor in a hosting environment and add an alert when the value becomes to low.

ASP.NET Worker Process / Application Pool

The maximum memory ASP.NET allows a worker process to consume before it is recycled is by default 60% of the total system memory. Read more about memoryLimit tag.

<system.web>
<processModel>
<memoryLimit>60</memoryLimit>
</processModel>
</system.web>

Default Application Pool – Do not use it and do NOT delete it (even if you plan to never use it). Create your own pool instead but not create one pool for each site on the web server. Instead consolidate your applications into one pool and only use a separate pool for troublesome web applications. You must have separate pools for ASP.NET 1.1 and 2.0 applications though.

Application Pool RecyclingYou can also configure the application pool to recycle if it consumes to much memory. If you worker process constantly recycles you need to find out why because EPiServer will always be a little bit sluggish after a restart (before data is cached again). You also loose all in-process session data when the process restarts which is an issue of its own.

Settings: I recommend that you uncheck “Recycle worker process (in minutes)” and use “Recycle worker process at the following times” instead.

Application Pool Performance You should also uncheck “Shutdown worker process after being idle”. This is very important for EPiServer sites that has a low traffic (for example during the night).

Do not use Web Garden unless you know what you are doing. It will require double memory for the cache and out of process state-handling. If you change the number of worker processes from 1 to 2 it is like having two web servers on the same box with IIS acting like load balancer. (It is a cheap way in a development environment to test how you application will behave in a farm with load balancing.)

MSSQL Server and EPiServer

MSSQL Server Properties MemoryIts a best practice to have two machines, one for the web server and one for the database. But sometimes you have to run the database on the same machine as the web server. If you do, it is important to remeber that the MSSQL Server competes with the Web Server for resources like memory. You should always limit the amount of memory the database can use because it is better for EPiServer to have data cached in the web application than in the database.

Usually the usage of the database is low (around 5% CPU) in EPiServer scenarios because EPiServer tries to cache what it needs in the web application. If you have a higher usage of the database this can be an indication of that caching does not work as it should in the web application but it could also indicate that you do something else wrong. (More about database issues in a later post.)

Other processes

Finally, do not forget to have use the ordinary task manager to check if you have other processes that compete for the memory on your web server.

Bookmark and Share

Tags: , , , , , , , , ,

  1. Björn Gullander’s avatar

    Good article that handles some of these assembly loading problems that can be a horror to resolve.

    Is there any case when the bind redirect does not work? I have a case where a 3rd-party component uses version 4.61.0.83 of Episerver.dll but in our codebase we use version 4.61.3.83.

    We have added an assembly redirect in the web.config but still we get the yellow screen telling that the 3rd-party application wants the earlier version.

    Is there any other way to force the 3rd-party application to use version 4.61.3.83 ?

    Or is my only solution to ask for a new dll referencing the correct version from our partner?

    Reply

  2. Patrik Almqvist’s avatar

    Great article, though I have a question.. You write about DefaultApplictionPool: “Do not use it and do NOT delete it (even if you plan to never use it). Create your own pool instead…” bur you do not explain why!? Maybe it’s obvious, and just lack of knowledge from my side, but would very much like to know why.

    Reply

    1. Fredrik Haglund’s avatar

      Ah… sorry! The reason is that there are installers that expect that both the Default Application Pool and the Default Web Site exist, otherwise they fail. There are olso a risk that an installer modifies the first pool or web site. So that is why you should leave them in place and not use them.

      If you used Cassini in Visual Studio for debugging it did also picks up settings from the default web site that affects the site you are debugging.

      Reply

  3. Patrik Almqvist’s avatar

    Thanks a lot!!

    Reply

  4. Marco ter Horst’s avatar

    Hi Fredrik,

    in the blog you state: ” … Do not use Web Garden unless you know what you are doing. …”

    In fact, it’s not quite so difficult to implement a Web Garden with EpiServer. All thanks to Microsoft’s own ASP.NET State Service.

    The main problem with Web Gardens and EpiServer is session state information.
    E.g. if you log on to the CMS while your request is being handled by IIS worker process (A) and perform an action in the CMS which is handled by IIS worker process (B), you will get prompted to login again.
    Not really a workable environment.

    With the ASP.NET State Service it’s fairly simple to handle session information outside of the IIS worker processes. It only requires the service to be started (it ships with Windows Server out of the box) and the web.config to be altered to use it (See: http://technet.microsoft.com/nl-nl/library/cc732412(WS.10).aspx ).

    With this change implemented, enabling a Web Garden is a piece of cake really ;-)

    Hope this helps!

    Reply

    1. Fredrik Haglund’s avatar

      Yes, this is true but there is also the fact that EPiServer caches all page data in memory.

      If you enable 2 or more instances they will start compeeting about memory. From a performance perspective it is probably better to scale out and use load balancing

      Reply

    2. Marco ter Horst’s avatar

      Just to update those of you who are interested in this topic:
      We’ve been running a Web Garden now for almost 2 1/2 months now on our production system and we’re very happy with the result and the performance and capacity boost we gained from it without having to buy new resources.
      This is our setup:
      1 virtual server with 4CPU cores and 8GB of memory
      1 App Pool for web users configured to use 3 max. worker processes
      1 App Pool for CMS users configured to use 1 max. worker process
      Both pools are configured to recycle @ 950000KB memory usage as we saw that the website becomes instable @ values above this (regardless of Web Garden setup or not.
      As you can see, with 8GB of RAM, memory is not really the issue in our setup.

      Reply

      1. Fredrik Haglund’s avatar

        Ok, glad to hear that it works well for you.

        Why it solves your performance issue is another question.

        You will get the same items cached several times in memory, you get the complexity to setup EPiServer’s cache invalidation (propagating publish events between the instances), setup ASP.NET State handling and lose performance to serialization instead and finally you will probably have to buy load balancing licenses (not sure about that). So I would not recommend it unless you have a real need.

        If you have a lot of requests for static content like images investigate if Kernel Mode Caching can help you to improve your performance.

        One benefit I can see is that when you have more than one worker process running it can scale better to use your other CPU cores but asp.net applications usually scales well so this might be an indication of thread blocking code?

        Reply