Sunil
Sunil

Reputation: 21406

Will following ever cause RACE CONDITION when accessing ASP.Net Cache Item?

I am using the following code, and to me it seems that a race condition would never happen with code below. Or there is still a chance of race condition?

        List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
        if (listFromCache != null)
        {
           //do something with listFromCache. **IS IT POSSIBLE** that listFromCache is 
                                              //NULL here
        }
        else
        {
             List<Document> list = ABC.DataLayer.GetDocuments();
             Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                          System.Web.Caching.Cache.NoSlidingExpiration);
        }

UPDATE: Chris helped me solve this problem, but I just thought, I would share some details that would be be very helpful to others.

To completely avoid any race condition, I had to add a check within the true part, else I could end up with a List with zero count, if someone else clears it in Cache ( not remove the item, but just call Clear method on the List object in Cache) after my if has evaluated to TRUE. So then, I would not have any data within my true part of if in listFromCache object.

To overcome, this subtle RACE condition in my original code, I have to double check listFromCache in the true part as in code below, and then repopulate Cache with latest data.

Also, as Chris said, if someone else 'removes' the items from Cache by calling the method Cache.Remove, then listFromCache would not be affected, since the Garbage Collector will not remove the actual List object from HEAP memory because a variable called 'listFromCache' is still having a reference to it ( I have explained this in more detail in a comment under Chris's answer post).

List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
    if (listFromCache != null)
    {
      //OVERCOME A SUBTLE RACE CONDITION BY IF BELOW
      if( listFromCache == null || listFromCache.Count == 0)
      {
          List<Document> list = ABC.DataLayer.GetDocuments();
          Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                      System.Web.Caching.Cache.NoSlidingExpiration);
       }
       //NOW I AM SURE MY listFromCache contains true data
       //do something with listFromCache. **IS IT POSSIBLE** that listFromCache is 
                                          //NULL here
    }
    else
    {
         List<Document> list = ABC.DataLayer.GetDocuments();
         Cache.Insert(dataCacheName, list, null, DateTime.Now.AddMinutes(5), 
                      System.Web.Caching.Cache.NoSlidingExpiration);
    }

Upvotes: 1

Views: 724

Answers (1)

Chris Sinclair
Chris Sinclair

Reputation: 23218

No, it's not possible in your comment that listFromCache will become null as it's a local reference at that point. If the cache entry is nullified elsewhere, it doesn't affect your local reference. However, you could possibly get a condition where you retrieved a null value, but while in the process of gathering the documents (ABC.DataLayer.GetDocuments()) another process has already done so and inserted the cache entry, at which point you overwrite it. (this may be perfectly acceptable for you, in which case, great!)

You could try locking around it with a static object, but honestly, I'm not sure if that'll work in an ASP.NET context. I don't remember if Cache is shared across all ASP.NET processes (which IIRC, have different static contexts) or only shared within each single web worker. If the latter, the static lock will work fine.

Just to demonstrate too:

List<Document> listFromCache = Cache[dataCacheName] as List<Document>;
if (listFromCache != null)
{
    Cache.Remove(dataCacheName);
    //listFromCache will NOT be null here.
    if (listFromCache != null)
    {
        Console.WriteLine("Not null!"); //this will run because it's not null
    }
}

Upvotes: 1

Related Questions