Wednesday, December 30, 2015

Caching Data Using MOCA

Overview

We often need to cache some data during any implementation.  If you are creating a C component, that may take form of a "static" variable.  You could sometimes use "session variables" - if intended scope is a transaction.  You could also employ "Environment Variables". 

RedPrairie software itself utilizes caching in several algorithms.  Most of the implementations use C static variables.  This path is fine but requires a lot of housekeeping code and also prone to bugs - especially memory leaks.

As of 2011, MOCA has introduced external caching.  One such caching provider that MOCA supports is "infinispan".

Possibilities

This opens up some very interesting possibilities.  In this blog, I will describe a solution that we incorporate in almost all of our implementations, i.e. we cache results of an arbitrary MOCA command such that if the same command is called again - it simply returns data from the cache.  The solution is quite simple - thanks to the wonderful concepts implemented in MOCA.

MOCA Command "execute ossi moca and cache"

Whenever we need to call a command whose result can be cached, we execute it via this interface.  It is similar to the "execute server command" MOCA command - but the difference is that the results can be cached and if called again, it will return data from the cache.

It has following input parameters:
Argument Description Comments
uc_moca_cmd This is the exact command that needs to be executed Similar to what you will pass to execute server command
uc_inline Should the command have access to stack variables? Similar to "inline" parameter of execute server command
uc_cache_key The results of the execution will be saved in cache with this key This will include some key data for the execution so that it can be looked up again. You could default it to MOCA command itself but that will not work very well for inline contexts. Most of the time you can come up with primary key of this cache node. For example policy table primary key or area key etc.
uc_cache_grp The cache can be made up of several groups For example policy caches can be placed in one group, areas in another

While such caching is often very useful in production environments - it can be a pain in development environment where we are frequently changing the data that is cached.  That is why it is a good idea to support an environment variable to disable caching.  When the caching is disabled - this behaves exactly like "execute server command".  A sample implementation may be as follows:
publish data
where uc_inhibit_ossi_cache = @@UC_INHIBIT_OSSI_CACHE
|
if ( @uc_inhibit_ossi_cache = '1' )
   execute server command
   where cmd = @uc_moca_cmd
   and inline = @uc_inline
else
   execute ossi moca and cache core


MOCA Command "execute ossi moca and cache core"

This is the main command that utilizes the caching functionality in MOCA.  It works off the same input as the above command.  It first looks up the the cache for the passed in key.  If found it returns the complete result set from the cache.  If not found - it will execute the command, put the result set in cache and then return the data.  A sample implementation may be as follows:
publish data
where uc_my_infispan_cache = nvl(@uc_cache_grp, 'OSSI__MISC' )
and   uc_my_inline = string ( nvl(@uc_inline,0) )
|
{
   [[
   import com.redprairie.moca.cache.*;
   import com.redprairie.moca.cache.infinispan.*;
   //
   // This creates the cache.  Each cache group becomes a different cache.
   //
   MocaCache cache = CacheManager.getCache(uc_my_infispan_cache, 
                        new InfinispanCacheProvider(), null, null); 
   //
   // First get the cache_key from the cache.  We always put the full resultset in cache
   // so the get is expected to return that.
   //
   my_rs = cache.get ( uc_cache_key );

   //
   // If we do not find resultset in cache - then we execute the command (inline or not)
   // after executing, we put the whole result set in cache
   //
   if ( my_rs == null )
   {
      if ( uc_my_inline == "1" )
         my_rs = moca.executeInline ( uc_moca_cmd )
      else
         my_rs = moca.executeCommand ( uc_moca_cmd )
      //
      cache.put ( uc_cache_key, my_rs );
   }

   //
   // If we found in cache or executed - by this time we have the resultset
   //
   [ret:my_rs]
   ]]
   |
   //
   // we publish the resultset
   publish data combination
   where res = @ret
}


Use Cases

This comes in quite handy.  For example in complex integration projects we often have lookup tables - this improves the performance significantly in that case.  Similarly data in policies, area, storage polices, pick policies, etc. is quite static and can take advantage of this capability.  

In addition to this, this method makes the concept quite abstract and requires minimum housekeeping.  All we need to do is construct the command and execute it.  MOCA allows the whole recordset to be cached so we can thus cache complex information.

Additional Documentation

For additional features and detailed documentation, refer to "MOCA Developer Guide" chapter on "Caching"

3 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. This comment has been removed by a blog administrator.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete