background cache invalidation

2007-07-26 @ 05:37#

i came up with a pattern for background cache invalidation this morning that seems solid (i need to noodle on it a bit, tho).

my problem is that some updates require the invalidation of *many* cache entries. for example, if i update a single blog entry, that could invalidate not only that one entry, but any number of pages that show that entry including the home page, and many list pages. even lists built to give the editor a pick list of blog entries is now invalid!

at first, i created a list of cache URIs that should be invalidated after each POST/PUT/DELETE. this works fine, but can be quite slow when lots of URI are in the list. next, i worked up a routine that looped through the list firing off a new thread for each item. bad idea! i ran out of threads and choked the server!

next i implemented a single loop, on a separate thread, that invalidated all the cache items. problem there was, if the list was long, some screens might not update soon enough. i had to refresh my edit screen pick list before i saw the updated version of the resource!

finally, i implemented a mix of immediate and background cache invalidation. Now the SqlXmlHandler has two collections: ImmediateCacheUri and BackgroundCacheUri. that way, i can control which cache invalidations must be completed before yielding to the user and which can be done on a separate thread. not too bad.

here's how my ClearCache method looks now:

private void ClearCache(string id) { string domain_root = string.Format( "{0}://{1}{2}", this.Context.Request.Url.Scheme, this.Context.Request.Url.DnsSafeHost, util.GetConfigItem("rootfolder") ); // do the immediate cache invalidations 'inline' if (this.ImmediateCacheUri != null) { // build list of absolute uri string[] uri = new string[this.ImmediateCacheUri.Length]; for (int i = 0; i < uri.Length; i++) uri[i] = domain_root + this.ImmediateCacheUri[i].Replace("{@id}", id); // initialize object and start on this thread CollectionRequestor creq = new CollectionRequestor(); creq.Uri = uri; creq.Execute(); } // do the background invalidations on a diff thread if (this.BackgroundCacheUri != null) { // build list of absolute uri string[] uri = new string[this.BackgroundCacheUri.Length]; for (int i = 0; i < uri.Length; i++) uri[i] = domain_root + this.BackgroundCacheUri[i].Replace("{@id}", id); // initialize object and start on a new thread CollectionRequestor creq = new CollectionRequestor(); creq.Uri = uri; System.Threading.Thread th = new System.Threading.Thread(creq.Execute); th.Start(); // wait .1 seconds before returning System.Threading.Thread.Sleep(100); } }

code