Working with HTTP.SYS or Kernel Mode Caching in Internet Information Services 6.0

Caching has always been one of my favorite areas. Although, I never attempted to look much into how various settings in IIS or HTTP affect caching, I always had it on my to do list which was pushed down the priority list for several months. Now, that I have a good break from my cases, I am spending some time deep diving into how caching works with IIS, specifically how HTTP Kernel mode caching and User mode caching in IIS can be exploited to get better performance from an IIS 6.0 web server.

Before I begin, I want to mention that there are many parameters that affect caching and all of it depends on what type of content is being served by IIS. Depending on what type of application you have (Static/ASP/ASP.net/PHP etc) you will need to first make decisions on what those settings are, that need to be tuned to increase performance. This has always been the question for most developers and web server administrators: What cache settings do I need to change to gain performance?

This blog doesn't talk about all scenarios, but will hopefully help you understand how each settings affect caching and assist you in making decisions. Please note that no matter what setting you have, you MUST always measure performance to prove that performance is better. Always begin with a baseline and then go from there.

I will also try and discuss the performance counter settings that you can use to measure caching in IIS 6.0 on Windows Server 2003 systems. So let's begin by understanding how HTTP Kernel mode caching works and then examining the settings that affect caching.

Caching of Static content

For each GET request , the IIS worker process tells Http.sys whether or not to cache a response based on the "activity-period cache algorithm". If the same URL is requested twice within 10 seconds (the default value for the ActivityPeriod registry entry located at: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Inetinfo\Parameters) the IIS worker process tells Http.sys to cache the full response by URI. All subsequent requests for the cached response will be served from the cache.

Every 120 seconds, the Http.sys response cache runs a flush algorithm, which flushes the cached respponses that have not been requested within the 120-second interval. The flush algorithm is also called when IIS receives change notification for a file — that is, when the file has been edited or changed in some way. The HTTP response cache is known as the kernel URI cache in System Monitor. A web server administrator can configure the cache scanvenging period & the size of the responses that will be cached. Please refer to the Microsoft KB article below for the default values and limits.

Http.sys registry settings for IIS
https://support.microsoft.com/kb/820129

CAUTION: Caching in kernel mode will increase performance, but also note that by doing so, you will be utilizing more kernel mode memory and therefore should be used with caution.

Scenario: If you have a static file called Default.htm on your site, you request it 2 times within 10 seconds, the response to default.htm will be cached in Kernel mode for the next 120 seconds. You can view this behavior using performance monitor logging. Let's first take quick look at the counters in question.

Go through Table D.13 Kernel Counters for the Web Service Cache Object on the Microsoft Website below:

https://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/ba61cb2d-2c0b-4709-9270-8fb79a16f7d9.mspx?mfr=true

So in the above scenario the performance counters should show the following values, assuming no other requests were made during this test:

Performance object: Web Service Cache

Counter 1st Request 2nd Request 3rd Request
Kernel: Current URIs Cached 0 1 1
Kernel: Total URIs Cached 0 1 1
Kernel: URI Cache Hits 0 0 1
Kernel: URI Cache Misses 1 2 2

Did you notice the changes? The Kernel URI Cache Misses counter remains the same, however, the Kernel: URI Cache Hits in increased by one. So what this means is that for the first 2 requests in scenario 1, a lookup was performed in the kernel mode cache for default.htm and since it was not cached, Http.sys forwarded the request to the worker processes. It also recorded that the URI was not found in the cache by updating Kernel: URI Cache Misses. Worker process notices that both requests came within a time span of 10 seconds and it is for the same URI. It then instructs Http.sys to cache the response for the URI. There are however many times when the response is not cached in Kernel mode. Only responses that will be cached in the kernel are responses to HTTP GET requests.

The Microsoft KB article below lists the reasons why Http.sys will not cache responses.

Reasons content is not cached by HTTP.sys in kernel
https://support.microsoft.com/kb/817445

Caching dynamic content in Kernel mode.

How to Cache ISAPI Responses in the Kernel

To cache ISAPI responses in the kernel, you add the following cache headers to go out with the response:

    • Cache-Control: public
• Expires (Optional)
• Last-Modified

It is recommended that the Expires value be calculated using an algorithm of current time on the server, plus the amount of time you want the cached copy to be served from the cache. 

The information provided thus far is true for any static content. Caching behaves differently for dynamically generated content than for static content. KB: 817445 still apply. Things are handled differently when the request is for dynamic content such as ASP & ASP.net. ASP & ASP.net are handled by ISAPI extensions in IIS and has a separate processing pipeline. Also, these technologies allow developers to control caching programmatically. By default ASP & ASP.net content will be cached in Kernel if possible. You can enable or disable this feature by using the methods in this Microsoft KB:

An ASP.NET page is stored in the HTTP.sys kernel cache in IIS 6.0 when the ASP.NET page generates an HTTP header that contains a Set-Cookie response
https://support.microsoft.com/kb/917072

David Wang (Microsoft) has a good post on Cache Insertion and Cache Eviction Policy, which I highly recommend reading. You can find it here:

https://blogs.msdn.com/david.wang/archive/2005/07/07/HOWTO_Use_Kernel_Response_Cache_with_IIS_6.aspx

According to this post, any dynamic page can be cached in the Kernel, IF the script engine that handled the request uses the ISAPI cache insertion policy mentioned in the post. I have't tested this with ASP much, but did try this with ASP.net. ASP.net exploits the Kernel mode cache extensively to provide better performance. By adding the following directive in an ASP.net web page, you can cache the response in a kernel mode cache

    <%@ OutputCache Location="Any" Duration="120" VaryByParam="None" %>

    NOTE: The other values of Client/Downstream/Server/ServerAndClient (Available in ASP.net 2.0) for Location tag didn't cache the response in Kernel.

The following cache related headers are added to the response by ASP.net. You can view this using a HTTP trace tool such as Fiddler or Web Development Helper.

    • Cache-Control: public
• Expires
• Last-Modified

On the first request, you will see that URI Cache Misses counter is incremented by 1 and an HTTP Status code of 200. At the same time you will notice that the Kernel: Current URIs cached and Kernel Total URIs Cached will also be incremented by 1. It doesn't check if there was another request within a span of 10 seconds for the same URI to be cached. Note that over here, it is ASP.net ISAPI that specifies that the response must be cached. The perfmon counters will show the following values.

Web Service Cache

Kernel: Current URIs Cached 1
Kernel: Total Flushed URIs 0
Kernel: Total URIs Cached 1
Kernel: URI Cache Hits 26
Kernel: URI Cache Hits % 96.296
Kernel: URI Cache Misses

1

Configuring and Tuning Kernel mode caching

One of the first things you want to begin is by finding out what content to cache. You may want to identify pages that are frequently used. Rahul Soni (Technical Lead, Microsoft PSS - IIS/ASP.net & a close colleague of mine) has written an article on how to use log parser to get a graph of the most used pages on your web server. You can read it here:

Log Parser 2.2 and ASP.NET
https://support.microsoft.com/kb/910447

Ensure is that Kernel mode caching is enabled in Windows Server 2003. If this setting is disabled, responses will never be cached in Http.sys. This setting is enabled by default and therefore it is not necessary to check this setting if you have a clean install of Windows 2003. You will want to check this setting if you upgraded from a previous version of Windows. All HTTP related parameters are located here in the system registry:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\HTTP\Parameters

NOTE: All of the parameters discussed are DWORD values.

Property UriEnableCache
Default Value 1
Type Boolean
Description If non-zero, the Http.sys response and fragment cache is enabled.

NOTE: Changes that are made to the registry will not take effect until you restart the HTTP service. Additionally, you have to restart IIS services as well. You can do this from command line as follows:

    a. net stop http /y
    b. net start http /y
    c. IISReset /start

My Suggestions

  • If you are caching too much in Kernel mode, then I recommend not using /3GB switch. This will give you 2 GB worth of Kernel mode memory as opposed to 1 GB
  • Adjust the following Cache Management Settings & Connection Management registry settings and measure performance using perfmon
UriMaxCacheMegabyteCount Default Value: 0. A non-zero value specifies the maximum memory available to the kernel cache. The default value, 0, allows the system to automatically adjust the amount of memory available to the cache. Note that specifying the size only sets the maximum, and the system may not allow the cache to grow to the specified size.
UriMaxUriBytes Default Value: 262144 bytes (256 KB). This is the maximum size of an entry in the kernel cache. Responses or fragments larger than this will not be cached. If you have enough memory, consider increasing this limit.

UriScavengerPeriod

Default Value: 120 seconds. Setting this to a high value reduces the number of scavenger scans and retention of response in cache. Setting this period to too low a value causes more frequent scavenger scans, and may result in excessive flushes and cache churn.
MaxConnections This value controls the number of concurrent connections Http.sys will allow. Each connection consumes non-paged-pool, a precious and limited resource. The default is determined quite conservatively to limit the amount of non-paged-pool used for connections. On a dedicated web server with ample memory, the value should be set higher if a significant concurrent connection load is expected. A high value may result in increased non-paged-pool usage, so care should be taken to use a value appropriate for the system.