Reputation: 73112
I have an ASP.NET MVC 3 / .NET Web Application, which is heavily data-driven, mainly around the concept of "Locations" (New York, California, etc).
Anyway, we have some pretty busy database queries, which get cached after they are finished.
E.g:
public ICollection<Location> FindXForX(string x)
{
var result = _cache.Get(x.ToKey()) as Locaiton; // try cache
if (result == null) {
result = _repo.Get(x.ToKey()); // call db
_cache.Add(x.ToKey(), result); // add to cache
}
return result;
}
But i don't want to the unlucky first user to be waiting for this database call.
The database call can take anywhere from 40-60 seconds, well over the default timeout for an ASP.NET request.
I want to "pre-warm" these calls for certain "popular" locations (e.g New York, California) when my app starts up, or shortly after.
I don't want to simply do this in Global asax (Application_Start), because the app will take too long to start up. (i plan to pre-cache around 15 locations, so that's a few minutes of work).
Is there any way i can fire off this logic asynchronously? Maybe a service on the side is a better option?
The only other alternative i can think of is have an admin page which has buttons for these actions. So an administrator (e.g me) can fire off these queries once the app has started up. That would be the easiest solution.
Any advice?
Upvotes: 15
Views: 4326
Reputation: 39
Sounds like you should just dump the results in a separate table and have a scheduled task to repopulate that table periodically.
If one pre-calculated table isn't enough because it ends up with too much data that you need to search through, you could use more than one.
Upvotes: 3
Reputation: 20674
Would suggest taking a look at auto-starting your app, especially if you are load balanced.
Upvotes: 1
Reputation: 20780
Look into "Always running" app setting for IIS 7.5. What this basically do is have an app pool ready whenever the existing one is to be recycled. Of course, the very first would take the 40-60 seconds but afterwards things would be fast unless you physically restart the machine.
Upvotes: 6
Reputation: 2213
Doing the loading in a Task
from Application_Start
is the way to go, as mentioned by Scott.
Just be careful - if your site restarts and 10 people try to view California, you don't want to end up with 10 instances of _repo.Get(x.ToKey()); // call db
simultaneously trying to load the same data.
It might be a good idea to store a boolean value "IsPreloading"
in the application state. Set it to true
at the start of your preload function and false
at the end. If the value is set, make sure you don't load any of your 15 preloaded locations in FindXForX
.
Upvotes: 1
Reputation: 300549
Before you start cache warming, I suggest you check that the query is "as fast as it can be" by first looking at how many logical reads it is doing.
Upvotes: 3
Reputation: 19117
The quick and dirty way would be to fire-off a Task
from Application_Start
But I've found that it's nice to wrap this functionality into a bit of infrastructure so that you can create an ~/Admin/CacheInfo page to let you monitor the progress, state, and exceptions that may be in the process of loading up the cache.
Upvotes: 6
Reputation: 8726
One option is to use a website health monitoring service. It can be used to both check website health, and if scheduled frequently enough, to invoke your common URLs.
Upvotes: 2
Reputation: 18065
One solution is to launch a worker thread in your Application_Start
method that does the pre-warming in the background. If you do it right, your app won't take longer to start up, because the thread will be executed asynchronously.
Upvotes: 2