JBoss Development is brought to you in partnership with:

Manik Surtani is a core R&D engineer at JBoss and project lead on JBoss Cache. He has a background in artificial intelligence and neural networks, a field he left behind when he moved from academic circles to the commercial world. Since then, he's been working with Java-related technologies, first for a startup, focusing on knowledge management and information exchange. He later worked for a large London-based consultancy as a tech lead focused on e-commerce applications on large J2EE and peer-to-peer technology. Manik is a strong proponent of open source development methodologies, ethos, and collaborative processes, and often speaks at Java User Groups around the world. Manik is a DZone MVB and is not an employee of DZone and has posted 39 posts at DZone. You can read more from them at their website. View Full User Profile

Infinispan for the Power-user: Cache Modes

03.30.2010
| 22564 views |
  • submit to reddit

This is the second part of an in-depth series of articles on Infinispan.  In this article I'd like to talk about Infinispan's different cache modes, and why you'd use one over the other.

Read the other parts in this series:

Part 1 - Remoting
Part 2 - Cache Modes
Part 3 - Event Notifications

 

Infinispan is primarily a peer-to-peer system where cache instances are expected to live within the same JVM of your application.  

NOTE: As of Infinispan 4.1.0, Infinispan also supports client-server style of interaction, but that is outside the scope of this article and will be addressed in a separate article once Infinispan 4.1.0 is released.

 

Infinispan supports 3 broad clustered modes, and a single non-clustered mode.  Further, the clustered modes could be configured to use a synchronous or asynchronous transport for network communications.  Let's start with the local mode.

Local
While Infinispan is particularly interesting in clustered mode, it also offers a very capable local mode, where it acts as a simple, in-memory data cache similar to JBoss Cache and EHCache.  But why would one use a local cache rather than a map?  Caches offer a lot of features over and above a simple map, including write-through and write-behind caching to persist data, eviction of entries to prevent running out of memory, and support for expirable entries.  Infinispan, specifically, is built around a high-performance, read-biased data container which uses modern techniques like MVCC locking – which buys you non-blocking, thread-safe reads even when concurrent writes are taking place.  Infinispan also makes heavy use of compare-and-swap and other lock-free algorithms, making it ideal for high-throughput, multi-CPU/multi-core environments.  Further, Infinispan's Cache API extends the JDK's ConcurrentMap – making migration from a map to Infinispan trivial.  Performance benchmarks in local mode are available in this blog article.

Replication
Replication is a simple clustered mode where cache instances automatically discover neighboring instances on other JVMs on the same local network, and form a cluster.  Entries added to any of these cache instances will be replicated to all other cache instances in the cluster, and can be retrieved locally from any instance.  This clustered mode provides a quick and easy way to share state across a cluster, however replication practically only performs well in small clusters (under 10 servers), due to the number of replication messages that need to happen – as the cluster size increases.  Infinispan can be configured to use UDP multicast which mitigates this problem to some degree.


Invalidation

Invalidation is a clustered mode that does not actually share any data at all, but simply aims to remove data that may be stale from remote caches.  This cache mode only makes sense if you have another, permanent store for your data such as a database and are only using Infinispan as an optimization in a read-heavy system, to prevent hitting the database every time you need some state.


Invalidation allows you to store state in the cache, but every time an entry is modified, all remote caches are notified that the entry has been modified and if the remote caches happen to have a cached copy, this cached copy is purged.


Distribution
Distribution is a powerful clustering mode which allows Infinispan to scale linearly as more servers are added to the cluster.  Distribution makes use of a consistent hash algorithm to determine where in a cluster entries should be stored.  This algorithm is configured with the number of copies of each entry that should be maintained cluster-wide.  This represents the tradeoff between performance and durability of data.  The more copies you maintain, the lower performance will be, but also the lower the risk of losing data due to server outages.  But regardless of how many copies are maintained, distribution still scales linearly and this is key to scalability.  Another feature of the consistent hash algorithm is that it is deterministic in locating entries without resorting to multicasting requests or maintaining expensive metadata.  This means that doing a PUT would result in at most num_copies remote calls, and doing a GET anywhere in the cluster would result in at most 1 remote call.  In reality, num_copies remote calls are made even for a GET, but these are done in parallel and as soon as any one of these returns, the entry is passed back to the caller.

To prevent repeated remote calls when doing multiple GETs, L1 caching can be enabled.  L1 caching places remotely received values in a near cache for a short period of time (configurable) so repeated lookups would not result in remote calls.  In the above diagram, if L1 was enabled, a subsequent GET for the same key on Server3 would not result in any remote calls.

L1 caching is not free though.  Enabling it comes at a cost, and this cost is that every time a key is updated, an invalidation message needs to be multicast to ensure nodes with the entry in L1 invalidates the entry.  Further, this adds a memory overhead as L1 caches take up space.  Is L1 caching right for you?  The correct approach is to benchmark your application with and without L1 enabled and see what works best for your access pattern.

Synchronous or asynchronous?

Orthogonal, but still related to clustered modes discussed above, is whether remote calls made are synchronously or asynchronously.  Synchronous simply means that any remote call made will block until the recipient applies the change and sends back an acknowledgement.  Asynchronous mode is where remote calls are made, but the caller does not wait for an acknowledgement, but simply assumes the remote call was successful and returns.  Choosing between synchronous and asynchronous is a tradeoff between performance and guarantees that remote calls succeed.  Any of the clustered modes discussed above can be configured to be synchronous or asynchronous.

AttachmentSize
Figure1.png136.36 KB
Figure2.png35.06 KB
Figure3.png48.67 KB
Figure4.png42.23 KB
Published at DZone with permission of Manik Surtani, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Peter Veentjer replied on Wed, 2010/03/31 - 5:59am

Hi Manik,

Nice article.

Here are a few questions:

1) you are using MVVC to provide read consistency.  Is that statement level read consistency (so read committed) or transaction level read consistence (so repeatable read). The last one increases the chance of livelocking but does provide better consistency guarantees.

2) Normally MVCC systems suffer from writeskew problem:

http://pveentjer.wordpress.com/2008/10/04/breaking-oracle-serializable/

does your approach suffer from that as well? 

3) Do you also provide failure atomicity? So all changes make it, or non make it?

4) MVCC is timestamp based, but in a distributed environment there is not a single shared clock (unless you want a non scalable system). So what are you using for the timestamp? The reason why I'm asking this is that I'm working on solving that issue for a distributed STM implementation (I'm using a vector clock to solve that problem).

 

Peter Veentjer

Multiverse: Software Transactional Memory for Java

http://multiverse.codehaus.org

 

 

Manik Surtani replied on Thu, 2010/04/01 - 5:55am

Hi Peter

1) We support both READ_COMMITTED and REPEATABLE_READ, with READ_COMMITTED being the default. See this config reference link for details on how to configure this. This wiki page has more info as well.

2) Write skews only really occur when using REPEATABLE_READ. READ_COMMITTED just assumes the latest committed version and overwrites anyway; R_R is where you have issues when, for example, updating a shared counter. Write skews are detected easily enough, how they are handled depends on how you configure Infinispan: ignore the write skew and overwrite, or abort the tx.

3) Yes, if using transactions.

4) MVCC is not necessarily timestamp based - all it is, is versioned. So it needs to have a notion of the current, committed version and older versions, etc. Rather than timestamps, we use object refs and compare the object ref of the known, committed version against an update coming in. This is used to detect write skews. In the distributed case, locks are acquired before updates take place to prevent a concurrent object ref swap. These locks are pretty short lived though (duration of the object ref swap, cluster-wide).

HTH
Manik

Peter Veentjer replied on Tue, 2010/05/04 - 9:18am in response to: Manik Surtani

2) Write skews only really occur when using REPEATABLE_READ. READ_COMMITTED just assumes the latest committed version and overwrites anyway; R_R is where you have issues when, for example, updating a shared counter. Write skews are detected easily enough, how they are handled depends on how you configure Infinispan: ignore the write skew and overwrite, or abort the tx.

 

It is correct that from a coding perspective the writeskew is very easy to detect, but it also means that you need to track the readset and to validate the readset when you commit. So writeskew detection consumes a lot of resources.

 

4) MVCC is not necessarily timestamp based - all it is, is versioned. So it needs to have a notion of the current, committed version and older versions,

 A timestamp create by a logical clock is still a stimestamp (but that is a definition question).

etc. Rather than timestamps, we use object refs and compare the object ref of the known, committed version against an update coming in. This is used to detect write skews.

So you are tracking reads or not?

In the distributed case, locks are acquired before updates take place to prevent a concurrent object ref swap. These locks are pretty short lived though (duration of the object ref swap, cluster-wide).

I guess this is the same for a distributed and a non distributed version. But without some kind of clock and without relying on locks, it is not possible to create a consistent view of reality. MVCC without a logical clock (could also be a vector clock) IMHO is subject to all kinds of isolation anomalies. In Oracle this clock is called the SCN.

Peter Veentjer

Multiverse: Software Transactional Memory for Java

http://multiverse.codehaus.org

 

Prashant Mname replied on Thu, 2010/06/24 - 4:31am

Hi Manik,

Needed your help on some issue which am facing while setting up Infinispan.

Currently I am evaluating infinispan for cache & state replication in clustering.

Setup is  as follows:

Machine 1  :  Apache Load Balancer,   Tomcat A,   Tomcat B

Machine 2 :   Tomcat C,   Tomcat D

Now when I configure with default global confguration and with enabling JMX expose, Tomcat A & B are forming cluster and receives cluster view when they detect each other but Tomcat C or  D is started then it doesnt get acknowldged in Tomcat A & B. Although C & D forms another cluster view and only detects eachother and not A & B.

This setup is working fine for Tomcat Clustering which is using its onw JGroups implementation only, session replication is also working fine for all 4 tomcat instances.

Please advice on any required setting to anable to form cluster over LAN.

Code Attachment:

This code is present in ApplicationContextListener & makes static cachemanager available to create caches in another classes

public static CacheManager cacheManager = null;

public void contextInitialized(ServletContextEvent ce) {

     GlobalConfiguration gc = GlobalConfiguration.getClusteredDefault();
     gc.setTransportClass(JGroupsTransport.class.getName());
     gc.setExposeGlobalJmxStatistics(true);
     gc.setAllowDuplicateDomains(true);

     Configuration c = new Configuration();
     c.setCacheMode(Configuration.CacheMode.REPL_SYNC);

     c.setFetchInMemoryState(true);
     c.setExposeJmxStatistics(true);

     cacheManager = new DefaultCacheManager(gc,c);
     cacheManager.start();

}

 Following log appears in Tomcat A when Tomcat B is started

INFO Incoming-1,machine01-18483 org.infinispan.remoting.transport.jgroups.JGroupsTransport - Received new cluster view: [machin01-18483|5] [machine01-18483, machine-01-20491]

 Following log appears in Tomcat C when Tomcat C started followed by Tomcat D started

INFO main org.infinispan.remoting.transport.jgroups.JGroupsTransport - Received new cluster view: [machine-02-46148|0] [machine-02-46148]

INFO Incoming-2,machine02-46148 org.infinispan.remoting.transport.jgroups.JGroupsTransport - Received new cluster view: [machine02-46148|1] [machine02-46148, machine-02-49415]

Thanks,

Prashant

Sumit Kumar replied on Wed, 2011/10/12 - 4:54am

Hi, Please give steps to configure Clustered cache in JBOSS AS 6.0 using infinispan cache. Thanks and Regard Sumit

Marcelo Rojas replied on Tue, 2012/01/03 - 3:18pm

Hi Manik, I have a testing web application with two pages for store and then get data from the cache. In the first page I have this code: GlobalConfiguration gc = GlobalConfiguration.getClusteredDefault(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); CacheManager cm = new DefaultCacheManager(gc, c); defaultCache = cm.getCache("ha-partition"); // The line for store data! defaultCache.put("key1", "first key"); testValue = defaultCache.get("key1"); In the second page, exactly the same (I don't want to bore with a large code) but without the line for store the data, so this second page should get the data from the first one and display it, but, is not working......... Question: we can use the "ha-partition" ?? as a normal local-cache?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.