1
votes

I have a website which will retrieve some heavy data from database and stored it into a cache object. However, when in peak hours, if the cache object is expired and all users retrieve the data at the same time, my website become extremely slow (CPU 100%) and the connection between the website and the db will timeout. As a result, the cache object will become empty.

The temporary solution for me is, every time when the cache object is expired. I have to stop all users by redirect all users to a static page. And then clear the cache object and access the page as a administrator (at the moment only administrators are allowed to access) to generate the cache object. After that i will enable the page again and users can access the page as usual.

I was thinking, is it possible to make it whenever the cache object is expire, the cache object will be created when the first visitor access to the page only(in peak hours, it might be up to 50 users access the page at the same time), and the remaining users that accessing the page have to wait until the cache object is created(stop them to call the create cache object module).

Current how it works is every time when user access to this page, it will check for the cache object. If it is null, then it will retrieve HeavyData from database.

if (Cache[ID] == null)
    {
        HeavyData = RetrieveHeavyDataFromDatabase(); // retrieve data from db 
        Cache.Insert(ID, HeavyData, null, DateTime.Now.AddMinutes(720)), System.Web.Caching.Cache.NoSlidingExpiration);
    }
2

2 Answers

2
votes

The problem may be that multiple request are trying to recreate the cache.

Just add a lock object and an if-lock-if wrapper around your singleton. This will still hold up every request until the Cache is populated, but will ensure that only 1 request will enter the heavy process.

http://en.wikipedia.org/wiki/Double-checked_locking

private static object lockObject = new object();

if (Cache[ID] == null)
{
    lock(lockObject)
    {
        if (Cache[ID] == null)
        {
             HeavyData = RetrieveHeavyDataFromDatabase(); // retrieve data from db 
             Cache.Insert(ID, HeavyData, null, DateTime.Now.AddMinutes(720)), System.Web.Caching.Cache.NoSlidingExpiration);
        }
    }
}
1
votes

You should wrap a lock statement around it. It will make sure that only one thread can do the heavy retreiving.

static Object lockObject = new Object();
lock(lockObject)
{
    if (Cache[ID] == null)
    {
        HeavyData = RetrieveHeavyDataFromDatabase(); // retrieve data from db 
        Cache.Insert(ID, HeavyData, null, DateTime.Now.AddMinutes(720)), System.Web.Caching.Cache.NoSlidingExpiration);
    }
}