Wednesday, 6 November 2013

Performance Improvement without Code Change


         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

No comments:

Post a Comment

Note: only a member of this blog may post a comment.

Build Bot using LUIS