Data Caching, SyncLock Applications, Expiry and HELP!

Posted on 2006-05-27
Last Modified: 2008-02-07

In way over my head.... I have a 2 web application that has some problems - most using cached datasets/tables. I have implemented data caching that loads some very frequently-used tables into cache. Essentially, the web app serves a number of domains and needs different settings/content for each - and much of that content is contained in the cached datatables/datasets but the datasets are not big but can be accessed 20-30 times in the life of a page request for a complex page.

The data caching was simply set up to hold the datasets/tables in cache and then expire them after 30 minutes - so I could reload any new settings/content. When in production under load the app had serious problems - I think related to race conditions when items in the cache expired - so I added created a LoadCache class and added a SyncLock on an object around the cache insert procedures to stop instances/threads trying to add stuff at the same time. But the problems remain it seems because multiple threads are trying to READ the cache object, then find the data doesn't exist (because it expired), then requests a load of the cache (and finds the lock on it) while another thread has already issued the request to load it. I am not sure this is what is actually happening as it really hard to trace/debug. In any event I suddenly get errors galore as threads somehow can't see the cached data.

Here is summary of what I've done:

class cacheloader
  private shared cachelock as new object

  public sub loadcache(tblname as string)
      '...get the table from db
        httpcontext.current.cache.insert(tbl) 'the table is actually in its own dataset
       end synclock
  end sub
end class

then elsewhere I have:

dim ds as new dataset
ds=ctype(httpcontext.current.cache("mytable"), dataset)
if isnothing(ds) then
      dim c as new cacheloader
      ds=ctype(httpcontext.current.cache("mytable"), dataset)
end if

I suspect that there is an issue with the scope of the cacheloader cachelock object (surely this is private to each instance of cahceloader?) and thus not a functioning lock? Should cacheloader be declared globally somewhere?

All that aside, I have come to the conclusion that the really-frequently accessed data needs to not expire in cache cuz I just can't seem to get it to work properly under load and keep everything synched. There is a good possibility that I haven't created and used the LoadCache class properly

Now to the question: if I load the datatable/sets into Application (i.e. as an Application("mydataset")) then when does this get refreshed? I have multiple worker processes configured - do each of these use the same Application object? If I load the dataset in Application during Application_Start, will each worker process run the Application_Start and have it's own copy of Application, or is it global/shared to all the worker processes. I ask this as I have the worker processes set in IIS to restart every 1/2 hour - and that would be an OK interval to reload this data.

Is there any worker process/instance specific way of loading this data so as each worker process stops/restarts, it gets its own (current) copy of data and loads it?

Sorry for the rambling...  What I thought was a good idea (using expiring cache for extremely frequently accessed data - sometimes 10+ times per page request) is not as easy as it would appear from MS docs. In fact, in no example of data caching I have seen from MS does they even mention the need to SyncLock to avoid multiple threads modifying the cache.

And... before anyone suggest sql data dependencies: I don't want the complexity of this. If I can't get simple expiring caches to work, I can't imagine having to try to debug sql data dependencies....

Thanks for any help.

Question by:ctudorprice
    LVL 26

    Accepted Solution

    application object does not have refresh or expires as cache objects ..

    I also see that u have this code to add data into cache

    i hope u know that cache has other parameters like priority, expiry, callback functions etc which can used for solving all ur problems .. pls read MSDN for all the features .. there is no need for locks etc .. if u feel that ur object in cache has to last for 30 minutes, u can set the priority to max and so it will never get removed till 30 minutes .. sliding expiration also helps for similar situation .. callback function can also be used so that when the item gets removed from cache, u can check the reason y it was removed and take approriate action ...


    Author Comment

    thanks - I ended up (glutton for punishment) totally rewriting the caching and am currently volume testing it. (I did have the other params for cache.insert).

    I rewrote to use datatables in the cache instead of datatables in datasets because I thought that maybe datasets were adding a lot of overhead and were unnecessary. Also took your advice and moved the important tables to max priority. Put an Application.Lock AND a SyncLock around the cache insert - just to be double sure, and changed the scope of the object I was sync locking on to global - and generally simplified everything.

    I'm still not clear on what the best approach to managing synchronization on the cache object is though - Application.Lock or SyncLock?


    Author Comment

    Just so I don't confuse anyone as much as I was confused....

    There was/is no need for synclock or application.lock as the tables I was storing in cache are/were read-only - doh. SyncLock is only required to ensure that the same threads don't update read/write items in cache. I had a procedure that I called to load the table into cache and that did that but when the cache ran out of memory and cache memory was full, they were immediately removed. So, I changed that to a function that inserted the table into cache and returned the table it retrieved - rather than subsequently trying to read the inserted table from cache. This enables my app to always get the table from the caching insert function, even if it is immediately removed from the cache... I'm sure there are more elegant ways to do this but my app now works under heavy load.

    Here's a stripped-down outline of what I ended up doing:

    dbcache.getcachedtable("mytable", CacheItemPriority.Normal)
    dbcache.getcachedtable("mytable", CacheItemPriority.NotRemovable)

    class dbachce
    public shared function getcachedtable(tablename as string, cachepriority As Caching.CacheItemPriority = CacheItemPriority.Normal) as datatable
    Dim dt As DataTable = Nothing
    If HttpContext.Current.Cache(tablename) Is Nothing Then
          Dim ld As New dbcacheloader
                dt = ld.loadcachetable(tablename,cachepriority)
                dt = CType(HttpContext.Current.Cache(tablename), DataTable)
    End If
    end function
    end class

    class dbcacheloader
    public function loadcachetable(tbl_command as string="", cachepriority As Caching.CacheItemPriority = CacheItemPriority.Normal)

    Dim cacheexpires As DateTime
    If HttpContext.Current.Application("cacheexpires") = Nothing Or HttpContext.Current.Application("cacheexpires") <= DateTime.Now Then
          cacheexpires = DateTime.Now.AddMinutes(30)
          HttpContext.Current.Application("cacheexpires") = cacheexpires
          cacheexpires = HttpContext.Current.Application("cacheexpires")
    End If

    gettable = DB.GetTable("select * from " & tbl_command) 'class to open connection, retrieve datatable

    'inserts the table into cache
    HttpContext.Current.Cache.Insert(tbl_command, gettable, Nothing, cacheexpires, TimeSpan.Zero, cachepriority, Nothing)
    'returns the table that was retrieved to the caller, just in case item was cleared immediately from cache due to low memory
    Return gettable
    end function
    end class

    Thanks Rejojohny for clearing my head. I was looking for a complicated solution to a simple problem.
    LVL 26

    Expert Comment

    you are welcome, glad to be of help .. :-)


    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    Maximize Your Threat Intelligence Reporting

    Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

    Introduction This article shows how to use the open source plupload control to upload multiple images. The images are resized on the client side before uploading and the upload is done in chunks. Background I had to provide a way for user…
    International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
    Migrating to Microsoft Office 365 is becoming increasingly popular for organizations both large and small. If you have made the leap to Microsoft’s cloud platform, you know that you will need to create a corporate email signature for your Office 365…
    In this sixth video of the Xpdf series, we discuss and demonstrate the PDFtoPNG utility, which converts a multi-page PDF file to separate color, grayscale, or monochrome PNG files, creating one PNG file for each page in the PDF. It does this via a c…

    758 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    14 Experts available now in Live!

    Get 1:1 Help Now