Legacy Knowledge Base
Published Jul. 2, 2025

Liferay's Clustering API

Written By

Liferay Support

How To articles are not official guidelines or officially supported documentation. They are community-contributed content and may not always reflect the latest updates to Liferay DXP. We welcome your feedback to improve How To articles!

While we make every effort to ensure this Knowledge Base is accurate, it may not always reflect the most recent updates or official guidelines.We appreciate your understanding and encourage you to reach out with any feedback or concerns.

Legacy Article

You are viewing an article from our legacy "FastTrack" publication program, made available for informational purposes. Articles in this program were published without a requirement for independent editing or verification and are provided"as is" without guarantee.

Before using any information from this article, independently verify its suitability for your situation and project.

Liferay will support our API and resolve any issues and answer any questions having to do with the API itself or any other part of Liferay's software. Issues and questions regarding custom development may be handled by our Global Services team or by the developer of those customizations.

Many plugins need to be able to communicate over a cluster.  While nodes in a cluster normally share a single database that can be used, it is often desirable to transmit messages to update in memory caches on other nodes or to tell all the nodes to take some action in response to a user's actions.

This article documents basic information on three ways to transit data across a cluster:

  1. MultiVMPoolUtil
  2. Clusterable Annotation
  3. ClusterExecutorUtil

Resolution

1. MultVMPoolUtil

Example

The MultiVMPoolUtil can be used to create and fetch portal caches, these caches are basically maps that can be shared across the cluster, or used on a single node. To create/fetch a portal cache simply call:

PortalCache portalCache = MultiVMPoolUtil.getCache("MarkAppCache"); 
portalCache.put("First key", "First Value");

*Portal caches can be used much like a map (the key and value does not have to be a String, that's just what I'm using for the example):

These values can then be retrieved later, but even better we allow designating a listener on the portal cache like so:

CacheListener<String, String> myCacheListener = new MyCacheListenerImpl<String, String>();
portalCache.registerCacheListener(myCacheListener);

Configuration

These caches can be configured to allow data to be sent across different nodes in a cluster.

<cache
    eternal="false"
    maxElementsInMemory="100"
    name="TestCache"
    overflowToDisk="false"
    timeToIdleSeconds="600"
>
    <cacheEventListenerFactory
        class="com.liferay.portal.cache.ehcache.LiferayCacheEventListenerFactory"
        properties="replicatePuts=true,replicatePutsViaCopy=true,replicateUpdatesViaCopy=true"
        propertySeparator=","
    />
    <bootstrapCacheLoaderFactory class="com.liferay.portal.cache.ehcache.LiferayBootstrapCacheLoaderFactory" />
</cache>

This would be in a file indicated by the ehcache.multi.vm.config.location property.

The root for this location for Tomcat is: tomcat-home/webapps/ROOT/WEB-INF/classes.

It's important to note that the properties replicatePutsViaCopy and replicateUpdatesViaCopy have a default of false for performance reasons. The max size of the cache is indicated by maxElementsInMemory. When this cap is reached a registered cache listener can react to the eviction.

Side Note

Changing the ehcache.multi.vm.config.location is not just about setting up a caches to propagate across the cluster, it also directly changes all of Liferay's caches - this can be essential for performance tuning, so make sure to bring over elements from the default file and tune them for your specific environment (usually this involves increasing/decreasing cache sizes).

2. Clusterable Annotation

Liferay's Clusterable annotation is simple to implement and is used by the ClusterableAdvice:

@Clusterable (acceptor = MyClusterInvokeAcceptor.class, onMaster = true)
public void myMethod(Args[] args)

The cluster acceptor can implement the ClusterInvokeAcceptor which only has one method which determines if the annotated method will be fired on the node. Setting onMaster to true means only the master node will have this method called.

This annotation can be used by service classes and is useful when operations need to be run on several nodes.

3. ClusterExecutorUtil

The ClusterExecuterUtil can be used when more fine grain handling needs to be done or when needing to do this outside of a service. This not as convenient as the annotation, it can only call static methods and all the arguments must be serializable.

First we need to create a method key:

MethodKey myMethodKey = new MethodKey(MethodClass.class, "methodName", FirstSerializableArguement.class ...);

Next we create the method handler:

MethodHandler myMethodHandler = new MethodHandler(myMethodKey, myFirstArguement....);

Now we can create the cluster request (having a true skipLocal parameter allows us to skip the current node):

ClusterRequest clusterRequest = ClusterRequest.createMulticastRequest(methodHandler, true);

ClusterExecutorUtil.execute(clusterRequest);

This allows us to call methods outside of services.

Additional Information

Did this article resolve your issue ?

Legacy Knowledge Base