hazelcast - Skullabs/kikaha GitHub Wiki

Hazelcast

Hazelcast is an open source in-memory data grid. It was designed to store data in-memory but in a distributed fashion, similarly to Memcached and Redis. The kikaha-hazelcast module integrated the most used structures that Hazelcast offers. This guide will cover these features and how do configure them. For a complete set of configurations and features that Hazelcast offers, please refer to the Hazelcast documentation. To enable this integration, just include the Hazelcast module on your classpath.

<dependency>
  <groupId>io.skullabs.kikaha</groupId>
  <artifactId>kikaha-cloud-hazelcast</artifactId>
</dependency>

Configuring your project to use Hazelcast

There is a few ways to configure Hazelcast, but we recommend you to use one of bellow:

  • Hazelcast's XML configuration file - which is broadly referenced on its documentation page
  • Hazelcast's programmatic API

Using the Hazelcast's XML configuration file

Define where is the hazelcast.xml file you want to use. As recommended, the bellow sample configuration suggests you to place the hazelcast.xml file at the <resources_dir>/conf directory.

server:
  hazelcast:
    config: "conf/hazelcast.xml"

Now, you can configure Hazelcast the same way is documented at Hazelcast's Manual.

Configuring Hazelcast: the programmatic way

Every time you start a Kikaha application you can be notified every time a new Hazelcast Instance will be used. This would allow you to configure Hazelcast globally, defining every single detail you need to have it ready for your use. The bellow code shows how one can be notified from Hazelcast Instances' configurations and configure a few elements before the instance be started.

@Slf4j
@Singleton
public class MyCacheConfiguration implements HazelcastConfigurationListener {

    public final static String MAP_NAME = "my-cache";

    @Override
    public void onConfigurationLoaded(Config hzConfig) throws IOException {
        MapConfig cacheMap = hzConfig.getMapConfigs().computeIfAbsent( MAP, k -> new MapConfig().setName(k) );
        configureIMapWithDefaults( cacheMap );
    }

    void configureIMapWithDefaults( MapConfig mapConfig ){
        log.info( "Configuring cache '"+mapConfig.getName()+"'..." );
        mapConfig.setEvictionPolicy( EvictionPolicy.LFU )
            .setAsyncBackupCount( 1 )
            .setReadBackupData( true )
            .setMaxSizeConfig( new MaxSizeConfig(3000, MaxSizeConfig.MaxSizePolicy.PER_NODE ) );
    }
}

As you probably noticed, the above code implements the kikaha.hazelcast.HazelcastConfigurationListener. Every implementation of this interface will be notified with the com.hazelcast.config.Config when a new Hazelcast Instance is about to be created.

Distributed Data Structures

Hazelcast offers distributed implementations of Java interfaces. You can find a Java interface list of all available structure Hazelcast offers here.

Despite being very useful, use Hazelcast could be very labor and verbose. Kikaha's Hazelcast module is a tiny layer that integrates most of these distributed structures with the CDI module, allowing developers to inject Hazelcast structures anywhere. Bellow, there is a class with all Hazelcast structures developers are allowed to inject on its classes.

Note that Hazelcast module uses the javax.inject.Named annotation to identify the Distributed Structure is about to be instantiated. It is a mandatory annotation.

import javax.inject.*;
import java.util.*;
import java.concurrency.atomic.*;

import com.hazelcast.core.*;

@Singleton
public class SampleService {

	@Inject
	@Named( "atomic-booleans" )
	IMap<Long, AtomicBoolean> map;

	@Inject
	@Named( "atomic-booleans" )
	MultiMap<Long, AtomicBoolean> multimap;

	@Inject
	@Named( "atomic-booleans" )
	IQueue<AtomicBoolean> queue;

	@Inject
	@Named( "atomic-booleans" )
	ISet<AtomicBoolean> set;

	@Inject
	@Named( "atomic-booleans" )
	IList<AtomicBoolean> list;

	@Inject
	@Named( "atomic-booleans" )
	ITopic<AtomicBoolean> topic;
	
	@Inject
	@Named( "atomic-booleans" )
	ReplicatedMap<Long,AtomicBoolean> replicatedMap; 

	@Inject
	@Named( "executor-service" )
	IExecutorService executorService;

	@Inject
	@Named( "ilock" )
	ILock lock;
	
	@Inject
	@Named( "id-generator" )
	IdGenerator idGenerator;
}

Programmatically configuring Hazelcast's Distributes Data Strucutures

It is possible to be notified when Kikaha creates an instance of any Hazelcast's Distributes Data Strucutures (DDS). It helps to fill the gap when Hazelcast doesn't provide a data loader ou data store for a specific DDS.

Every time a distributable strucuture is created in the JVM, Kikaha searches for kikaha.hazelcast.HazelcastProducedDataListener implementations. Bellow is illustrated how is ossible to define the initial ID for a Hazelcast IdGenerator.

Note that, in order be reachable by the Kikaha's Hazelcast module at the start up time, you have to make all kikaha.hazelcast.HazelcastProducedDataListener implementations injectable.

import kikaha.hazelcast.HazelcastProducedDataListener;
import com.hazelcast.core.*;
import javax.inject.*;

@Singleton
public class IdGeneratorLoader implements HazelcastProducedDataListener<IdGenerator> {
  
  // Inject your dao or JDBC connection here to retrieve data from DB
  @Inject IdGeneratorDao dao;

  @Override
  public void dataProduced( IdGenerator data ){
    long lastId = dao.loadLastIdFromDatabase( data.getName() );
    data.init( lastId );
  }
}

Distributed Session Management with Hazelcast

As described at the Authentication and authorization topic, Kikaha will store all logged in user's information into a kikaha.core.security.SessionStore implementation. Kikaha's Hazelcast module implements a SessionStore that store any Session (and its data) into a com.hazelcast.core.IMap called session-cache. Developers are encouraged to configure the session-cache's eviction policy at the hazelcast.xml file, or programmatically, in order to avoid OutOfMemoryException.

Basically, to setup Hazelcast as your Session Manager, once you had added kikaha-hazelcast as a dependency, you just need to configure the kikaha.hazelcast.HazelcastSessionStore as your session store on your application.yml file.

# application.yml
server:
  # Authentication configuration
  auth:
    # Using Hazelcast to store sessions from UI
    session-store: kikaha.hazelcast.HazelcastSessionStore

Bellow a sample hazelcast.xml file, that evicts 25% of all stored session based on LRU eviction police. For more details about eviction policy, please consider read the Hazelcast's Manual entry about this topic.

<hazelcast
	xsi:schemaLocation="http://www.hazelcast.com/schema/config hazelcast-config-3.6.xsd"
	xmlns="http://www.hazelcast.com/schema/config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <map name="default">
    <time-to-live-seconds>0</time-to-live-seconds>
    <max-idle-seconds>0</max-idle-seconds>
    <eviction-policy>LRU</eviction-policy>
    <max-size policy="PER_NODE">5000</max-size>
    <eviction-percentage>25</eviction-percentage>
    <min-eviction-check-millis>100</min-eviction-check-millis>
  </map>

</hazelcast>
⚠️ **GitHub.com Fallback** ⚠️