[asp.net] Fixing slow initial load for IIS

IIS has an annoying feature for low traffic websites where it recycles unused worker processes, causing the first user to the site after some time to get an extremely long delay (30+ seconds).

I've been looking for a solution to the problem and I've found these potential solutions.

A. Use the Application Initialization plugin

B. Use Auto-Start with .NET 4

C. Disable the idle-timeout (under IIS Reset)

D. Precompile the site

I'm wondering which of these is preferred, and more importantly, why are there so many solutions to the same problem? (My guess is they aren't, and I'm just not understanding something correctly).

Edit

Performing C seems to be enough to keep my site warmed up, but I've discovered that the real root of my site's slowness has to do with Entity Framework, which I can't seem to figure out why it's going cold. See this question, which unfortunately hasn't been answered yet has been answered!

I eventually just had to make a warm up script to hit my site occasionally to make sure it stayed speedy.

This question is related to asp.net .net-4.0 iis-7.5

The answer is


I was getting a consistent 15 second delay on the first request after 4 minutes of inactivity. My problem was that my app was using Windows Integrated Authentication to SQL Server and the service profile was in a different domain than the server. This caused a cross-domain authentication from IIS to SQL upon app initialization - and this was the real source of my delay. I changed to using a SQL login instead of windows authentication. The delay was immediately gone. I still have all the app initialization settings in place to help improve performance but they may have not been needed at all in my case.


A good option to ping the site on a schedule is to use Microsoft Flow, which is free for up to 750 "runs" per month. It is very easy to create a Flow that hits your site every hour to keep it warm. You can even work around their limit of 750 by creating a single flow with delays separating multiple hits of your site.

https://flow.microsoft.com


Web Hosting Challenge

You have to remember that none of the machine configuration options are available if you are hosted on a shared server as many of us (smaller companies and individuals) are.

ASP.NET MVC Overhead

My site takes at least 30 seconds when it hasn't been hit in over 20 minutes (and the web app has been stopped). It is terrible.

Another Way to Test Performance

There's another way to test if it is your ASP.NET MVC start up or something else. Drop a normal HTML page on your site where you can hit it directly.
If the problem is related to ASP.NET MVC start up then the HTML page will render almost immediately even when the web app hasn't been started.
That's how I first recognized that the problem was in the ASP.NET MVC startup. I loaded an HTML page at any time and it would load blazing fast. Then, after hitting that HTML page I'd hit one of my ASP.NET MVC URLs and I'd get the Chrome message "Waiting for raddev.us..."

Another Test With Helpful Script

After that I wrote a LINQPad (check out http://linqpad.net for more) script that would hit my web site every 8 minutes (less than the time for the app to unload -- which should be 20 minutes) and I let it run for hours.

While the script was running I hit my web site and every time my site came up blazingly fast. This gives me a good idea that most likely the slowness I was experiencing was because of ASP.NET MVC startup times.

Get LinqPad and you can run the following script -- just change the URL to your own and let it run and you can test this easily. Good luck.

NOTE: In LinqPad you'll need to press F4 and add a reference to System.Net to add the library which will retrieve your page.

ALSO : make sure you change the String URL variable to point at a URL that will load a route from your ASP.NET MVC site so the engine will run.

System.Timers.Timer webKeepAlive = new System.Timers.Timer();
Int64 counter = 0;
void Main()
{
    webKeepAlive.Interval = 5000;
    webKeepAlive.Elapsed += WebKeepAlive_Elapsed;
    webKeepAlive.Start();
}

private void WebKeepAlive_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    webKeepAlive.Stop();
    try
    {
        // ONLY the first time it retrieves the content it will print the string
        String finalHtml = GetWebContent();
        if (counter < 1)
        {
            Console.WriteLine(finalHtml);
        }
        counter++;
    }
    finally
    {
        webKeepAlive.Interval = 480000; // every 8 minutes
        webKeepAlive.Start();
    }
}

public String GetWebContent()
{
    try
    {
    String URL = "http://YOURURL.COM";
    WebRequest request = WebRequest.Create(URL);
    WebResponse response = request.GetResponse();
    Stream data = response.GetResponseStream();
    string html = String.Empty;
    using (StreamReader sr = new StreamReader(data))
    {
        html = sr.ReadToEnd();
    }
    Console.WriteLine (String.Format("{0} : success",DateTime.Now));
    return html;
    }
    catch (Exception ex)
    {
        Console.WriteLine (String.Format("{0} -- GetWebContent() : {1}",DateTime.Now,ex.Message));
        return "fail";
    }
}

See this article for tips on how to help performance issues. This includes both performance issues related to starting up, under the "cold start" section. Most of this will matter no matter what type of server you are using, locally or in production.

https://blogs.msdn.microsoft.com/b/mcsuksoldev/2011/01/19/common-performance-issues-on-asp-net-web-sites/

If the application deserializes anything from XML (and that includes web services…) make sure SGEN is run against all binaries involved in deseriaization and place the resulting DLLs in the Global Assembly Cache (GAC). This precompiles all the serialization objects used by the assemblies SGEN was run against and caches them in the resulting DLL. This can give huge time savings on the first deserialization (loading) of config files from disk and initial calls to web services. http://msdn.microsoft.com/en-us/library/bk3w6240(VS.80).aspx

If any IIS servers do not have outgoing access to the internet, turn off Certificate Revocation List (CRL) checking for Authenticode binaries by adding generatePublisherEvidence=”false” into machine.config. Otherwise every worker processes can hang for over 20 seconds during start-up while it times out trying to connect to the internet to obtain a CRL list. http://blogs.msdn.com/amolravande/archive/2008/07/20/startup-performance-disable-the-generatepublisherevidence-property.aspx

http://msdn.microsoft.com/en-us/library/bb629393.aspx

Consider using NGEN on all assemblies. However without careful use this doesn’t give much of a performance gain. This is because the base load addresses of all the binaries that are loaded by each process must be carefully set at build time to not overlap. If the binaries have to be rebased when they are loaded because of address clashes, almost all the performance gains of using NGEN will be lost. http://msdn.microsoft.com/en-us/magazine/cc163610.aspx


I'd use B because that in conjunction with worker process recycling means there'd only be a delay while it's recycling. This avoids the delay normally associated with initialization in response to the first request after idle. You also get to keep the benefits of recycling.


Writing a ping service/script to hit your idle website is rather a best way to go because you will have a complete control. Other options that you have mentioned would be available if you have leased a dedicated hosting box.

In a shared hosting space, warmup scripts are the best first level defense (self help is the best help). Here is an article which shares an idea on how to do it from your own web application.


Examples related to asp.net

RegisterStartupScript from code behind not working when Update Panel is used You must add a reference to assembly 'netstandard, Version=2.0.0.0 No authenticationScheme was specified, and there was no DefaultChallengeScheme found with default authentification and custom authorization How to use log4net in Asp.net core 2.0 Visual Studio 2017 error: Unable to start program, An operation is not legal in the current state How to create roles in ASP.NET Core and assign them to users? How to handle Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause() ASP.NET Core Web API Authentication Could not load file or assembly 'CrystalDecisions.ReportAppServer.CommLayer, Version=13.0.2000.0 WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for jquery

Examples related to .net-4.0

TLS 1.2 in .NET Framework 4.0 Is it possible to run a .NET 4.5 app on XP? What and When to use Tuple? Fixing slow initial load for IIS Exception: "URI formats are not supported" What is the best way to implement a "timer"? Twitter Bootstrap and ASP.NET GridView How can I convert this foreach code to Parallel.ForEach? "This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded" Differences between .NET 4.0 and .NET 4.5 in High level in .NET

Examples related to iis-7.5

The client and server cannot communicate, because they do not possess a common algorithm - ASP.NET C# IIS TLS 1.0 / 1.1 / 1.2 - Win32Exception Register .NET Framework 4.5 in IIS 7.5 HTTP Error 401.2 - Unauthorized You are not authorized to view this page due to invalid authentication headers IIS error, Unable to start debugging on the webserver How do I get to IIS Manager? How prevent CPU usage 100% because of worker process in iis How do I give ASP.NET permission to write to a folder in Windows 7? ASP.NET 4.5 has not been registered on the Web server Fixing slow initial load for IIS "Cannot verify access to path (C:\inetpub\wwwroot)", when adding a virtual directory