title: Disabling ASP.NET HttpHandler response caching
body: <h2>Background</h2>
  
  <p>I'm in the midst of comparing the performance of NancyFx and ServiceStack.NET running under IIS 7 (testing on a Windows 7 host).  Both are insanely fast - testing locally each framework processes over 10,000+ req/sec, with ServiceStack being about 20% faster.</p>
  
  <p>The problem I'm running into is that ASP.NET appears to be caching the responses for each unique URI request from the HttpHandler, quickly leading to massive memory pressure (3+ GBs) and overworking the garbage collector (~25% time consumed by the GC).  So far I've been unable to disable the caching and buildup of objects, and am looking for suggestions on how to disable this behavior.</p>
  
  <h2>Details</h2>
  
  <p>The request loop is basically as follows:</p>
  
  <pre><code>for i = 1..100000:
      string uri = http://localhost/users/{i}
      Http.Get(uri)
  </code></pre>
  
  <p>The response is a simple JSON object, formatted as { UserID: n }.</p>
  
  <p>I've cracked open WinDBG, and for each request there are:</p>
  
  <ul>
  <li>One <code>System.Web.FileChangeEventHandler</code></li>
  <li>Two <code>System.Web.Configuration.MapPathCacheInfos</code></li>
  <li>Two <code>System.Web.CachedPathDatas</code></li>
  <li>Three <code>System.Web.Caching.CacheDependencys</code></li>
  <li>Five <code>System.Web.Caching.CacheEntrys</code></li>
  </ul>
  
  <p>Obviously, these cache items are what is leading me to believe it's a cache bloat issue (I'd love to get rid of 150,000 unusable objects!).</p>
  
  <h2>What I've tried so far</h2>
  
  <ul>
  <li>In IIS 'HTTP Resonse Headers', set 'Expire Web content' to 'immediately'.</li>
  <li><p>In the web.config</p>
  
  <pre><code>&lt;system.web&gt;
   &lt;caching&gt;
      &lt;outputCache enableOutputCache="false" enableFragmentCache="false"/&gt;
    &lt;/caching&gt;
  &lt;/system.web&gt;
  </code></pre></li>
  <li><p>Also in the web.config (and many variations on the policies, including none).</p>
  
  <pre><code>&lt;caching enabled="false" enableKernelCache="false"&gt;
    &lt;profiles&gt;
      &lt;add policy="DontCache" kernelCachePolicy="DontCache" extension="*/&gt;
    &lt;/profiles&gt;
  &lt;/caching&gt;
  </code></pre></li>
  <li><p>Looked through the source code of the frameworks to see if there might be any "features" built in that would use ASP.NET caching.  While there are caching helpers, they are private to the framework itself and do not appear to leverage ASP.NET caching.</p></li>
  </ul>
  
  <h2>Update #1</h2>
  
  <p>Digging through reflector I've found that setting the value for <code>UrlMetadataSlidingExpiration</code> to zero eliminates a large portion of the excessive memory usage, at the expense of cutting throughput by 50% (the FileAuthorizationModule class caches the FileSecurityDescriptors, which must be somewhat expensive to generate, when <code>UrlMetadataSlidingExpiration</code> is non-zero).</p>
  
  <p>This is done by updating the web.config and placing the following in :</p>
  
  <pre><code>&lt;hostingEnvironment urlMetadataSlidingExpiration="00:00:00"/&gt;
  </code></pre>
  
  <p>I'm going to try to fully disable the FileAuthorizationModule from running, if possible, to see if that helps.  However, ASP.NET is still generating 2*N <code>MapPathCacheInfo</code> and <code>CacheEntry</code> objects, so memory is still getting consumed, just at much slower rate.</p>
  
  <h2>Update #2</h2>
  
  <p>The other half of the problem is the same issue as described here: <a href="https://stackoverflow.com/questions/1421996/prevent-many-different-mvc-urls-from-filling-asp-net-cache">Prevent many different MVC URLs from filling ASP.NET Cache</a>.  Setting</p>
  
  <p><code>&lt;cache percentagePhysicalMemoryUsedLimit="1" privateBytesPollTime="00:00:01"/&gt;</code> 
  helps, but even with these very aggressive settings memory usage quickly rises to 2.5GB (compared to 4GB).  Ideally these objects would never be created in the first place.  Failing that, I may resort to a hacky solution of using reflection to clear out the Caches (all these entries are "private" and are not enumerated when iterating over the public Cache).</p>