

                        TreeCache Passivation/ Overflow
                        ===============================



Author: Hany Mesha (hmesha@novell.com)
Revision: $Id: TreeCachePassivation.txt 360 2005-08-05 19:28:54Z hmesha $:

JIRA issue: http://jira.jboss.com/jira/browse/JBCACHE-66


Goal: 
-----

When a cache is full and a new object needs to be loaded into the cache, an 
existing object must be removed from the cache to make room for the new object. 
However, re-creating and re-loading a large objects in in-memory cache is 
expensive. Therefore, Passivation/ Activation is a mechanism to avoid such 
performance costly operations.

When an object is called back by the Eviction Policy Provider in the 
TreeCache for eviction, The TreeCache will determine whether to passivate 
the object or simply remove it. The decision will be based on the value of 
CacheLoaderPassivation flag in the cache loader configuration group 
(true/ false) of the cache configuration file.

Passivation is the process of removing an object from in-memory cache and 
persisting it to a secondary data store (i.e. file system, database) on eviction.

Activation is the process of restoring an object from the data store into the 
in-memory cache when it's needed to be used.

In both cases, the configured CacheLoader will be used to read from the data store
and write to the data store.

Configuration:
--------------
- CachePassivation: flag to be set by user to enable/ disable cache passivation
EXAMPLE:

       <attribute name="CacheLoaderClass">org.jboss.cache.loader.FileCacheLoader</attribute>
       <attribute name="CacheLoaderConfig">location=c:\\tmp\\node1</attribute>
       <attribute name="CacheLoaderShared">false</attribute>
       <attribute name="CacheLoaderPreload">/</attribute>
       <attribute name="CacheLoaderFetchTransientState">false</attribute>
       <attribute name="CacheLoaderFetchPersistentState">true</attribute>
       <!-- Cache Passivation for Tree Cache 
       On pasivation, The objects are written to the backend store on eviction if CacheLoaderPassivation 
       is true, otheriwse the objects are persisted.
       On activation, the objects are restored in the memory cache and removed from the cache loader 
       if CacheLoaderPassivation is true, otherwise the objects are only loaded from the cache loader -->
       <attribute name="CacheLoaderPassivation">false</attribute>

Use Case:
---------
See Related Issues section in this document.

Design:
-------

Passivation
-----------
1. implements Interceptor:
--------------------------
Passivation and Activation will be implemented using interceptors; see Refactoring.txt for details on 
interceptors.

- TreeCache.createInterceptorChain() will assemble the interceptor stack starting with the passivation 
interceptor at the bottom of the stack. The passivation interceptor will replace the cache store interceptor 
and the activation interceptor will replace the cache loader interceptor in the stack.

- Passivation will be applied for the whole node and its children therefore the passivation interceptor 
will only act on node evict() on the way in.

- Activation will be applied for the whole node and its children therefore the activation interceptor 
will only act if and only if the node , its attributes, and its children has been loaded in memory, 
on the way out.



2. passivate node on eviction:
------------------------------
When the eviction policy in effect calls its evict() (calls TreeCache.evict(fqn) or TreeCacheAop.evict(fqn)) 
to evict a node from the underlying cache instance, the passivation interceptor will intercept the call and 
store the node and its children in the cache loader store.

The PassivationInterceptor will extend CacheStoreInterceptor. The evict method call will be handled 
by the PassivationInterceptor then the metod call is passed to the parent class.

Pseudo code in PassivationInterceptor:

public Object invoke(MethodCall m) throws Throwable {
    Method            meth=m.getMethod();
    Object[]          args=m.getArgs();

    if(meth.equals(TreeCache.evictNodeMethodLocal) {
         Fqn evictedNode=(Fqn)args[0];
         // the evict() method is called; store the evicted node in the cache loader
         loader.put(evictedNode);
    }
    super.invoke(m);
}

Note: The PassivationInterceptor works on nodes (non-leaf nodes that is) which are not removed from the 
cache on eviction but rather cleared of their data. So in the activation process we still have handle to 
the node.


 
Activation:
-----------
When a user calls a get(fqn) or alike, the ActivationInterceptor will call the CacheLoaderInterceptor, 
ActivationInterceptor extends CacheLoaderInterceptor, to load the node from the cache loader and on the 
way out will remove it from the cache loader.

The criteria of the node to be removed on activation:
-----------------------------------------------------
The node was evicted earlier from the cache. On the way out, the activation interceptor checks the following 
conditions to qualify the "fqn" for removal:
1, Node exists in the memory cache
2. Its attributes are not UNINITIALIZED
3. If the node children in the memory cache and have been loaded, then remove the node from the cache loader
4. If the node doesn't have children in the memory cache, check if they're present in the the CacheLoader 
and haven't been loaded yet. Then don't remove the node from the cache loader.


Pseudo code in ActivationInterceptor invoke method

public Object invoke(MethodCall m) throws Throwable {
   Fqn          fqn=null;
   Node         n=null;
   Method       meth=m.getMethod();
   Object[]     args=m.getArgs();
   Object       retval=null;
            
   // First call the parent class to load the node
   retval= super.invoke(m);
      
   synchronized(this) {
      // On the way out: remove the node from the cache loader.
      // Only remove the node If it exists in the memeory, its attributes has 
      // been initialized, and its children have been loaded 
      // then notify the listeners that the node has been activated
      if(fqn != null && cache.exists(fqn) && !cache.exists(fqn, TreeCache.UNINITIALIZED)) {
         fqn=(Fqn)args[0];
         n=getNode(fqn, false, false); // don't load
         if(n != null) {
            if(n.hasChildren()) {
               if(((TreeNode) n).getChildrenLoaded()) {
                  loader.remove(fqn);
                  cache.notifyNodeActivated(fqn);
               }
            }
            else {
               Set children_names=null;
               try {
                  children_names=loader.getChildrenNames(fqn);
               }
               catch(Exception e) {
                  log.error("failed getting the children names for " + fqn + " from the cache loader", e);
               }
               if(children_names == null) {
                   loader.remove(fqn);
                   cache.notifyNodeActivated(fqn);                      
               }
            }
         }
      }
   }
   return retval;
}

4. Transaction:
---------------
On passivation, The cache passivation interceptor will be provide a put modification in the trnasaction 
modification list and let the parent class, the cache store interceptor, to process it along with other 
modifications in the associated transaction.
 
On activation, it's a get therefore, there's no transaction process involved.

Related Issues:
---------------

- JBCLUSTER-15
Affected packages in Tomcat module: org/jboss/web/tomcat/tc5/session

Currenlty JBoss Clustering HTTP Session replication doesn't support passivation.  Once the cache passivation 
is done and the JBoss Cache version included in jboss-head branch is updated, this feature can be added. 

1. org.jboss.web.tomcat.tc5.session.CacheListener needs to implement nodePrePassivated(fqn) and 
nodeActivated(fqn) methods that have been added to TreeCacheListener interface as part of the cache 
passivation feature implementation. 

2. ClusteredSession subclasses use JBossCacheService to process session replication. In JBossCacheService 
when remove local method (e.g. removeAttributesLocal(), removePojosLocal(), removeSessionLocal()) is called, 
calls the cache evict method which triggers passivation interceptor which fires notifyNodePrePassivated() 
to notify the cache listeners which calls nodePrePassivated() on the cache listeners.

In CacheListener.nodePrePassivated()???

3. JBossCacheService loadSession() is used load the session from the underlying cache instance which trigger 
the activation interceptor. Eventually, notifyNodeActivated() will be fired to notify cache listeners by 
calling nodeActivated() on the listeners.

In CacheListener.nodeActivated()???

BEN: httpsession is depecrated. We are using JBossCache now. Check into Tomcat module/.../tc5/session for 
details.

All you need is add two events in TreeCacheListener: prePassivated and afterActivated first. 
Then, subsriber of the events (e.g., Tomcat http session) can act accordingly.

Same thing for EJB3 as well. We will need to refactor Bill's implementation for SFSB to use JBossCache 
passivation/activation mechanism.
.

- JBCLUSTER-40
Affected packages in ejb3: org.jboss.ejb3.cache.tree, org.jboss.ejb3.stateful

Currently ejb3 has its own activation/ passivation mechanism which uses JBoss Cache File cache loader to 
passivate and activate sfsb. Once JBoss Cache implements this feature, ejb3 should re-work its passivation 
policy to take advantage of the new feature as follow:
1. Add to org.jboss.cache.TreeCache public EvictionPolicy getEvictionPolicy() and remove 
org.jboss.ejb3.cache.tree.PassivationTreeCache
2. Change the implementation in org.jboss.cache.treeCache.StatefulTreeCache.
3. Add to org.jboss.cache.eviction.BaseEviction public void createRegion(String name, int maxSize, 
long timeToLive) 
and public void removeRegion(String name) to act on the RegionManager. Then, remove 
org.jboss.ejb3.cache.tree.PassivationEvictionPolicy
4. Change the implementation in org.jboss.cache.tree.StatefulEvictionPolicy.
5. Remove org.jboss.ejb3.cache.tree.PassivationCacheLoader, 
6. Change the implementation in org.jboss.ejb3.cache.tree.StatefulCacheLoader
7. Refactor the changes in org.jboss.ejb3.stateful where necessary
8. turn the passivation flag to true in the cache service on the sfsb container startup???
