Infinispan Integration in AS6

Version 3

    As of version 6.0.0.CR1, Infinispan replaced JBoss Cache as the distributed caching technology upon which JBoss AS clustering services are built.

     

    What is Infinispan?

    http://community.jboss.org/wiki/WhatisInfinispan

     

    What impact will this have on my distributed application?

     

    That depends on the extent to which you customized the default clustering behavior.  A distributed application using the default web session cache and/or default stateful session bean cache without modifications will be able to run in AS6 seamlessly.  You will, however, need to make some modifications to your application/configuration if any of the following applies:

     

    • If you previously customized the default JBoss Cache configuration, these changes must be ported to Infinispan.  While migrating configuration from JBC to Infinispan is largely trivial, a migration tool is available for the lazier amongst us:  http://community.jboss.org/wiki/ConfigurationMigrationTools
    • If you used JBoss Cache as your Hibernate 2nd level cache provider, you will need to modify your hibernate/jpa configuration to instead point to Infinispan's 2nd-level cache provider.  This is described in detail below.
    • Distribution mode is the Infinispan replacement for JBoss Cache buddy replication.  See  http://infinispan.blogspot.com/2009/08/distribution-instead-of-buddy.html for details.
    • If your application previously accessed a JBoss Cache directly - you will need either bundle JBoss Cache and your configuration with your application or consider porting to Infinispan (our recommendation).  Infinispan provides an addition tree API module to ease migration from JBoss Cache.  Several methods for referencing an Infinispan Cache from your application are described are outlined below.
    • FIELD replication granularity (formerly provided by POJO Cache) is not currently supported by Infinispan, though the required fine-grained API is planned for a later release (see ISPN-24).  An application configured with FIELD granularity web session replication will automatically fallback (with a log warning) to SESSION granularity.

    Under the hood

    In the previous JBoss Cache backed clustering implementation, we used a shared JBoss Cache instance per clustering service for all applications running on a given server.  A JBC cache used a single JGroups channel and application specific objects (e.g. web sessions) were stored within application specific branches within the shared tree structure.

     

    With the conversion to Infinispan, the mapping of application data to a cache is slightly different.  By default, we use a single Infinispan cache container (aka, cache manager) instance per clustering service.  A cache container uses a single JGroups channel.  Application specific objects are stored within an application specific cache instances.  The following chart illustrates the relationship between JBoss Cache and Infinispan constructs:

     

    JBoss CacheInfinispan
    CacheManager, a registry of named cachesCacheContainerRegistry, a registry of named cache containers
    CacheCacheContainer, a container of named caches sharing the same channel
    Cache nodeCache
    Cache node entryCache entry

     

    CacheContainerRegistry

    As the name suggests, the cache container registry is a registry of named cache containers.  The cache container registry is deployed via the following file:

    $JBOSS_HOME/server/all/infinispan-cache-registry.sar/cache-container-registry-jboss-beans.xml

     

    <bean name="CacheContainerFactory" class="org.jboss.ha.ispn.DefaultCacheContainerFactory">
      <constructor>
        <parameter><inject bean="JChannelFactory"/></parameter>
        <parameter><inject bean="JMXKernel" property="mbeanServer"/></parameter>
      </constructor>
    </bean>
    
    <bean name="CacheContainerRegistry" class="org.jboss.ha.ispn.DefaultCacheContainerRegistry">
      <constructor>
        <parameter><inject bean="CacheContainerFactory"/></parameter>
        <parameter>
          <bean class="org.jboss.ha.ispn.config.xml.XMLCacheContainerRegistryConfigurationSource">
            <constructor>
              <!-- Location of infinispan configuration file -->
              <parameter class="java.lang.String">infinispan-configs.xml</parameter>
            </constructor>
          </bean>
        </parameter>
      </constructor>
    </bean>
    

     

    The registry uses 2 sub-components:

    1. A factory for creating an Infinispan cache container.  The default implementation allows Infinispan to obtain its channel from the default AS channel factory and ensures that Infinispan performs any JMX registration with the AS's default mbean server.  The factory also implements a workaround for ISPN-658.
    2. A source of configuration for the registry.  The default implementation parses a set of Infinispan configurations from a single xml file.  This file is described below.

     

    When the registry is started, a cache container is created and started for each entry.  Starting a cache container registers it with JMX, but does not start its JGroups channel, nor create any caches.  Caches are started on demand.  The container's channel is not started until the 1st cache is requested.  When the registry is stopped, each cache container is stopped (including the container's channel), along with any caches that are still running.

    infinispan-configs.xml

    The default CacheContainerRegistry configuration resides in the file located at:

    $JBOSS_HOME/server/all/cluster/infinispan-cache-registry.sar/infinispan-configs.xml

     

    The file is effectively wrapper xml around a series of Infinispan cache container configuration blocks.  For each container configuration block, we define an additional name, set of aliases, and an optional jndi-name.  If a jndi-name is defined, then the cache container will be bound to JNDI.

    e.g.

    <?xml version="1.0" encoding="UTF-8"?>
    <infinispan-configs default="ha-partition" xmlns="urn:jboss:infinispan-configs:1.0">
      <!-- web tier Clustered Single Sign-On, HA-JNDI, Distributed State -->
      <infinispan-config name="ha-partition">
        <alias>clustered-sso</alias>
        <infinispan xmlns="urn:infinispan:config:4.2">
          <global>
            <transport clusterName="${jboss.partition.name:DefaultPartition}-HAPartition" distributedSyncTimeout="17500">
              <properties>
                <property name="stack" value="${jboss.default.jgroups.stack:udp}"/>
              </properties>
            </transport>
            <globalJmxStatistics enabled="true"/>
            <shutdown hookBehavior="DONT_REGISTER"/>
          </global>
          <default>
            <locking isolationLevel="REPEATABLE_READ" lockAcquisitionTimeout="15000" useLockStriping="false" concurrencyLevel="1000"/>
            <jmxStatistics enabled="true"/>
            <lazyDeserialization enabled="false"/>
            <invocationBatching enabled="true"/>
            <clustering mode="replication">
              <stateRetrieval timeout="60000" fetchInMemoryState="true"/>
              <sync replTimeout="17500"/>
            </clustering>
          </default>
        </infinispan>
      </infinispan-config>
      ...
    </infinispan-configs>

     

    While its nice to define all cache containers in a single file, you may find it preferrable to package an application specific cache container configuration with your application.  Since the distributed web session and clustered stateful session bean implementations use the CacheContainerRegistry to create their respective caches, if you want to package a cache container configuration with your application (within its jar/war/ear), you'll want to install the configuration into the registry on deploy, and uninstall it on undeploy.

    e.g.

    <bean name="CustomCacheContainerRegistryConfigurationEntry"
          class="org.jboss.ha.ispn.config.CacheContainerRegistryConfigurationEntry">
      <!-- When we're installed, register ourself with the registry -->
      <install bean="CacheContainerRegistry" method="add">
        <parameter><this/></parameter>
      </install>
        
      <!-- When we're uninstalled, unregister ourself from the registry -->
      <uninstall bean="CacheContainerRegistry" method="remove">
        <parameter><this/></parameter>
      </uninstall>
      
      <property name="id">custom</property>
      <property name="configuration">
        <bean class="org.jboss.ha.ispn.CacheContainerConfiguration">
          <constructor>
            <parameter>
              <bean class="org.infinispan.config.GlobalConfiguration">
                <!-- Define global configuration properties -->
              </bean>
            </parameter>
            <parameter>
              <bean class="org.infinispan.config.Configuration">
                <!-- Define default configuration properties -->
              </bean>
            </parameter>
          </constructor>
        </bean>
      </property>
    </bean>

     

    Default Cache Configurations

    The default infinispan-configs.xml contains the following cache configurations:

     

    ContainerCacheUsage
    ha-partitiondefaultUsed by clustered single-sign-on, HA-JNDI, and Distributed State.
    webdefaultThe default cache configuration used for distributed web sessions, using asynchronous replication.
    websyncLike the above, but uses synchronous replication mode.
    webdistA distributed web session cache configuration variant using distribution mode.
    sfsbdefaultThe default cache configuration used for clustered EJB3 stateful session beans, using asynchronous replication.
    sfsbsyncLike the above, but uses synchronous replication mode.
    sfsbdistA clustered EJB3 stateful session bean cache configuration variant using distribution mode.
    hibernateentityThe default cache configuration for entities and collections, uses invalidation mode, and READ_COMMITTED transaction isolation.
    hibernateentity-repeatableLike the above, but uses REPEATABLE_READ transaction isolation.
    hibernatereplicated-entityAn alternative cache configuration for entities and collections using replication instead of invalidation.
    hibernatelocal-queryThe default cache configuration for query caching, non-replicating.
    hibernatereplicated-queryA query cache configuration that replicates queries synchronously.
    hibernatetimestampsThe default cache configuration for timestamps.  N.B. Replicated timestamps are required even for local query caching.

     

    Aliases

    In the previous JBoss Cache based AS clustering implementation, JBC's CacheManager supported cache aliases; that is, the same cache could be referenced by multiple names.  In the Infinispan CacheContainerRegistry, we support cache container aliases (remember that the role of an Infinispan cache container is effectively analogous to a JBC cache), where the same cache container can be referenced by multiple names.

    e.g.

    <infinispan-config name="web">
      <alias>standard-session-cache</alias>
      <infinispan xmlns="urn:infinispan:config:4.2">
        ...
      </infinispan>
    </infinispan-config>

     

    Distributed Web Sessions

    The Infinispan-backed distribution web session implementation uses a separate cache instance per web application.  By default, this cache instance is based on a clone of the default cache configuration from the "standard-session-cache" cache container ("standard-session-cache" is an alias of the "web" cache container).  The new cache is uniquely named based on the web application's host and context path.

     

    There are a number of ways to manipulate the distributed caching behavior for a given web application:

     

    1. A distributed web application can override the default template cache via the replication-confg/cache-name element in the application's jboss-web.xml file.  The value of the cache-name may identify the default cache configuration of a specific cache container, or a specific named cache configuration if the "/" delimiter is present.

       

      e.g.  The following web application will use the "dist" cache configuration from the "web" cache container.

      <jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" version="6.0">
        <replication-config>
          <cache-name>web/dist</cache-name>
        </replication-config>
      </jboss-web>

       

    2. AS6 also adds the ability to manipulate the cache mode of a given web application via new replication-mode and backups elements.

       

      e.g. The following web application will use the default cache configuration from the "standard-session-cache" (a.k.a. "web") cache container, but overrides the cache mode to use DIST_SYNC, where num_owners is 4.

      <jboss-web xmlns="http://www.jboss.com/xml/ns/javaee" version="6.0">
        <replication-config>
          <replication-mode>SYNCHRONOUS</replication-mode>
          <backups>3</backups>
        </replication-config>
      </jboss-web>

       

    3. Alternatively, you can define an application specific cache configuration in infinispan-configs.xml.  The cache configuration name should adhere to the format: "//host/context-path".

      e.g.

      <infinispan-configs xmlns="urn:jboss:infinispan-configs:1.0">
        ...
        <infinispan-config name="web">
          <alias>standard-session-cache</alias>
          <infinispan xmlns="urn:infinispan:config:4.2">
            <global>...</global>
            <default>...</default>
            <namedCache name="//localhost/myapp">
              ...
            </namedCache>
        </infinispan-config>
        ...
      </infinispan-config>

       

    Load Balancing and Distribution Mode

    Unlike plain replication, when using distribution mode, a given session may not necessarily be stored in a particular node's cache instance.  The nodes on which a given session will be stored are determined by a consistent hashing algorithm that considers the num_owners c current cluster topolgy.  When a request arrives for a new session, the load balancer is generally free to forward the request to a node of its choosing.  When determining the session id of a new request, we make sure that the value returned will hash to the local node.

    During failover, where the session id is already known, your load balancer will not know which nodes have the requested session in their local cache.  If the selected node's cache instance does not contain the requested session (i.e. it's stored on some other node), then this request will incur the cost of remotely reading the session.  When the completed request returns to the client, its jvm route will indicate, not the node that processed the request, but rather a random node from the set of nodes to which the current session hashes.  If the current node happens to be in this set, then it will take precedence.  In this way, subsequent requests for that session will be directed to a node on which the session is known to cache locally.

    Clustered Stateful Session Beans

    The Infinispan-backed clustered stateful session bean implementation uses a separate cache instance per bean deployment.  By default, this cache instance is based on a clone of the default cache configuration from the "sfsb-cache" cache container ("sfsb-cache" is an alias of the "sfsb" cache container).  The new cache is uniquely named based on the EAR, JAR and EJB name.

     

    The default template cache can be overridden for a specific SFSB via either the @CacheConfig annotation, or via the cache-config/cache-name element within the jboss.xml deployment descriptor.   The value of the @CacheConfig name, or cache-name may identify the default cache configuration of a specific cache container, or a specific named cache configuration if the "/" delimiter is present.

     

    e.g. The following clustered stateful session bean will use the "sync" cache configuration from the "sfsb" cache container.

    @Stateful
    @Clustered
    @CacheConfig(name="sfsb/sync")
    public class MyBean
    {
    }
    <jboss xmlns="http://www.jboss.com/xml/ns/javaee" version="6.0">
      <enterprise-beans>
        <session>
          <ejb-name>MyBean</ejb-name>
          <clustered>true</clustered>
          <cache-config>
            <cache-name>sfsb/sync</cache-name>
          </cache-config>
        </session>
      </enterprise-beans>
    </jboss>

     

    JPA/Hibernate 2nd Level Cache

    Configuration of a JPA 2nd level cache is enabled via your application's persistence.xml file.

    e.g.

    <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
      <persistence-unit name="tempdb" transaction-type="JTA">
        <jta-data-source>java:/DefaultDS</jta-data-source>
        <properties>
          <property name="hibernate.cache.use_second_level_cache">true</property>
          <property name="hibernate.cache.use_query_cache">true</property>
          <property name="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.JndiInfinispanRegionFactory</property>
          <property name="hibernate.cache.infinispan.cachemanager">java:CacheManager/entity</property>
        </properties>
      </persistence-unit>
    </persistence>

     

    The Infinispan region factory knows nothing of the CacheContainerRegistry. It instead looks up the relevant cache container (i.e. cache manager) in JNDI.  The name to which the hibernate cache container is bound is defined in infinispan-configs.xml:

     

    <infinispan-config name="hibernate" jndi-name="java:CacheManager/entity">
      <infinispan-config xmlns="urn:infinispan:config:4.2">
        ...
      </infinispan-config>
    </persistence>

     

    In addition to the configuration properties listed above, the following properties can be used to further customize the behavior of individual cache regions.

     

    PropertyDefaultUsage
    hibernate.cache.infinispan.entity.cfgentityDefines the template cache for use with entity cache regions
    hibernate.cache.infinispan.collection.cfgentityDefines the template cache for use with collection cache regions
    hibernate.cache.infinispan.query.cfglocal-queryDefines the template cache for use with query cache regions. Query caching requires "hibernate.cache.use_query_cache" property set to "true", and must be enabled on a per-query basis.
    hibernate.cache.infinispan.timestamps.cfgtimestampsDefines the template cache for use timestamp cache regions.

     

    For more detail and addition configuration options for the Infinispan Hibernate 2nd level cache implementation, see this document:

    http://community.jboss.org/wiki/UsingInfinispanasJPAHibernateSecondLevelCacheProvider


    Using Infinispan Directly

    Your application may want to use an Infinispan cache directly for its own purposes.  While you could technically configure and access an Infinispan cache with no help from the AS, utilizing the CacheContainerRegistry - or at the very least, the CacheContainerFactory - allows you to inject AS managed components in your cache (e.g. an AS provided JGroups channel).

     

    The example bean configuration below creates an Infinispan cache container from the CacheContainerFactory, using a jgroups channel from AS-supplied "udp" stack.  The cache container will also register with the AS mbean server, under the name "custom".  This bean also encapsulates the setup and cleanup logic of the cache container (and any caches created from it).

     

    <!-- Factory created cache container -->
    <bean name="CustomCacheContainer" class="org.infinispan.manager.CacheContainer">
      <constructor factoryMethod="createCacheContainer">
        <factory bean="CacheContainerFactory"/>
        <parameter>
          <bean class="org.jboss.ha.ispn.CacheContainerConfiguration">
            <constructor>
              <parameter>
                <bean class="org.infinispan.config.GlobalConfiguration">
                  <property name="clusterName">${jboss.partition.name:DefaultPartition}-Custom</property>
                  <property name="transportProperties">
                    <map class="java.util.Properties" keyClass="java.lang.String" valueClass="java.lang.String">
                      <entry>
                        <key>stack</key>
                        <value>udp</value>
                      </entry>
                    </map>
                  </property>
                  <property name="exposeGlobalJmxStatistics">true</property>
                  <property name="cacheManagerName">custom</property>
                  <!-- Define global configuration properties -->
                </bean>
              </parameter>
              <parameter>
                <bean class="org.infinispan.config.Configuration">
                  <!-- Define default configuration properties -->
                </bean>
              </parameter>
            </constructor>
          </bean>
        </parameter>
      </constructor>
    </bean>

     

    If your cache needs to be shared across applications, or if you merely wish to have all infinispan configurations stored in a single location, you can define a custom cache container within the infinispan-cache-registry.sar/cache-container-registry-jboss-beans.xml file, and expose the cache container as a bean.

    e.g.

    <!-- Cache container obtained from registry -->
    <bean name="CustomCacheContainer" class="org.infinispan.manager.CacheContainer">
      <constructor factoryMethod="getCacheContainer">
        <factory bean="CacheContainerRegistry"/>
        <parameter>custom</parameter>
      </constructor>
      <!-- Let registry control container's lifecycle -->
      <start ignored="true"/>
      <stop ignored="true"/>
    </bean>

     

    Now that the cache container is deployed as a bean using one of the above mechanisms, it can be injected into any application object.

    e.g.

    <bean name="MyObject" class="MyObject">
      <constructor>
        <parameter><inject bean="CustomCacheContainer"/></parameter>
      </constructor>
    </bean>

     

    import org.infinispan.Cache;
    import org.infinispan.manager.CacheContainer;
    
    public class MyObject
    {
      private Cache<Object, Object> cache;
    
      public MyObject(CacheContainer container)
      {
        this.cache = container.getCache();
      }
    }

     

    Happy coding...