What to do about the slow startup of web services
Due to the architecture of web services and web applications they can be quite slow to start. For example on my Windows 2003-box the initial localhost-call to a simple “Hello World!”-web service takes approximately 8 seconds, while the next request is more or less immediate.
Why is this?
This isn’t news, really. One of the first things we learned in the early beta stages of ASP.NET was that the first request would take a little extra time due to the Just In Time Compilation (JIT). This was a major change from the classic ASP architecture where everything was interpreted rather than compiled. Upon the first request the .aspx / .asmx file will be compiled (JIT’ed) into Microsoft Immediate Language (MSIL) and the resulting .dll will be moved to it’s proper location.
This compilation will occur every time the application pool starts causing a significantly longer response time on the first request compared to the following requests.
Run-time performance over startup performance
It all comes down to prioritizing run-time performance over a quick startup. Which, in my humble opinion, is a sound choice. There are, however, situations where you may feel that this is to your disadvantage. The other day I got a question from a customer who had a web service that was called quite infrequently and to him this meant that with every other request response times would be horrendous, simply because the application got recompiled.
For each case I get I usually do a quick search on the problem description. I don’t really expect to find the solution this way, since I can only assume that the customer has tried this as well. Instead it will likely present me with a list of troubleshooting steps that the customer has already tried as well as possible solutions that didn’t fit. (Off course I still need to verify this with the customer).
This time I didn’t really need to do this since I was pretty sure what the cause was. Still, I was curious to see how common this problem was. I honestly didn’t think this was a very common scenario, but my quick search on the web revealed the contrary. The few hits that I quickly browsed through suggested writing an application that would ping the web service in order to keep it alive. I must admit I find this quite inventive, but I really don’t think it’s the best approach. :)
What to do
So, what is to be done about this? Well, I’ve already mentioned that there are better solutions than pinging the web service. As I told my customer there are basically two good options:
- Precompile the assemblies
- Change the settings for automatic recycling on the worker process
Precompiling the assemblies
There is a nice little tool called Native Image Generator. (ngen.exe) It will compile the assembly into native images and installs them into the native image cache. The syntax is pretty straight-forward: ngen.exe install [assembly] The problem is that once you’ve changed the assembly you’d have to precompile it again making updates a bit tedious.
There is also a service-version of the ngen called the Native Image Service. More information about ngen can be found at https://msdn2.microsoft.com/sv-se/magazine/cc163808(en-us).aspx
Changing the recycling settings
The reason why my customer was experiencing this problem was because his application pool would time out and recycle itself due to the default settings in the IIS manager. By default the worker process will recycle after 20 minutes of inactivity, so if no one has pinged your application in that time the next request will cause the application to recompile, resulting in a longer response time. By tweaking this setting in the IIS manager to something more suitable to the current rate of requests, like 40 minutes or maybe even 60 he should also be able to find a nice balance. Actually, considering the low load it would probably be a good idea to turn this setting off completely. Here’s how:
- Open up IIS Manager
- Locate the Application Pool in question
- Right-click it and select “Properties”
- Go to the “Performance”-tab
- Change, or disable the “Idle timeout” setting