A list of common performance mistakes when development with EPiServer and ASP.NET for larger sites with a lot of page request and many pages. The list is ordered by the most frequent errors at the top.
Don't connect dynamic content to static event handlers
When you connect static event handlers to dynamic content, the GC cannot remove those objects and the site will have a memory leek.
The most common memory leek in EPiServer is that static events from DataFactory.Instance (sense this instance is created one, it is considered as static) are connected to in Global.asax (that is considered as dynamic content sense it will be created a new instance of the application if a soft reset is done). And this will cause a memory leek over time.
Don’t hit the database in each request
If the database is hit on each request the database will get a high load.
Use Find pages with criteria wisely
The method Datafactory.Instance.FindPagesWithCriteria has some issues like
Going to hit the database each time it is invoked. And these questions is going to be heavy for a larger site sense it can result in table scans in the tblProperty table.
Doesn’t scale – it is designed to deliver all find links without any paging (skip, take and order by is missing), so all find page links pages has to be created in memory before it deliver its result.
For smaller sites (with number of pages below 10’000, and a good hardware) this dosen’t need to be an issue, but be aware of the problem.
For larger sites that needs to look for pages in the whole structure I recommend to use a external search index.
Don't execute code that should not execute
The most common scenario is that the correct logging level is not checked before building the log message
Example
private readonly ILog _logger = LogManager.GetLogger(typeof(MyClass));
...
_log.Debug("STATUS = " + statusMessage +
" : Name = " + page.PageName +
" Property Name = " + propertyName +
", ID = " + page.PageLink.ID +
", Old URL = " + oldUrl + ", New URL = " + newUrl);
The snippet of code above will access four possibly slow methods with the risk of generating an unnecessary Null Reference Exception, nine String objects, call three methods; create a log object; and throw away the result.
A better implementation would be to check if the result should be generated first
private readonly ILog _logger = LogManager.GetLogger(typeof(MyClass));
...
if (_log.IsInfoEnabled)
{
_log.InfoFormat(e, "STATUS = {0} : Name = {1} Property Name = {2}, ID = {3}, Old URL = {4}, New URL = {5}”,
statusMessage,
page.PageName,
propertyName,
page.PageLink.ID,
oldUrl,
newUrl);
}
This is very common that content on a web page is generated but not showed, for example in tab views.
Use a simple 404 page
When path to content can't be resolved by ASP.NET it going to use the error page in the solution, both for dynamic and static content. If the website has any code running for all missing content it going to take load from the server, it is better to deliver a static 404 page so the server doesn't have to work for each missed request.
If there is any requirements of having a better user experience for this static HTML page the page can be generated automatically by a scheduled job, then the server only needs to render this 404 page each time the scheduler job runs, and deliver the generated static 404 page for all missed requests.
Use a simpler 500 page
Since this error page will be generated if the application gets overloaded it is a bad idea to put extra load on the server.
This error should never appear; so to use a very static simple error page with just an error message and a link to the first page of the site should be enough.
Cache empty/null values and errors
If empty/null values is not cached the query will be asked on each request to show noting-
Use correct Virtual path provider
There exists an open-source module called "CdnSupport" that works with EPiServer Virtual Path Versioning Provider and expose the files with a unique URL for each version of the file, then the cache time can be set high (normally to 30 days) for those files.
See
Virtual Path Providers3rd part system
Ensure that only one request accessing 3rd part system, this can be done by using a cached proxy when communicate with 3rd part system, or preload the data with a scheduled job.
Classes implementing IDisposable should be used in a "using" block
See
IDisposable patternDon't attach to events that are used a lot
DataFactorys PageLoad events are going to be accessed each time a page is loaded and can make a listing in EPiServer take very long time.
Regular expressions
When using regular expression, don't create them for each request. Use instead a static field with a precompiled expression(RegexOptions.Compiled option).
Locking
Locking can be an issue in both low CPU hangs (some functionality locking other so it can’t deliver data) and high CPU usage (the context switching for many threads can take a lot of CPU).
Statistics
Statistics generates a lot of traffic that has to be aggregated, both over time and over the number of pages. The EPiServer log service sample analyzer (TimeSpanAnalyzer shows an example how to implement aggregation over time, but misses to aggregate over number of pages – if you going to use it rewrite it so it dosen’t use Object store)