

Terracotta has been integrated with Ehcache since Ehcache 1.4.
From version 1.7 Ehcache has been seamlessly integrated with Terracotta 3.1.1 and takes just a few lines of config in ehcache.xml to get up and running.
In Ehcache 2.0 additional configuration options have been added which provide finer grained control.
Distribution with the Terracotta Server Array ("TSA") is the preferred distribution mechanism. It provides coherency, JTA, HA, scale and high performance. It is available as open source or with additional features in the Ehcache EX and FX product editions.
Ehcache distributed with TSA is different to the other distribution mechanisms. They all replicate data, with 100% of data held in each node. Scaling is thus limited to how much can be comfortably held in each node. Replication is also not JTA transactional or guaranteed coherent.
With TSA the data is split between an Ehcache node, which is the L1 Cache and the TSA, which is the L2 Cache. As with the other replication mechanisms the L1 can hold as much data as is comfortable. All the rest lies in the L2. In Ehcache EX, each CacheManager can have only one logical TSA (there can be multiple redundant TSAs for HA). In Ehcache FX, the TSAs are striped for unlimited scale.
Data is held in-process in the Ehcache L1 for rapid access, however the data is also always in the TSA. So the cache is unaffected by termination of an Ehcache node. When the node comes back up it reconnects to the TSA L2 and as it uses data fills its local L1. There is thus no notion of a bootstrap as there is with the other distribution mechanisms.
As this example shows, running Ehcache with Terracotta clustering is no different from normal programmatic use.
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class TerracottaExample {
CacheManager cacheManager = new CacheManager();
public TerracottaExample() {
Cache cache = cacheManager.getCache("sampleTerracottaCache");
int cacheSize = cache.getKeys().size();
cache.put(new Element("" + cacheSize, cacheSize));
for (Object key : cache.getKeys()) {
System.out.println("Key:" + key);
}
}
public static void main(String[] args) throws Exception {
new TerracottaExample();
}
}
The above example looks for sampleTerracottaCache.
In ehcache.xml, we need to uncomment or add the following line:
<terracottaConfig url="localhost:9510"/>
which tells Ehcache to load the Terracotta server config from localhost port 9510. Note: You must have a Terracotta 3.1.1 or higher server running locally for this example.
Next we want to enable Terracotta clustering for the cache named sampleTerracottaCache. Uncomment or add the following in ehcache.xml.
<cache name="sampleTerracottaCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="1800"
overflowToDisk="false">
<terracotta/>
</cache>That's it!
Terracotta configuration in ehcache.xml is in three parts:
The attributes of ehcache are:
an optional name for the CacheManager. The name is optional and primarily used for documentation or to distinguish Terracotta clustered cache state. With Terracotta clustered caches, a combination of CacheManager name and cache name uniquely identify a particular cache store in the Terracotta clustered memory.
The name will show up in the Developer Consoles.
an optional boolean flag specifying whether this CacheManager should check for new versions of Ehcache over the Internet. If not specified, updateCheck="true".
an optional setting that determines whether the CacheManager should automatically register the SampledCacheMBean with the system MBean server. Currently, this monitoring is only useful when using Terracotta and thus the "autodetect" value will detect the presence of Terracotta and register the MBean. Other allowed values are "on" and "off". The default is "autodetect".
<Ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect">
Note: You need to install and run one or more Terracotta servers to use Terracotta clustering.
See http://www.terracotta.org/web/display/orgsite/Download.
With a server/servers up and running you need to specify the location of the servers.
Configuration can be specified in two main ways: by reference to a source of configuration or by use of an embedded Terracotta configuration file.
To specify a reference to a source (or sources) of configuration, use the url attribute. The url attribute must contain a comma-separated list of:
Example using a path to Terracotta configuration file:
<terracottaConfig url="/app/config/tc-config.xml"/>
Example using a URL to a Terracotta configuration file:
<terracottaConfig url="http://internal/ehcache/app/tc-config.xml"/>
Example pointing to a Terracotta server installed on localhost:
<terracottaConfig url="localhost:9510"/>
Example using multiple Terracotta server instance URLs (for fault tolerance):
<terracottaConfig url="host1:9510,host2:9510,host3:9510"/>
To embed a Terracotta configuration file within the Ehcache configuration, simply place the usual Terracotta XML config within the terracottaConfig element.
In this example we have two Terracotta servers running on server1 and server2.
<terracottaConfig>
<tc-config>
<servers>
<server host="server1" name="s1"/>
<server host="server2" name="s2"/>
</servers>
<clients>
<logs>app/logs-%i</logs>
</clients>
</tc-config>
</terracottaConfig>Cache elements can also contain information about whether the cache can be clustered with Terracotta.
The terracotta sub-element has the following attributes:
Indicates whether this cache should be clustered with Terracotta. By default, if the terracotta element is included, clustered=true.
Indicates whether this cache should be clustered with serialized copies of the values or using Terracotta identity mode. By default, values will be cached in serialization mode which is similar to other replicated Ehcache modes. The identity mode is only available in certain Terracotta deployment scenarios and will maintain actual object identity of the keys and values across the cluster. In this case, all users of a value retrieved from the cache are using the same clustered value and must provide appropriate locking for any changes made to the value (or objects referred to by the value).
Indicates whether this cache should have coherent reads with guaranteed consistency across the cluster. By default, this setting is true. If you set this property to false, reads are allowed to check the local value without locking, possibly seeing stale values.
This is a performance optimization with weaker concurrency guarantees and should generally be used with caches that contain read-only data or where the application can tolerate reading stale data.
Note that from Ehcache 2.0, this setting has the same affect as coherent.

Indicates whether cache values are deserialized on every read or if the materialized cache value can be re-used between get() calls.
This setting is useful if a cache is being shared by callers with disparate classloaders or to prevent local drift if keys/values are mutated locally w/o putting back to the cache. i.e. if set to true then each thread has its own copy and cannot affect other threads.
NOTE: This setting is only relevant for caches with valueMode=serialization

Indicates whether this cache should have coherent reads and writes with guaranteed consistency across the cluster. By default, its value is true. If this attribute is set to false (or "incoherent" mode), values from the cache are read without locking, possibly yielding stale data. Writes to a cache in incoherent mode are batched and applied without acquiring cluster-wide locks, possibly creating inconsistent values across cluster. Incoherent mode is a performance optimization with weaker concurrency guarantees and should generally be used for bulk-loading caches, for loading a read-only cache, or where the application that can tolerate reading stale data.
This setting overrides coherentReads, which is deprecated. For backward compatibility any configurations setting a value for coherentReads will apply to coherent.

When set to true, clustered caches use Terracotta SYNCHRONOUS WRITE locks. Asynchronous writes(synchronousWrites="false") maximize performance byallowing clients to proceed without waiting for a "transaction received" acknowledgement from the server. Synchronous writes (synchronousWrites="true") maximizes data safety by requiring that a client receive server acknowledgement of a transaction before that client can proceed. If coherence mode is disabled using configuration (coherent="false") or through the coherence API, only asynchronous writes can occur (synchronousWrites="true" is ignored). By default this value is false (i.e. clustered caches use normal Terracotta WRITE locks).
The simplest way to enable clustering is to add:
<terracotta/>
To indicate the cache should not be clustered (or remove the terracotta element altogether):
<terracotta clustered="false"/>
To indicate the cache should be clustered using identity mode:
<terracotta clustered="true" valueMode="identity"/>
Following is an example Terracotta clustered cache named sampleTerracottaCache.
<cache name="sampleTerracottaCache"
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="1800"
overflowToDisk="false">
<terracotta/>
</cache>The copyOnRead setting is most easily explained by first examining what it does when not enabled and exploring the potential problems that can arise.
For a cache for which copyOnRead is NOT enabled, the following reference comparsion will always be true (NOTE: assuming no other thread changes the cache mapping between the get()'s)
Object obj1 = c.get("key").getValue();
Object obj2 = c.get("key").getValue();
if (obj1 == obj2) {
System.err.println("Same value objects!");
}The fact that the same object reference is returned accross multiple get()'s implies that the cache is storing a direct reference to cache value. When copyOnRead is enabled the object references will be fresh and unique upon every get().
This default behavior (copyOnRead=false) is usually what you want although there are at least two scenarios in which this is problematic: (1) Caches shared between classloaders and (2) Mutable value objects
Imagine two web applications that both have access to the same Cache instance (this implies that the core ehcache classes are in a common classloader). Imagine further that the classes for value types in the cache are duplicated in the web application (ie. they are not present in the common loader). In this scenario you would get ClassCastExceptions when one web application accessed a value previously read by the other application. One solution to this problem is obviously to move the value types to the common loader, but another is to enable copyOnRead so that thread context loader of the caller will be used to materialize the cache values on each get(). This feature has utility in OSGi environments as well where a common cache service might be shared between bundles
Another subtle issue concerns mutable value objects in a clustered cache. Consider this simple code which shows a Cache that contains a mutable value type (Foo):
class Foo {
int field;
}
Foo foo = (Foo) c.get("key").getValue();
foo.field++;
// foo instance is never re-put() to the cache
// ...If the Foo instance is never re-put() to the Cache your local cache is no longer consistent with the cluster (it is locally modified only). Enabling copyOnRead eliminates this possibility since the only way to affect cache values is to call mutator methods on the Cache.
It is worth noting that there is a performance penalty to copyOnRead since values are deserialized on every get().
Terracotta clustering works by clustering the MemoryStore, unlike the replication mechanisms which use the CacheEventListener infrastructure.
This results in a simpler programming contract than with the replication mechanisms.
Things to note:
Cache listeners listen for changes, including replicated cluster changes, made through the Cache API. Because Terracotta cluster changes happen transparently directly to the MemoryStore a listener will not be invoked when an event occurs out on the cluster. If it occurs locally, then it must have occurred through the Cache API, so a local event will be detected by a local listener.
A common use of listeners is to trigger a reload of a just invalidated Element. In Terracotta clustering this is avoided as a change in one node is always coherent to the other nodes.
To send out cache change events across the cluster, you need to set up a local listener on the relevant cache so that these events can be propagated to other nodes. This is done by adding the following cacheEventListenerFactory tag to the cache:
<cacheEventListenerFactory class="net.sf.ehcache.event.TerracottaCacheEventReplicationFactory"/>
">The interface net.sf.ehcache.cluster.CacheCluster provides methods for obtaining topology information for a Terracotta cluster.
The following methods are available:
The interface net.sf.ehcache.cluster.ClusterNode provides methods for obtaining information on specific cluster nodes.
public interface ClusterNode {
/**
* Get a unique (per cluster) identifier for this node.
*
* @return Unique per cluster identifier
*/
String getId();
/**
* Get the host name of the node
*
* @return Host name of node
*/
String getHostname();
/**
* Get the IP address of the node
*
* @return IP address of node
*/
String getIp();
}
">Terracotta has a Terracotta Cluster event notification mechanism. From Ehcache 2.0, Ehcache can receive these events.
This is not cache event notification, rather it is cluster event notification.
The Terracotta Distributed Ehcache cluster events API provides access to Terracotta cluster events and cluster topology.
The interface net.sf.ehcache.cluster.ClusterTopologyListener provides methods for detecting the following cluster events:
public interface ClusterTopologyListener {
/**
* A node has joined the cluster
*
* @param node The joining node
*/
void nodeJoined(ClusterNode node);
/**
* A node has left the cluster
*
* @param node The departing node
*/
void nodeLeft(ClusterNode node);
/**
* This node has established contact with the cluster and can execute clustered operations.
*
* @param node The current node
*/
void clusterOnline(ClusterNode node);
/**
* This node has lost contact (possibly temporarily) with the cluster and cannot execute
* clustered operations
*
* @param node The current node
*/
void clusterOffline(ClusterNode node);
}This example prints out the cluster nodes and then registers a ClusterTopologyListener which prints out events as they happen.
CacheManager mgr = ...
CacheCluster cluster = mgr.getCluster("Terracotta");
// Get current nodes
Collection<ClusterNode> nodes = cluster.getNodes();
for(ClusterNode node : nodes) {
System.out.println(node.getId() + " " + node.getHostname() + " " + node.getIp());
}
// Register listener
cluster.addTopologyListener(new ClusterTopologyListener() {
public void nodeJoined(ClusterNode node) { System.out.println(node + " joined"); }
public void nodeLeft(ClusterNode node) { System.out.println(node + " left"); }
public void clusterOnline(ClusterNode node) { System.out.println(node + " enabled"); }
public void clusterOffline(ClusterNode node) { System.out.println(node + " disabled"); }
});If Ehcache got disconnected from the Terracotta Server Array say due to a network issue, then in Ehcache 2.0 each cache operation will block indefinitely. In other words it is configured for fail-fast to protect the ACIDity of the cluster.
However this approach will also cause processing of requests to the cache to stop likely causing the outage to cascade. In some cases graceful degradation may be more appropriate. When the clusterOffline events fires you could call Cache.setDisabled() which will cause puts and gets to bypass the cache. Your application would then degrade to operating without a cache, but might be able to do useful work.
You could also take the whole application off-line.
When connectivity is restored you could then reverse the action, taking the cache back online or the application back on line as the case may be.
We expect to add some of these common behaviours into configuration in the next release:
Please see Terracotta Documentation for much more information.
The extra thing at development time is having a local Terracotta server running for integration and/or interactive testing.
Terracotta has a Maven plugin available which makes this very simple.
<pluginRepositories>
<pluginRepository>
<id>terracotta-snapshots</id>
<url>http://www.terracotta.org/download/reflector/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<plugin>
<groupId>org.terracotta.maven.plugins</groupId>
<artifactId>tc-maven-plugin</artifactId>
<version>1.5.1</version>
<executions>
<execution>
<id>run-integration</id>
<phase>pre-integration-test</phase>
<goals>
<goal>run-integration</goal>
</goals>
</execution>
<execution>
<id>terminate-integration</id>
<phase>post-integration-test</phase>
<goals>
<goal>terminate-integration</goal>
</goals>
</execution>
</executions>
</plugin>
To start Terracotta:
mvn tc:start
To stop Terracotta:
mvn tc:stop
See http://forge.terracotta.org/releases/projects/tc-maven-plugin/ for a complete reference.
You need to include the ehcache-terracotta jar in your classpath.
You have not configured a Terracotta server for Ehcache to connect to.
timeToIdle and timeToLive work as usual. Ehcache 1.7 introduced a less fine grained age recording in Element which rounds up to the nearest second. Some APIs may be sensitive to this change.
In Ehcache Elements can have overridden TTI and TTLs. Terracotta supports this functionality.
Ehcache supports LRU, LFU and FIFO eviction strategies.
Terracotta supports LRU and LFU eviction from the local node. Not FIFO and not custom evictors.
The Terracotta server provides an additional store, generally referred to as the Level 2 or L2 store.
The MemoryStore in JVM in the local node is referred to as the L1 Store.
maxElementsInMemory - the maximum number of elements in the local L1 store.
maxElementsOnDisk - is overridden when using Terracotta to provide the L2 size. The L2 size is effectively the maximum cache size.
overflowToDisk normally controls whether to overflow to the DiskStore. This is ignored when using Terracotta - the DiskStore is never used. When the store gets full, elements will always overflow to the Terracotta L2 Store running on the server. The L2 can be further configured with the tcconfig.
Two things to cause elements to be flushed from L1 to L2.
An Element, key and value in Ehcache is guaranteed to .equals() another as it moves between stores.
In the Express install or Serialization mode of Terracotta, which is the default, Terracotta is the same. Elements will not == each other as they move between stores.
An Element in Ehcache is guaranteed to .equals() another as it moves between stores.
However in Identity mode, Terracotta makes a further guarantee that they key and the value ==. This is achieved using extensions to the Java Memory Model.
Terracotta' non Ehcache API offers an async writethrough to the database which is guaranteed. It uses the TIM Async module and works by putting the database update in a clustered queue. It guarantees that a node, even if the local node fails, will take it out and process it.
That option is not available with Ehcache although it may get added.
It isn't. This is a problem with using a database as an integration point. Integration via a message queue, with a Terracotta clustered application acting as a message queue listener and updating the database avoids this. As would The application receiving a REST or SOAP call and writing to the database.
AQ can have DB trigger put in a poll. Or AQ can push it up.
A local CacheEventListener will work locally, but other nodes in a Terracotta cluster are not notified unless the TerracottaCacheEventReplicationFactory event listener is registered for the cache.
SampledCache and SampledCacheManager MBeans are made available in the Terracotta Developer Console.
These are time based gauges, based on once per second measurements. These are different to the JMX MBeans available through the ManagementService.
The following classloaders are tried in this order:
For full compatibility Ehcache 2.0 should be used with Terracotta Server 3.2.1.
Ehcache 1.7 works with TC 3.1.1.
Ehcache Core 1.7.3 core works with TC 3.2.
Note that existing threads doing I/O against the TC server (whether for data or for locks) are stuck.