Coherence is an in-memory data grid solution that addresses the issues of working with distributed objects in memory (cache). Amongst its many features, Pluggable Cache Stores allows caching (i.e., load/store) contents from any persistence layer.
This way, the cache abstracts content persistence and loading through the CacheStore interface to be implemented by the user. The synchronization of updates, also decoupled from the persistence layer, is governed by the following strategies:
Relational databases is the most common option for persistence, but its selection forces you to define an object-relational mapping from Java classes to the underlying relational model (using for example: hibernate, TopLink or any ad-hoc JPA implementation). But that is only half of the job to be done. It is also paramount to define how you will manage the connections to execute this persistence.
The following diagram depicts how Coherence persists in an RDBMS through the CacheStore interface:
The CacheStore is responsible for handling the connection to the database. Not only in terms of connection creation/pooling but also in terms of how to detect changes in the instances of the database it is connected to.
Connection management is critical when you are looking for extreme performance and high availability; hence it’s critical for the CacheStore to handle connection acquirement properly.
Oracle Universal Connection Pool (UCP) provides you not only all the intrinsic advantages of connection pooling (connection re-use, management, availability, purge, etc.) but it also leverages the possibility to use all the features you have when connected to a RAC environment:
By taking this approach, you are not only setting the relationship between your cache and your persistence layer. You are also optimizing the configuration and management with your underlying RAC, by exploiting all its features in terms of connection handling.
For more information about UCP, please refer to: Introduction to UCP.
In a previous article we already discussed these features and how to exploit them from a JBoss Web application: Using Universal Connection Pooling (UCP) with JBoss AS
In this article we are going to show you how to use UCP from Coherence using Pluggable Cache Stores, hence also making it available for any type of Coherence client.
1. Download and install coherence standalone, UCP and Oracle JDBC Driver.You can skip this step if you already have coherence.jar, ucp.jar and ojdbc7.jar.
During install, select an Oracle Home location. In this location you will find the coherence jar that we will be using during this demo:
Download UCP and Oracle JDBC Driver (ucp.jar and ojdbc.jar) UCP JDBC
For this sample, I copied these jars are in the home directory (~) and coherence.jar to ~/ucpcoherence dir:
2. Configure Cache and CacheStore.
You need to indicate coherence the scheme for your cache. The scheme will define its behavior (such as if it’s local, replicated or distributed, etc.). For our configuration, these are the most important things to understand:
3. Provide CacheStore Implementation.
As configured in previous file, you need to provide an implementation for CacheStore that will execute its methods when objects in the cache are added or requested (it will store or load if the object is not on the cache). We provide an implementation for the load method and the constructor of the class (which shows how to use UCP). You can define the behavior for the rest of the methods as part of your test.
Notice that the values for UCP can be changed/monitored through JMX as it was explained in this article.
To check more in how to use UCP with Hibernate you can check in this article.
4. Provide the Java Bean.
This is the class that will live in our cache. It’s a simple Java Bean which needs to implement Serializable in order to be able to be shared across the nodes of the cache. In a real-life scenario you will want to implement coherence’s PortableObject instead of only Serializable, so you will be using coherence ultra-optimized mechanisms to share/store objects: Portable Object Format Serialization (POF).
Note: getters/setters/constructors are removed to be easier to read this example. Also note that your implementation of toString() is what you are going to see in coherence’s console.
5. Configure Operational File.
In order to provide some specific operational values for your cache, you will need to provide the following operational configuration file.
Take particular attention for the name of the cluser “cluster_ucp”, the address/port you will be synchronizing with other nodes of the cluster “localhost:6699” and the name of the configuration file you will use (you set this through the system property parameter “tangosol.coherence.cacheconfig=example-ucp.xml”, defined in 2nd step.
Note: since version 12.2.1 you no longer need to use prefix “tangosol”.
From command line you can start the nodes of your cache through DefaultCacheServer class and the following parameters:
(from ~/ucpcocherence, being ./ucp_samples/bin the output folder for your eclipse project or the place where you have your compiled custom classes)
../jdk1.8.0_60/jre/bin/java -Dtangosol.coherence.override=tangosol-coherence-override.xml -Dtangosol.coherence.cluster=cluster_ucp -cp coherence.jar:../ucp.jar:../ojdbc8.jar:./ucp_samples/bin com.tangosol.net.DefaultCacheServer
Note: as stated before, since 12.2.1 you no longer need to use tangosol prefix, it will also work with coherence.cluster.
You can check by the output the name of the cluster (cluster_ucp), the addresses it’s listening (localhost:6699) and the name of the member you just started (Id=1).
If you issue the same command on a separate process to start 2nd node:
You can check member id = 2 joining the cluster (it shows in both processes). This way now you have 2 nodes working for this cluster.
Lastly, you start the client for the cluster (which indeed is a new member itself) using CacheFactory class and running this command:
../jdk1.8.0_60/jre/bin/java -Dtangosol.coherence.override=tangosol-coherence-override.xml -Dtangosol.coherence.cluster=cluster_ucp -cp coherence.jar:../ucp.jar:../ojdbc8.jar:./ucp_samples/bin com.tangosol.net.CacheFactory
Note that if you wouldn’t want your client to be a storage member you can provide the option -Dtangosol.coherence.distributed.localstorage=false to achieve this behavior. This can also be done via configuration.
You see member id = 3 with command line option to interact with the cache.
The first thing you need to do is to start a cache with the same scheme as the one we defined. In order to do that you use the same name pattern we defined in cache-mapping using a name matching “test*”. So you issue:
And you will notice now that the prompt is with this cache
Now you try to load the first object by issuing:
You’ll notice for each new object loaded in the cache the additional time it takes to read its value from the database and you’ll see the output of the object retrieved in the console (executing Employee.toString() method).
Run again get 100 and you’ll notice the difference in the response time of using an object that’s already in the cache.
7. Interact with the cache from Java code
To interact with the cache from a java class it’s even easier. The only thing you should do is add the VM parameter -Dtangosol.coherence.override=tangosol-coherence-override.xml (pointing to the same one that started the nodes) to the following code:
Since coherence 12.2.1 the interface NamedCache supports generics, so you might be able to update previous code with:
NamedCache<Integer, Employee> cache = CacheFactory.getTypedCache("test1", TypeAssertion.withoutTypeChecking());