2

OutputCache is an easy way to improve performance in ASP.NET <=4.6 MVC. We should apply this attributes for actions that are very expensive, but between the first request and the cache being created we could have a lot of concurrent requests - and will consume the server resources that we are trying to protect (Memory, CPU, I/O).

If multiples requests are from the same user, by default, aspnet serialize the requests as we can see in this link, The Downsides of ASP.NET Server Sate:

by default the ASP.NET pipeline will not process requests belonging to the same session concurrently. It serialises them, i.e. it queues them in the order that they were received so that they are processed serially rather than in parallel.

So, in order to simulate this situation, we need to create multiples requests (from different users). It can be done by creating request from more than one browser or using any kind of workload with multiple threads.

So, I'm trying to inherits from OutputCacheAttribute and create something like OutputCacheConcurrentRequestAttribute and serialize the requests from multiples users. Have anyone tried to do that?

The idea is to Thread.Sleep all the subsequent requests for the same resource (Considering unique by the OutputCache properties and action) on OnActionExecuting while the first request is not finished.

Looking the IL in ILSpy, some methods, which could help me in this job, are private and internal, like those:

Assembly: System.Web.Mvc

Namespace: System.Web.Mvc

class OutputCacheAttribute
{
private GetChildActionUniqueId
private BuildUniqueIdFromActionParameters
}

internal class DescriptorUtil

I'm trying to not duplicate the code and not use reflector (For performance reasons).

Have anyone faced the same problem?


Edit 1

After some research I have found that the MVC Donut caching have faced a lot of difficults to inhertis the OutputCacheAttribute and they have rewritten it completely:

In terms of implementing donut caching, hooking into the OutputCacheModule HttpModule would be very difficult/impossible, so instead we must rewrite the OutputCacheAttribute completely

It was much more easy to complete my idea using the DonutOutputCacheAttribute.

mqueirozcorreia
  • 905
  • 14
  • 33

1 Answers1

1

Are you actually seeing this as a performance issue or is this just theoretical? I would caution that going too far down this road can hurt performance... maybe consider a solution that pre-warms the cache?

That said, you might have better luck with just a lock in your action you are trying to make just execute once. Create a variable, and do a lock on that variable in an action filter (before the action fires), and release in your action filter after the action fires, and that should serialize your requests.

public class SerializedOutputCacheAttribute : OutputCacheAttribute
{
    private readonly object lockObject = new object();

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Monitor.Enter(this.lockObject);
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        Monitor.Exit(this.lockObject);
    }
}

You can then apply it to any action, and that action will be serialized but not affect other actions.

    [SerializedOutputCache(Duration = 5000)]
    public ActionResult About()

EDIT: This approach wont work because OutputCacheAttribute actually pushes to IIS for the caching, so the action will still fire multiple times.

Cleverguy25
  • 493
  • 3
  • 14
  • This is a performance issue. It's not possible to pre-warms the cache, because the action is dynamically generated based on web resource and parameters. – mqueirozcorreia Jan 22 '16 at 22:58
  • I really want to use the idea of using the lock in the ActionFilter, but and don't want to lock every action when one is being executed, but the specific lock using UniqueId (Considering Action and parameters) that is why I want to use `GetChildActionUniqueId`. – mqueirozcorreia Jan 22 '16 at 23:03
  • You can apply the action filter to a specific action with its attribute. – Cleverguy25 Jan 22 '16 at 23:14
  • But like the OutputCache, I want to reuse it on all actions that is needed – mqueirozcorreia Jan 23 '16 at 00:13
  • You can lock on an instance variable assigned to the attribute attached to a specific instance of the action. See my edit of my answer. – Cleverguy25 Jan 24 '16 at 06:57
  • I like the idea of Monitor.Enter and Monitor.Exit, but the ActionFilter still may be shared to different actions: [In ASP.NET MVC 3, filters are cached more aggressively.](http://www.asp.net/whitepapers/mvc3-release-notes#RTM-BC) Is that right? So, that is why I'm thinking about using a list of lock to create unique lock (Considering Action and parameters) – mqueirozcorreia Jan 24 '16 at 14:51
  • The lock should be [like the ideia on this post](http://stackoverflow.com/questions/781189/how-to-lock-on-an-integer-in-c) using as static variable. What do you think? – mqueirozcorreia Jan 24 '16 at 14:58
  • I had setup my code and did a quick test, but I got a false positive. OutputCache works a bit differently than I guessed, it actually hands off a lot of the work for caching to IIS. So this approach wont work at all no matter how you setup your locks. I would suggest you move your lock to a list of locks in your controller actions itself. You would have to cache your ViewModels instead. – Cleverguy25 Jan 24 '16 at 21:51