Quarkus Infinispan Embedded

The new Infinispan Embedded Quarkus extension simplifies using Infinispan as an embedded data grid or cache in Quarkus applications.

  • Embedded Infinispan: Run Infinispan in the same process as your application, eliminating the need for a standalone server.

  • Seamless Quarkus Integration: Optimized for Quarkus, providing fast startup, low memory footprint, and simplified configuration.

  • Flexible Caching and Data Grid Capabilities: Easily store and manage data in-memory for high performance and scalability.

This extension is ideal for developers who want to build high-speed, lightweight applications without the overhead of managing external data services.

Native support in this extension is limited and not recommended for production use. Infinispan 16.0 uses the Foreign Function & Memory (FFM) API for off-heap memory management and Lucene uses it for memory-mapped I/O. These FFM-based APIs are not fully compatible with GraalVM native image compilation.

For native applications, consider using Infinispan in Client/Server mode with the Quarkus Infinispan Client extension.

Quick Overview

To incorporate Quarkus Infinispan Embedded into your Quarkus project, add the following Maven dependency:

<dependency>
    <groupId>io.quarkiverse.infinispan</groupId>
    <artifactId>quarkus-infinispan-embedded</artifactId>
    <version>2.0.1</version>
</dependency>

Injecting EmbeddedCacheManager

In your application, you can inject org.infinispan.manager.EmbeddedCacheManager to interact completely with Infinispan embedded.

package io.quarkiverse.infinispan.embedded.samples;

import jakarta.inject.Inject;

import org.infinispan.Cache;
import org.infinispan.commons.api.CacheContainerAdmin;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.manager.EmbeddedCacheManager;

import io.quarkus.logging.Log;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;

@QuarkusMain
public class MyServiceExample implements QuarkusApplication {

    @Inject
    private EmbeddedCacheManager cacheManager; (1)

    @Override
    public int run(String... args) {
        Configuration config = new ConfigurationBuilder()
                .clustering().cacheMode(CacheMode.DIST_ASYNC).build();
        Log.info(cacheManager.administration() (2)
                .withFlags(CacheContainerAdmin.AdminFlag.VOLATILE)
                .getOrCreateCache("mycache", config));
        Cache<String, String> mycache = cacheManager.getCache("mycache");
        mycache.put("greeting", "Hello world!");
        Log.info(mycache.get("greeting"));
        return 0;
    }
}
1 @Inject is used to inject the EmbeddedCacheManager into your bean.
2 Use the EmbeddedCacheManager. The scope of the bean is @ApplicationScoped

Injecting embedded caches

In your application, you can inject either org.infinispan.Cache or org.infinispan.AdvancedCache to interact with an Infinispan embedded cache instance.

package io.quarkiverse.infinispan.embedded.samples;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.infinispan.Cache;

import io.quarkiverse.infinispan.embedded.Embedded;

@ApplicationScoped
public class CacheInjectionExample {

    @Inject (1)
    @Embedded("mycache") (2)
    private Cache<String, String> cache;

    public void addValue(String key, String value) {
        cache.put("greeting", "Hello world!"); (3)
    }
}
1 @Inject is used to inject the cache into your bean.
2 The @Embedded("cache-name") specifies the cache name. Specifies the name of the cache. If the cache does not exist, it will be created on first access with default configuration (either local or distributed, depending on setup)
3 Use the Cache. The scope of the bean is @ApplicationScoped

Caching annotations

This extension implements the QuarkusCache extension to integrate seamlessly with the Quarkus caching. By providing a custom implementation backed by Infinispan’s embedded cache, it allows developers to leverage Quarkus’s caching annotations (e.g., @CacheResult, @CacheInvalidate) while benefiting from the robustness and flexibility of Infinispan embedded. This ensures compatibility with the broader Quarkus ecosystem and enables declarative caching for local or distributed cache scenarios out of the box.

package io.quarkiverse.infinispan.embedded.samples;

import static java.util.Objects.requireNonNull;

import java.util.concurrent.ThreadLocalRandom;

import jakarta.enterprise.context.ApplicationScoped;

import org.infinispan.protostream.GeneratedSchema;
import org.infinispan.protostream.annotations.Proto;
import org.infinispan.protostream.annotations.ProtoSchema;

import io.quarkus.cache.CacheInvalidate;
import io.quarkus.cache.CacheInvalidateAll;
import io.quarkus.cache.CacheResult;
import io.quarkus.logging.Log;

@ApplicationScoped (1)
public class WeatherService {
    ThreadLocalRandom random = ThreadLocalRandom.current();

    @Proto (2)
    public record Weather(String temperature, String windSpeed, String windDirection) {
        @ProtoSchema(includeClasses = { Weather.class }, (3)
                schemaPackageName = "io.quarkiverse.weather")
        public interface WeatherSchema extends GeneratedSchema {
        }
    }

    @CacheResult(cacheName = "weather") (4)
    public Weather retrieve(String city) {
        requireNonNull(city);
        String temperature = String.format("%.1f", random.nextDouble() * 40); // Random temperature between 0 and 40
        String windSpeed = String.format("%dkm", random.nextInt(30) + 10); // Random wind speed between 10 and 40 km/h
        String[] directions = { "North", "South", "East", "West", "Northwest", "Southeast" };
        String windDirection = directions[random.nextInt(directions.length)]; // Random wind direction
        return new Weather(temperature, windSpeed, windDirection);
    }

    @CacheInvalidateAll(cacheName = "weather") (5)
    public void clearAll() {
        Log.info("Clearing weather cache");
    }

    @CacheInvalidate(cacheName = "weather") (6)
    public void clearWeather(String city) {
        Log.info("Clear city: " + city);
    }
}
1 Declares the class as an @ApplicationScoped CDI bean, ensuring a single instance throughout the app.
2 Marks the Weather record for Protobuf serialization.
3 Declares the Protobuf schema, including the Weather class and setting the schema package.
4 Caches the result of retrieve() using the weather cache.
5 Invalidates all entries in the weather cache.
6 Invalidates the specific cache entry for a given city.

If the cache does not exist, it will be created on first access with default configuration (either local or distributed, depending on setup)

Check the cache-sample under the samples folder.

Providing Infinispan Embedded configuration

To customize the embedded Infinispan cache configuration, use the property quarkus.infinispan-embedded.xml-config.

<infinispan> (1)
		<cache-container name="default" statistics="true"> (2)
				<local-cache name="todolist"> (3)
						<persistence passivation="false"> (4)
								<file-store shared="false">
									<data path="target/data" />
									<index path="target/index" />
								</file-store>
							</persistence>
						</local-cache>
					</cache-container>
				</infinispan>
1 Root element for Infinispan configuration.
2 Declares a cache container named "default" with statistics enabled.
3 Defines a local cache called "todolist".
4 Disabled passivation and configures a file-based persistent store.
quarkus.infinispan-embedded.xml-config=caches.xml (1)
1 Quarkus loads the Infinispan configuration from the specified XML file (caches.xml)

Check the persisted-cache-sample under the samples folder.

In this example, the local cache will use file persistence to persist the state.

Default caches configuration

When no explicit configuration is provided for a cache, Quarkus automatically applies sensible defaults based on the context. It defines two built-in configuration names:

  • DEFAULT_LOCAL_QUARKUS_CACHE_CONFIGURATION for local caches

  • DEFAULT_CLUSTERED_QUARKUS_CACHE_CONFIGURATION for distributed caches.

These default configurations are registered at runtime and applied on first access to any undefined cache. The local configuration uses CacheMode.LOCAL for single-node setups, while the clustered configuration uses CacheMode.DIST_SYNC with Protobuf (application/x-protostream) as the media type, ensuring compatibility with remote marshalling. This approach simplifies development by reducing boilerplate, while still supporting custom XML-based configurations when needed.

Embedded Query

The Infinispan Query module is an optional dependency. If you need to use Infinispan embedded queries, add the infinispan-query dependency explicitly:

<dependency>
    <groupId>org.infinispan</groupId>
    <artifactId>infinispan-query</artifactId>
</dependency>

The Hibernate Search version must match the Infinispan version. If necessary, import the Hibernate Search BOM to override the Quarkus-provided version.

<dependency>
    <groupId>org.hibernate.search</groupId>
    <artifactId>hibernate-search-bom</artifactId>
    <version>7.1.2.Final</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

Making infinispan-query optional keeps the extension lightweight for cache-only use cases and avoids pulling in Lucene and Hibernate Search when they are not needed.

Adding infinispan-query is not compatible with GraalVM native image compilation. Lucene uses the Foreign Function & Memory (FFM) API which is not supported in native mode. If you need native support, use cache-only mode without the query dependency.

Configuration Settings

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

Type

Default

The configured Infinispan embedded xml file which is used by the managed EmbeddedCacheManager and its Caches

Environment variable: QUARKUS_INFINISPAN_EMBEDDED_XML_CONFIG

string

Sets a cluster with defaults.

Environment variable: QUARKUS_INFINISPAN_EMBEDDED_CLUSTERED

boolean

true