View Full Version : Concurrency problem with transparent caching in 0.2
slowth
Mar 29th, 2006, 03:58 AM
I posted this to the spring-modules mailing list about a week ago but didn't get any replies. Trying the forums instead...
Hi,
I'm currently using springmodules 0.2 to transparently cache a web service used in a web application. I had to make a minor change to CacheProxyFactoryBean to make 0.2 work with Spring 1.2.7. Line 133 was changed into proxyFactory.setInterfaces(ClassUtils.getAllInterf aces(this.target)).
This all seemed to work fine until we started stress testing the web application. It seems like there are concurrency issues. Let me explain:
The web service has methods for retrieving a list of roles (getRoles) and to create a single role (createRole). The return value of the getRoles call is cached and the createRole call flushes the cache.
Suppose the cache is empty and thread A calls getRoles. The call will be forwarded to the web service. Thread B calls createRole (which flushes the cache) just after A has retrieved the list of roles from the web service but before the list have been put in the cache. A puts the list in the cache. When B then calls getRoles the stale list of roles will be returned from the cache. B will not see the newly created role in the list.
In 0.2 there is no synchronization of the cache. Preferably, the cache group (we're using OSCache) should be locked when updating/flushing the cache to prevent this problem.
Now my question is whether this have been fixed since 0.2? IIUC the latest CVS code won't work with Spring 1.2.7, right? If I upgrade to Spring 2.0M3 I'd like to know for sure that an upgrade will solve this problem. Will there be a backport of springmodules to work with Spring 1.2?
Regards,
Niklas Therning
Costin Leau
Mar 29th, 2006, 05:11 AM
Please report the issue on Jira. I think Alex, the maintainer of the module, is busy these weeks.
AFAIK, Cache is still compatible with spring 1.2.x - if the features from Spring 2.0m3 are there, they will be used (mainly the xml schema support). Moreover, Spring 2.0 is fully compatible with 1.2.x - you can upgrade spring and your code will run just like before.
Give it a try and in case it doesn't work, use jira so that Alex can take care of it.
slowth
Mar 29th, 2006, 05:40 AM
Ok, so Spring 2.0 features will only be used if available and things will still run though not necessarily compile using 1.2? Sounds, great!
I'll give it a try. I'll submit a bug if I run into the same concurrency problems. Thanks!
/Niklas
Costin Leau
Mar 29th, 2006, 05:52 AM
Ok, so Spring 2.0 features will only be used if available and things will still run though not necessarily compile using 1.2? Sounds, great!
They should work. Give it a try and see the results. Again, if it doesn't, you can use 2.0.
alruiz15
Apr 4th, 2006, 08:01 AM
First of all, thank you very much Costin for following up and for your help :)
Slowth, each cache provider provides its own synchronization mechanism (take a look at the OSCache source code.) The mission of the caching module is to provide a facade and declarative configuration for each of the supported cache providers. As data gets updated frequently, caching is a less attractive option because of concurrency issues. The best use (but not the only one) for caching is for read-only data or data that is not frequently updated (if the application can tolerate stale data).
Regards,
Alex.
slowth
Apr 4th, 2006, 10:11 AM
IMO, it doesn't matter if the provider handles concurrency since the problem lies in AbstractCachingInterceptor. To avoid issues like this there has to be some kind of lock around the proceed() call and the call which updates the cache. A simple synchronize block won't do since it will be extremely inefficient.
In the OSCache case I guess one would like to lock on the group somehow. I had one idea for a patch which involves adding beginUpdate(model)/endUpdate(model) methods to CacheProviderFacade. AbstractCachingInterceptor would then call beginUpdate() just before the call to proceed and then call endUpdate() after the cache has been updated. If another thread comes in and tries to get or flush a value which is being updated it will have to wait until the update has finished. WDYT?
Updates are very infrequent in my app so this problem will be very unlikely. What worries me though is that it COULD happen. And if it can happen I would have to deal with that somehow which probably means I can't do the caching completely transparent.
Regards,
Niklas
alruiz15
Apr 4th, 2006, 11:02 AM
There a lot of things that COULD happen. But it is impossible to go after all of them unless we have enough evidence that something is wrong: a failing test. If OSCache does not fit your needs, you can switch to other high-quality cache providers like EHCache (open source) or Coherence (commercial.) The purpose of the caching module is not to fix or change the implementation of a particular cache. If you need synchronized access to a group (or groups,) the OSCache mailing list is the place to go.
Regards,
Alex.
slowth
Apr 4th, 2006, 01:25 PM
The problem isn't with the particular provider I'm using. It wouldn't matter if I switched to ehcache or any other provider. The problem would remain. The sequence
method.proceed();
...
cacheProviderFacade.putInCache(...);
in AbstractCachingInterceptor has to be atomic. If not, another thread could potentially come in between those calls and make changes to the cached objects. This change will then never be visible since the last putInCache() executed puts an old result in the cache.
Another question is how to achieve this. I have no idea what one would lock on if using other providers then OSCache. And even with OSCache you don't have to use groups...
Now, maybe I'm using springmodules-cache in a way which wasn't intended. I will take your advice and only cache read-only objects.
BTW, thanks for providing this great piece of software! It helps a lot!
Regards,
Niklas
alruiz15
Apr 4th, 2006, 05:58 PM
Hi Niklas,
I don't believe that synchronizing cache access in the CachingInterceptor is a viable solution. We would end up synchronizing an already synchronized access to a data structure. The only way to prevent unwanted flushing of a cache is by having clear knowledge of the business requirements of the application and of course, a set of tests to back up our decision about using (or not using) caching.
BTW, thanks a lot for your kind words about the caching module :)
Regards,
Alex
jeanphil
Apr 1st, 2008, 04:34 PM
I'm having the same problem with my app. I looked at the org.springmodules.cache.interceptor.caching.Abstra ctCachingInterceptor invoke method and I'm thinking of adding the synchronization there:
synchronized(...) {
Object cached = cache.getFromCache(key, model);
if (null == cached) return cachedValueFromSource(mi, key, model);
return unmaskNull(cached);
}
However, it order to make it efficient, I need to synchronize on something meaningful. I was thinking of using the Method object contained in the MethodInvocation since I think there is only one of those for the whole classloader. Unfortunately, invoke is declared final so I guess I'll have to copy over the whole class.
Can anybody see any problems with what I'm trying to do?
Thanks!
JPB
vBulletin® v3.7.3, Copyright ©2000-2008, Jelsoft Enterprises Ltd.