Micronaut Cache

Cache support for Micronaut

Version:

1 Introduction

This project brings additional cache implementations to Micronaut.

To get started, you need to declare the following dependency:

implementation("io.micronaut.cache:micronaut-cache-core")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-core</artifactId>
</dependency>

The configuration implementations in this module require at least Micronaut version 1.3.0. Each implementation is a separate dependency.

To use the BUILD-SNAPSHOT version of this library, check the documentation to use snapshots.

Release History

For more information, check the GitHub release list.

2.0
  • Migrated Code from Core to Cache Module

  • New Caffeine Module

  • Now targets Micronaut 2.x

1.0

Initial Release

1.1

Adds support for Hazelcast 4.0

2 Cache Abstraction

Similar to Spring and Grails, Micronaut provides a set of caching annotations within the io.micronaut.cache package.

The CacheManager interface allows different cache implementations to be plugged in as necessary.

The SyncCache interface provides a synchronous API for caching, whilst the AsyncCache API allows non-blocking operation.

Cache Annotations

The following cache annotations are supported:

  • @Cacheable - Indicates a method is cacheable within the given cache name

  • @CachePut - Indicates that the return value of a method invocation should be cached. Unlike @Cacheable the original operation is never skipped.

  • @CacheInvalidate - Indicates the invocation of a method should cause the invalidation of one or many caches.

By using one of the annotations the CacheInterceptor is activated which in the case of @Cacheable will cache the return result of the method.

If the return type of the method is a non-blocking type (either CompletableFuture or an instance of Publisher the emitted result will be cached.

In addition if the underlying Cache implementation supports non-blocking cache operations then cache values will be read from the cache without blocking, resulting in the ability to implement completely non-blocking cache operations.

Caching with Caffeine

To cache using Caffeine add the following dependency to your application:

implementation("io.micronaut.cache:micronaut-cache-caffeine")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-caffeine</artifactId>
</dependency>

Then configure one or many caches. For example with application.yml:

Cache Configuration Example
micronaut:
    caches:
        my-cache:
            maximumSize: 20

The above example will configure a cache called "my-cache" with a maximum size of 20.

Naming Caches

Names of caches under micronaut.caches should be defined in kebab case (lowercase and hyphen seperated), if camel case is used the names are normalized to kebab case. So for example specifing myCache will become my-cache. The kebab case form should be used when referencing caches in the @Cacheable annotation.

To configure a weigher to be used with the maximumWeight configuration, create a bean that implements com.github.benmanes.caffeine.cache.Weigher. To associate a given weigher with only a specific cache, annotate the bean with @Named(<cache name>). Weighers without a named qualifier will apply to all caches that don’t have a named weigher. If no beans are found, a default implementation will be used.

3 JCache API support

When there is a JSR 107 (JCache) implementation in the classpath (Ehcache, Hazelcast, Infinispan, etc), the caching abstraction will use the JCache API internally by default. If you want Micronaut to use the concrete implementation API, JCache needs to be disabled:

micronaut:
  jcache:
    enabled: false

4 Redis Support

Using the CLI

If you are creating your project using the Micronaut CLI, supply the redis-lettuce feature to configure Redis/Lettuce in your project:

$ mn create-app my-app --features redis-lettuce

If you wish to use Redis to cache results the Micronaut Redis module provides a CacheManager implementation that allows using Redis as a backing cache.

5 Ehcache Support

To use Ehcache as the caching implementation, add it as a dependency to your application:

implementation("io.micronaut.cache:micronaut-cache-ehcache:2.0.1.BUILD-SNAPSHOT")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-ehcache</artifactId>
    <version>2.0.1.BUILD-SNAPSHOT</version>
</dependency>

To have Micronaut create a cache, the minimum configuration is:

ehcache:
  caches:
    my-cache:
      enabled: true

Then, you can use any of the caching annotations with my-cache as cache name.

See the configuration reference to check all possible configuration options.

Tiering options

Ehcache supports the concept of tiered caching. This library allows you to configure tiering caching options on a per-cache basis.

If no tier is explicitly configured, the cache will be configured with a heap tier of 100 entries maximum.

Heap tier

It can be sized by number of entries:

ehcache:
  caches:
    my-cache:
      heap:
        max-entries: 5000

Or by size:

ehcache:
  caches:
    my-cache:
      heap:
        max-size: 200Mb

Off-heap tier

ehcache:
  caches:
    my-cache:
      offheap:
        max-size: 1Gb

Do not forget to define in the java options the -XX:MaxDirectMemorySize option, according to the off-heap size you intend to use.

Disk tier

ehcache:
  storage-path: /var/caches
  caches:
    my-cache:
      disk:
        max-size: 10Gb

Clustered tier

Ehcache supports distributed caching with Terracotta

This is a complete example configuration:

ehcache:
  cluster:
    uri: terracotta://localhost/my-application
    default-server-resource: offheap-1
    resource-pools:
      resource-pool-a:
        max-size: 8Mb
        server-resource: offheap-2
      resource-pool-b:
        max-size: 10Mb
  caches:
    clustered-cache:
      clustered-dedicated:
        server-resource: offheap-1
        max-size: 8Mb
    shared-cache-1:
      clustered-shared:
        server-resource: resource-pool-a
    shared-cache-3:
      clustered-shared:
        server-resource: resource-pool-b

Multiple tier setup

A cache can be configured with multiple tiers. Read the Ehcache documentation on the valid configuration options.

For example, to configure a heap + offheap + disk cache:

ehcache:
  storage-path: /var/caches
  caches:
    my-cache:
      heap:
        max-size: 200Mb
      offheap:
        max-size: 1Gb
      disk:
        max-size: 10Gb

6 Hazelcast Support

Hazelcast caching is supported. Micronaut will create a Hazelcast client instance to connect to an existing Hazelcast server cluster or create an standalone embedded Hazelcast member instance.

Add the Micronaut Hazelcast module as a dependency:

implementation("io.micronaut.cache:micronaut-cache-hazelcast:2.0.1.BUILD-SNAPSHOT")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-hazelcast</artifactId>
    <version>2.0.1.BUILD-SNAPSHOT</version>
</dependency>

You can also add Hazelcast module to your project using cli feature as below:

Create a Micronaut application with Hazelcast module
$ mn create-app hello-world -f hazelcast

The minimal configuration to use Hazelcast is to simply declare hazelcast: with a network configuration for addresses of the Hazelcast cluster (example below).

hazelcast:
  network:
    addresses: ['121.0.0.1:5701']

If you provide a Hazelcast configuration file (ex.: hazelcast.xml, hazelcast.yml, hazelcast-client.xml, or hazelcast-client.yml) in the working directory or classpath, Micronaut will use this configuration file to configure Hazelcast instance.

When using the @Cacheable and other Cache Annotations, Micronaut will create the Hazelcast client and use the underlying IMap Cache Datastore on the server.

The full list of configurable options is below.

🔗
Table 1. Configuration Properties for HazelcastClientConfiguration
Property Type Description

hazelcast.client.network.smart-routing

boolean

hazelcast.client.network.connection-timeout

int

hazelcast.client.network.addresses

java.util.List

hazelcast.client.network.redo-operation

boolean

hazelcast.client.network.outbound-port-definitions

java.util.Collection

hazelcast.client.network.outbound-ports

java.util.Collection

hazelcast.client.connection-retry.initial-backoff-millis

int

hazelcast.client.connection-retry.max-backoff-millis

int

hazelcast.client.network.socket.tcp-no-delay

boolean

hazelcast.client.network.socket.keep-alive

boolean

hazelcast.client.network.socket.reuse-address

boolean

hazelcast.client.network.socket.linger-seconds

int

hazelcast.client.network.socket.buffer-size

int

hazelcast.client.properties

java.util.Properties

hazelcast.client.instance-name

java.lang.String

hazelcast.client.cluster-name

java.lang.String

hazelcast.client.labels

java.util.Set

hazelcast.client.user-context

java.util.concurrent.ConcurrentMap

For settings not in the above list, a BeanCreatedEventListener can be registered for HazelcastClientConfiguration or HazelcastMemberConfiguration. The listener will allow all properties to be set directly on the configuration instance.

@Singleton
public class HazelcastAdditionalSettings implements BeanCreatedEventListener<HazelcastClientConfiguration> {

    @Override
    public HazelcastClientConfiguration onCreated(BeanCreatedEvent<HazelcastClientConfiguration> event) {
        HazelcastClientConfiguration configuration = event.getBean();
        // Set anything on the configuration
        return configuration;
    }
}

Alternatively, the HazelcastClientConfiguration or HazelcastMemberConfiguration bean may be replaced with your own implementation.

7 Infinispan Support

Infinispan caching is supported. Micronaut will create an Infinispan client instance to connect to an existing Infinispan server using the HotRod protocol.

To get started, add the Micronaut Infinispan module as a dependency:

implementation("io.micronaut.cache:micronaut-cache-infinispan:2.0.1.BUILD-SNAPSHOT")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-infinispan</artifactId>
    <version>2.0.1.BUILD-SNAPSHOT</version>
</dependency>

By default, Micronaut will setup a RemoteCacheManager over 127.0.0.1:11222. To define custom addresses:

infinispan:
  client:
    hotrod:
      server:
        host: infinispan.example.com
        port: 10222

Micronaut will attempt by default to read a /hotrod-client.properties file from the classpath, and if found, use it. This file is expected to be in Infinispan configuration format, for example:

# Hot Rod client configuration
infinispan.client.hotrod.server_list = 127.0.0.1:11222
infinispan.client.hotrod.marshaller = org.infinispan.commons.marshall.ProtoStreamMarshaller
infinispan.client.hotrod.async_executor_factory = org.infinispan.client.hotrod.impl.async.DefaultAsyncExecutorFactory
infinispan.client.hotrod.default_executor_factory.pool_size = 1
infinispan.client.hotrod.hash_function_impl.2 = org.infinispan.client.hotrod.impl.consistenthash.ConsistentHashV2
infinispan.client.hotrod.tcp_no_delay = true
infinispan.client.hotrod.tcp_keep_alive = false
infinispan.client.hotrod.request_balancing_strategy = org.infinispan.client.hotrod.impl.transport.tcp.RoundRobinBalancingStrategy
infinispan.client.hotrod.key_size_estimate = 64
infinispan.client.hotrod.value_size_estimate = 512
infinispan.client.hotrod.force_return_values = false

## Connection pooling configuration
maxActive = -1
maxIdle = -1
whenExhaustedAction = 1
minEvictableIdleTimeMillis=300000
minIdle = 1

To read this file from a different classpath location:

infinispan:
  client:
    hotrod:
      config-file: classpath:my-infinispan.properties
You can use both an Infinispan’s property file and Micronaut configuration properties. The latter will complement/override values from the former.

The full list of configurable options via Micronaut properties is below.

🔗
Table 1. Configuration Properties for InfinispanHotRodClientConfiguration
Property Type Description

infinispan.client.hotrod.add-cluster

java.lang.String

infinispan.client.hotrod.add-servers

java.lang.String

infinispan.client.hotrod.balancing-strategy

java.lang.String

infinispan.client.hotrod.connection-timeout

int

infinispan.client.hotrod.force-return-values

boolean

infinispan.client.hotrod.key-size-estimate

int

infinispan.client.hotrod.marshaller

java.lang.String

infinispan.client.hotrod.add-context-initializer

java.lang.String

infinispan.client.hotrod.socket-timeout

int

infinispan.client.hotrod.tcp-no-delay

boolean

infinispan.client.hotrod.tcp-keep-alive

boolean

infinispan.client.hotrod.value-size-estimate

int

infinispan.client.hotrod.max-retries

int

infinispan.client.hotrod.batch-size

int

infinispan.client.hotrod.server.host

java.lang.String

infinispan.client.hotrod.server.port

int

infinispan.client.hotrod.statistics.enabled

boolean

infinispan.client.hotrod.statistics.jmx-enabled

boolean

infinispan.client.hotrod.statistics.jmx-domain

java.lang.String

infinispan.client.hotrod.statistics.jmx-name

java.lang.String

infinispan.client.hotrod.connection-pool.max-active

int

infinispan.client.hotrod.connection-pool.max-wait

long

infinispan.client.hotrod.connection-pool.min-idle

int

infinispan.client.hotrod.connection-pool.min-evictable-idle-time

long

infinispan.client.hotrod.connection-pool.max-pending-requests

int

infinispan.client.hotrod.async-executor-factory.factory-class

java.lang.Class

infinispan.client.hotrod.security.authentication.enabled

boolean

infinispan.client.hotrod.security.authentication.sasl-mechanism

java.lang.String

infinispan.client.hotrod.security.authentication.server-name

java.lang.String

infinispan.client.hotrod.security.authentication.username

java.lang.String

infinispan.client.hotrod.security.authentication.password

java.lang.String

infinispan.client.hotrod.security.authentication.realm

java.lang.String

infinispan.client.hotrod.security.ssl.enabled

boolean

infinispan.client.hotrod.security.ssl.key-store-file-name

java.lang.String

infinispan.client.hotrod.security.ssl.key-store-type

java.lang.String

infinispan.client.hotrod.security.ssl.key-store-password

class [C

infinispan.client.hotrod.security.ssl.key-alias

java.lang.String

infinispan.client.hotrod.security.ssl.trust-store-file-name

java.lang.String

infinispan.client.hotrod.security.ssl.trust-store-path

java.lang.String

infinispan.client.hotrod.security.ssl.trust-store-type

java.lang.String

infinispan.client.hotrod.security.ssl.trust-store-password

class [C

infinispan.client.hotrod.security.ssl.sni-host-name

java.lang.String

infinispan.client.hotrod.security.ssl.protocol

java.lang.String

infinispan.client.hotrod.config-file

java.lang.String

the configuration file location

To disable Infinispan:

infinispan:
  enabled: false

8 No Operation Cache Support

Dependent on the environment or when testing it might be undesirable to actually cache items. In such situations a no operation cache manager can be used that will simply accept any items into the cache without actually storing them.

Add the Micronaut no operation cache module as a dependency:

implementation("io.micronaut.cache:micronaut-cache-noop:2.0.1.BUILD-SNAPSHOT")
<dependency>
    <groupId>io.micronaut.cache</groupId>
    <artifactId>micronaut-cache-noop</artifactId>
    <version>2.0.1.BUILD-SNAPSHOT</version>
</dependency>

The no operation cache manager needs to be enabled explicitly:

noop-cache.enabled: true

9 Repository

You can find the source code of this project in this repository: