While fixing some of minor bugs in my
project , I found out lot of database hits with same parameters , which
was returning the same result. So I thought of bringing up a method where result
need to be cached based on parameter. But this needs a code change in all
areas, to avoid that I came up with POC for Dynamic proxy (also called as
interceptor), which will be take care of the intercepting the method and
will build cache and return cached value.Next
fetch will be from cache when the parameters are same.
This can be
implemented even when we are looking to change the whole repository fetch
logic, interceptor can be plugged out when the refactoring is complete. I have
used normal HTTP cache for explanatory purpose, but my code has flexible to
implement any cache and its invalidating logic.
Performance is better 30 times(check
image below), Result is intercepting FormRendererRepository
and NavigationRepository alone. First time load was 3.27 sec and second time
load was 0.156 sec. I have used simple child form, which has Primary
Navigation bar, Secondary navigation bar and two textboxes.
Advantages of using Interceptor
- Changes to existing code is zero
- Cache that needs to be used is 100% pluggable. You just need to inject the cache you need to access. All the cache access is part of developers job, so we can take measure to invalidate the Cache as per requirement.
- You can intercept only class that you need intercepting, other classes can work without being affected
- Since we are injecting the cache using proxy generator, Cache can be switched on & off
- Any type of cache can be used and alternated depending on environment(eg.. HttpCache(ASP.Net Server) or ARP (external cache server)).
Details about Implementation
Here I have used CastleOrg for interception and structure map for Injection.
In the POC I have used form rendering where the repository calls the database
for getting data. So I have intercepted the FormRepository and
CentralObjectPanelRepository. The interceptor duty is to check for cache value
with parameter passed when any public method is invoked. Cache key is created with
method name concatenated with parameter values. If the parameter is a model
then propertyname and propertyvalue is traversed and concatenated with method
name(This logic can be revisited and optimized). If the created key exist in
the Cache, then it’s value will be fetched from cache else the normal
invocation of function takes place. Have left out the flow of saving back to
cache after initial fetch for explanatory purpose.
Below class diagram explains that
ICacheprovider is pluggable, any cache mechanism that implements
ICacheprovider can be part of the Interceptor mechanism.
Cache should be plugged in using below
code.
var proxyGenerator = new ProxyGenerator();
For<CacheInterceptor>();
ForRequestedType<ICacheProvider>).CacheBy(InstanceScope.Singleton).Use<HttpCache>();
For<ICentralObjectPanelRepository>().EnrichAllWith(x=>proxyGenerator.CreateInterfaceProxyWithTarget<ICentralObjectPanelRepository>(x, new CacheInterceptor()));
For<IFormRepository>().EnrichAllWith(x=> proxyGenerator.CreateInterfaceProxyWithTarget<IFormRepository>(x, new CacheInterceptor()));
Invocator has the following code while
intercepting the class.
public void
Intercept(IInvocation invocation)
{
//check if the method has a return value
if (invocation.Method.ReturnType == typeof(void))
{
invocation.Proceed();
return;
}
var cacheKey = BuildCacheKeyFrom(invocation);
//try
get the return value from the cache provider
object item = null;
cacheProvider.Get<object>(cacheKey,out
item);
if (item != null)
{
invocation.ReturnValue = item;
return;
}
//call the intercepted method
invocation.Proceed();
if (invocation.ReturnValue != null)
{
cacheProvider.Set<object>(cacheKey,invocation.ReturnValue,
CacheExpiryMinutes);
}
return;
}Limitation of Interceptor
·
Implementing the interceptor
does no job with any of the cache functionality and it is our responsibility to
implement any type of cache and how it must be invalidated.
·
Method level intercepting needs
additional changes of code
·
When we have a very complex
model then the parameter string formed which is used for cache key will be very
huge, this can solved by ignoring the method completely for caching.
`The idea of blog is to let u know the way of
doing things better using cache without any change in existing code which will make the web page
render faster