ASP.NET Caching Asynchronously

2019-04-09 12:56发布

First off I think I should link to this article which pretty much accomplishes what I what.

Here's my problem: I had a user control on my site that will need to cache some data for at least 15 minutes and then pull the data again from the DB. The problem is the pull takes about 7-10 seconds to pull the result from the DB.

My thought is that I can set the cache to like two hours, then have a property in the cached object to say when the object was loaded (let's call this the LoadDate property). I would then have the code pull the cached object.

  • If it's null, I have no choice but to pull the data synchronously and then load my user control
  • If it's not null, I want to go ahead and load the data onto my user control from the cached object. I would then check the LoadDate property. If it's been 15 minutes or more, then set up an asynchronous process to reload the cache.
    • There needs to be a process to lock the cache object for this while it's updating
    • I need an if statement that says if the object is locked, then just forget about updating it. This would be for subsequent page loads by other users - as the first user would already be updating the cache and I don't want the update the cache over and over again; it should just be updated by the first call. Remember I'm already loading my user control before I even do the cache check

In the article I linked to before, the answer set up the cache updating perfectly, but I don't believe it is asynchronous. The question started with doing it asynchronously using the Page.RegisterAsyncTask. [Question 1] I can't seem to find any information on whether this would allow a asynchronous process to continue even if the user left the page?

[Question 2] Anybody have a good idea on how to do this? I have some code, but it has grown extremely long and still doesn't seem to be working correctly.

1条回答
祖国的老花朵
2楼-- · 2019-04-09 13:41

Question 1 (RegisterAsyncTask)

Very important thing to remember: from the client/user/browser perspective, this does NOT make the request asynchronous. If the Task you're registering takes 30 seconds to complete, the browser will still be waiting for 30+ seconds. The only thing RegisterAsyncTask does, is to free up your worker thread back to IIS for the duration of the asynchronous call. Don't get me wrong - this is still a valuable and important technique. However, for the user/browser making that particular request, it does not have a noticeable impact on response time.

Question 2

This may not be the best solution to your problem, but it's something I've used in the past that might help: when you add your item to the cache, specify an absoluteExpiration, and an onRemoveCallback. Create a function which gets fresh data, and puts it into the cache: this is the function you should pass as the onRemoveCallback. That way, every time your cached data expires, a callback occurs to put fresh data back into the cache; because the callback occurs as a result of a cache expiration event, there isn't a user request waiting for the 7-10 seconds it takes to cache fresh data.

This isn't a perfect design. Some caveats:

  • How to load the cache initially? Easiest way would be to call your cache-loader function from the Application_Start function.
  • Every 15 minutes, there will be a 7-10 second window where the cache is empty. Any requests during that time will need to go get the data themselves. Depending on your system usage patterns, that may be an acceptable small window, and only very few requests will occur during it.
  • The cache callback is not guaranteed to happen precisely when your cached item expires. If the system is under extremely heavy load, there could be a delay before the callback is triggered and the cache re-loaded. Again, depending on your system's usage, this may be a non-issue, or a significant concern.

Sorry I don't have a bulletproof answer for you (I'm going to keep an eye on this thread - maybe another SO'er does!). But as I said, I've used this approach with some success, and unless your system is extremely high-load, it should help address the question.

Edit: A slight twist on the above approach, based on OP's comment

You could cache a dummy value, and use it purely to trigger your refreshCachedData function. It's not especially elegant, but I've done it before, as well. :)

To elaborate: keep your actual cached data in the cache with a key of "MyData", no expiration, and no onRemoveCallback. Every time you cache fresh data in "MyData" you also add a dummy value to your cache: "MyDataRebuildTrigger", with a 15-minute expiration, and with an onRemoveCallback that rebuilds your actual cached data. That way, there's no 7-10 second gap when "MyData" is empty.

查看更多
登录 后发表回答