Caching system

Caching system

Old forum URL: forums.lhotka.net/forums/t/4186.aspx


jureleskovec posted on Thursday, January 17, 2008

Oi folks!

I'm building a 3-tier (task tracker) application. Lately I've been working on caching system that would cache certain business objects on clients and also on the server. These objects will mostly be read-only collections which do not change frequently (users, teams, projects...). For that I built additional infrastructure that would automatically detect changes of these chached objects using revision numbers and eventually automatically refresh (list) controls (which are bound to my CacheDataProvider).


My solution is the following:

Main players:
CacheContainer - serves as a cache container that wraps some (cached) business object and keeps a revision number.
ClientCachingManager - Maintains list of all cache containers on the client-side and serves as an updater.
ServerCachingManager - Maintains list of all cache containers on the server-side and updates server-side containers (reloads business objects from DB) as necessary.

AS it can be infered from above caching is done on the client as well on the server. client-side cache updates itself from the server-side cache and the server.side cache updates itself from a data source.

So far, ClientCachingManager periodicaly (every couple of seconds) does the following:
1) Makes Requests to the server and sends revision numbers of all its cache containers.
2) ServerCachingManager compares received revision numbers with the ones of the server-side containers.
3) The cache containers for which the revision numbers do not match are returned to the client as part of the response.
4) ClientCachingManager updates its containers with the ones it received from the server.

But this behaviour is not sufficient. The behaviour above is maybe just nice-to-have. Primary behaviour should be the same as the one above but instead relying on ClientCachingManager these checks and updates should happen with every request that some business object makes. I don't thing this would inflict to much overhead and besides it provides the same user expiriance as if there would be no caching at all.

In order to accomplish such task the ClientCacheManager should be updating the list of revision numbers in the 'DataPortalContext.ClientContext' so this data is sent along with other data every request. On the server-side' there should be some common 'exposed' entry point for all requests, so ServerCachingManager could intercept requests and do his job of comparing the received revision numbers and eventually return up-to-date containers. I expected same the pair of events of the server-side data portal like the ones on the client side (DataPortalInvoke, DataPortalInvokeComplete). 

But, I was surprised, there is no such events at all. I tryed many things but I just don't see how to intercept requests on the server.


So my question is: how to do intercept requests on the server-side.

In addition, a comment on my caching solution would also be appreciated and if there are questions about design and implementation I would gladly go into details an show the code if necessary.


-- Jure

ajj3085 replied on Thursday, January 17, 2008

Instead of rolling your own, I'd check out the Caching Application Block.  Has tons of options that are ready to use out of the box, and is extensible.

RockfordLhotka replied on Thursday, January 17, 2008

jureleskovec:
Oi folks!
I expected same the pair of events of the server-side data portal like the ones on the client side (DataPortalInvoke, DataPortalInvokeComplete). 

But, I was surprised, there is no such events at all. I tryed many things but I just don't see how to intercept requests on the server.

So my question is: how to do intercept requests on the server-side.

The problem on the server is with supporting the lack of WithEvents in C#, because without that feature there's no automatic way to hook events.

Remember that the server is stateless, so you'd need to hook events on every call to the server. In VB this would be a simple WithEvents statement, but in C# you'd actually need to implement some "init" method that the data portal would always call where you could hook the events. That's messy.

So instead, the data portal calls methods on your objects before and after each actual data portal call. See Chapter 4 for details.

You can create custom base classes for each of the 6 CSLA base classes (which is a good idea in any case), and implement these pre- and post-processing data portal methods in the base classes. That way you'll always be notified (on the server) when these calls occur.

Make sure to use ApplicationContext.ExecutionLocation to ensure the code really is running on the server though! Remember that the data portal can be configured in local mode (and is by default), in which case your "server" code is really running on the client.

jureleskovec replied on Friday, January 18, 2008

Rocky, thank you for the reply.

The problem on the server is with supporting the lack of WithEvents in C#, because without that feature there's no automatic way to hook events.


I don't understand what you mean with it and I'm sorry if I'm asking you stupid questions, I would just like to get to the bottom.
My ServerCachingManagerClass (its cache is shared among all clients) is static class, hence it would subscribe to the static event (lets say) Server.DataPortal.RequestCompleted only once, probably at the startup of the server (host):

static void Main(string[] args)
{
    // Hook-up event handler.
    Csla.Server.DataPortal.RequestCompleted += new ResponseEventHandler(ServerCachingManager.UpdateClientCache);

    // Create service host.
    host = new ServiceHost(typeof(Csla.Server.Hosts.WcfPortal));
    host.Open();
}

But in my case there are also other opportunities to do this (at the first use of  ServerCachingManager in the static constructor).
Server.DataPortal would on the other side raise this event just before its Fetch method would return and the result would return to client.
I re-checked the data portal code and I think I understand it good but I just don't see where I could be wrong. Woud you mind showing me the truth?


You can create custom base classes for each of the 6 CSLA base classes (which is a good idea in any case)

Yes, this would be possible but I gave that up for two reasons:
1) Like you said I would have to insert another layer of business base classes, which means a lot of duplicated code and added complexity just to achieve the desired behaviour.
2) This code does not anything to do with the business objects, its about cash and it doesn't belong there. This is part of seperate/extended infrastructure of the csla framework where ClientCachingManager talks to the ServerCachingManager.


I could also imagine other scenarios for this like loging all fetch requests and I'm shure there is a lot of other opportunities to use such events.


-- Jure

RockfordLhotka replied on Friday, January 18, 2008

I agree that the events could be useful.

The problem is hooking the events. You suggest a static class with a static constructor, but that static constructor won't run until some code (your code inside a DataPortal_XYZ method) either calls a method on that class or creates an instance of that class.

This means that you can't hook the events until after the data portal has invoked a DataPortal_XYZ method. There'd be no way to get a pre-call event, because you can't have hooked the events until you are in the call.

Additionally, this is unreliable for post-call events because there's no guarantee that every developer will cause that static constructor to be invoked. There's no way to guarantee that every DataPortal_XYZ method will call a method or create an instance on that class.

The only way to be sure of reliable event handling, and to support pre-call events, would be for the data portal to invoke some "init" method early in the server-side processing. And that could be done - you'd have to specify the type and method name in a config file setting, and CSLA would then invoke that method once per AppDomain to allow you to hook the events.

It is also the case that handling static events on the server means dealing with threading. All the event handlers would have to be written in a threadsafe manner. If you (the author of the handlers) don't get it just right you'll cause all sorts of nightmarish issues on the server.

The pre- and post- calls the way they are now don't have to worry about threading quite as much, because they are unique instances per thread. But static event handlers are one instance shared across all threads.

I'll add this to the wish list for some future release.

Copyright (c) Marimer LLC