<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://infinispan.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://infinispan.org/" rel="alternate" type="text/html" /><updated>2026-07-31T17:50:39+00:00</updated><id>https://infinispan.org/feed.xml</id><title type="html">Infinispan</title><subtitle>Infinispan is a distributed in-memory key/value data store with optional schema, available under the Apache License 2.0.</subtitle><entry><title type="html">Infinispan Hot Rod JS Client 0.16.0: Transactions, Multimaps, and Flags</title><link href="https://infinispan.org/blog/2026/06/29/hotrod-js-client-0-16-0" rel="alternate" type="text/html" title="Infinispan Hot Rod JS Client 0.16.0: Transactions, Multimaps, and Flags" /><published>2026-06-29T00:00:00+00:00</published><updated>2026-06-29T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/06/29/hotrod-js-client-016</id><content type="html" xml:base="https://infinispan.org/blog/2026/06/29/hotrod-js-client-0-16-0"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Hot on the heels of 0.15.0, we&#8217;re happy to announce the release of the <a href="https://github.com/infinispan/js-client/releases/tag/v0.16.0">Infinispan Hot Rod JS Client 0.16.0</a>.
This release brings three new features that round out the client&#8217;s capabilities: transactions, multimap support, and operation flags.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="transactions">Transactions</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Sometimes you need multiple operations to succeed or fail as a unit. The JS client now supports Hot Rod transactions with a simple begin/commit/rollback API:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">var tm = client.getTransactionManager();

await tm.begin();
try {
  await client.put('account-A', '900');
  await client.put('account-B', '1100');
  await tm.commit();
} catch (e) {
  await tm.rollback();
  throw e;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>During a transaction, reads go through the local write set first&#8201;&#8212;&#8201;so if you put a value and then get it within the same transaction, you&#8217;ll see your own write.
Under the hood, the client uses XA-style two-phase commit with version-based conflict detection, so concurrent modifications are caught at commit time.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="multimaps">Multimaps</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Multimaps let you associate multiple values with a single key&#8201;&#8212;&#8201;great for tags, categories, or any one-to-many relationship:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// Add values to a key
await client.multimapPut('colors', 'red');
await client.multimapPut('colors', 'green');
await client.multimapPut('colors', 'blue');

// Get all values for a key
var values = await client.multimapGet('colors');
console.log(values); // ['red', 'green', 'blue']

// Check containment
await client.multimapContainsEntry('colors', 'red');  // true
await client.multimapContainsValue('green');           // true
await client.multimapContainsKey('colors');             // true

// Remove a specific value or the entire key
await client.multimapRemoveEntry('colors', 'red');
await client.multimapRemoveKey('colors');

// Total number of key-value pairs across all keys
var size = await client.multimapSize();</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="operation-flags">Operation flags</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can now pass Hot Rod flags to fine-tune how individual operations behave.
Flags are available as constants on the <code>infinispan</code> module and can be combined with bitwise OR:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">var infinispan = require('infinispan');

// Skip cache store loading
await client.put('key', 'value', {
  flags: infinispan.flags.SKIP_CACHE_LOAD
});

// Combine multiple flags
await client.put('key', 'value', {
  flags: infinispan.flags.SKIP_CACHE_LOAD | infinispan.flags.SKIP_INDEXING
});

// Suppress listener notifications on remove
await client.remove('key', {
  flags: infinispan.flags.SKIP_LISTENER_NOTIFICATION
});</code></pre>
</div>
</div>
<div class="paragraph">
<p>The available flags are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>FORCE_RETURN_VALUE</code>&#8201;&#8212;&#8201;return the previous value on mutations</p>
</li>
<li>
<p><code>DEFAULT_LIFESPAN</code>&#8201;&#8212;&#8201;use the server-configured default lifespan</p>
</li>
<li>
<p><code>DEFAULT_MAXIDLE</code>&#8201;&#8212;&#8201;use the server-configured default max idle time</p>
</li>
<li>
<p><code>SKIP_CACHE_LOAD</code>&#8201;&#8212;&#8201;don&#8217;t load from the cache store</p>
</li>
<li>
<p><code>SKIP_INDEXING</code>&#8201;&#8212;&#8201;don&#8217;t index the entry</p>
</li>
<li>
<p><code>SKIP_LISTENER_NOTIFICATION</code>&#8201;&#8212;&#8201;don&#8217;t fire listener events</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-started">Get started</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Install or update the client via npm:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">npm install infinispan@0.16.0</code></pre>
</div>
</div>
<div class="paragraph">
<p>Check out the full <a href="https://github.com/infinispan/js-client">source code and documentation</a> on GitHub.</p>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="hotrod" /><category term="javascript" /><category term="client" /><category term="release" /><summary type="html"><![CDATA[Hot on the heels of 0.15.0, we&#8217;re happy to announce the release of the Infinispan Hot Rod JS Client 0.16.0. This release brings three new features that round out the client&#8217;s capabilities: transactions, multimap support, and operation flags.]]></summary></entry><entry><title type="html">Hello, Gophers! Introducing the Infinispan Go Client</title><link href="https://infinispan.org/blog/2026/06/22/infinispan-go-client" rel="alternate" type="text/html" title="Hello, Gophers! Introducing the Infinispan Go Client" /><published>2026-06-22T00:00:00+00:00</published><updated>2026-06-22T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/06/22/infinispan-go-client</id><content type="html" xml:base="https://infinispan.org/blog/2026/06/22/infinispan-go-client"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;ve ever thought "I wish I could talk to Infinispan from Go," today is your lucky day.
We&#8217;re thrilled to announce the <a href="https://github.com/infinispan/go-client">Infinispan Go Client</a>&#8201;&#8212;&#8201;a brand-new, pure-Go client that speaks the Hot Rod 4.1 binary protocol and gives you access to all the good stuff: caching, counters, queries, transactions, listeners, and more.</p>
</div>
<div class="paragraph">
<p>No CGo. No JNI bridges. No funny business. Just <code>go get</code> and you&#8217;re off to the races.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="fire-up-a-server">Fire up a server</h2>
<div class="sectionbody">
<div class="paragraph">
<p>First things first: you need an Infinispan server to talk to.
The fastest way is to grab one with Docker (or Podman&#8201;&#8212;&#8201;we don&#8217;t judge):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">docker run --name infinispan \
  -p 11222:11222 \
  -e USER="admin" \
  -e PASS="password" \
  infinispan/server:16.2</code></pre>
</div>
</div>
<div class="paragraph">
<p>Give it a couple of seconds to start up, and you&#8217;ve got a fully functional Infinispan node listening on port 11222. Easy.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="your-first-put-and-get">Your first Put and Get</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Let&#8217;s jump straight into code. Create a new Go module and grab the client:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">mkdir hello-infinispan &amp;&amp; cd hello-infinispan
go mod init hello-infinispan
go get infinispan.org/go-client/hotrod</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now write a tiny program:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"infinispan.org/go-client/hotrod"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// Connect to the server
	client, err := hotrod.NewClient(ctx, "hotrod://admin:password@localhost:11222")
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// Grab a cache handle
	cache := client.Cache("my-cache")

	// Store something
	err = cache.Put(ctx, []byte("greeting"), []byte("Hello from Go!"))
	if err != nil {
		log.Fatal(err)
	}

	// Read it back
	val, found, err := cache.Get(ctx, []byte("greeting"))
	if err != nil {
		log.Fatal(err)
	}
	if found {
		fmt.Println(string(val)) // Hello from Go!
	}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>Run it:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">go run .</code></pre>
</div>
</div>
<div class="paragraph">
<p>And there you have it&#8201;&#8212;&#8201;your Go program just stored and retrieved data from a distributed cache. That&#8217;s it. No XML. No YAML. Just a URI and you&#8217;re in.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="what-else-can-it-do">What else can it do?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Glad you asked. This isn&#8217;t a toy client&#8201;&#8212;&#8201;it&#8217;s packed with features. Here&#8217;s the highlight reel:</p>
</div>
<div class="sect2">
<h3 id="entries-with-ttl-and-idle-timeout">Entries with TTL and idle timeout</h3>
<div class="paragraph">
<p>Don&#8217;t want stale data hanging around? Set a lifespan or a max-idle time on your entries:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">cache.Put(ctx, []byte("session"), []byte("abc123"),
	hotrod.WithLifespan(30*time.Minute),
	hotrod.WithMaxIdle(5*time.Minute),
)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="conditional-operations">Conditional operations</h3>
<div class="paragraph">
<p>Optimistic locking fans, rejoice. You get compare-and-swap semantics:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">// Only put if the key doesn't exist yet
ok, _ := cache.PutIfAbsent(ctx, []byte("lock"), []byte("mine"))

// Replace only if the entry hasn't been modified
meta, _, _ := cache.GetWithMetadata(ctx, []byte("counter"))
ok, _ = cache.ReplaceIfUnmodified(ctx, []byte("counter"), []byte("42"), meta.Version)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="bulk-operations">Bulk operations</h3>
<div class="paragraph">
<p>Need to move a lot of data? <code>PutAll</code> and <code>GetAll</code> are hash-distribution-aware, so they route entries directly to the right nodes:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">entries := map[string][]byte{
	"key1": []byte("val1"),
	"key2": []byte("val2"),
	"key3": []byte("val3"),
}
cache.PutAll(ctx, entries)

results, _ := cache.GetAll(ctx, []string{"key1", "key2", "key3"})</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="event-listeners">Event listeners</h3>
<div class="paragraph">
<p>Want to react when data changes? Register a listener and get notified through a channel:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">listener, _ := cache.AddListener(ctx,
	hotrod.WithListenerInterests(hotrod.EventCreated, hotrod.EventModified),
)
defer cache.RemoveListener(ctx, listener)

for event := range listener.Events {
	fmt.Printf("Event: %s on key %s\n", event.Type, event.Key)
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="queries-with-ickle">Queries with Ickle</h3>
<div class="paragraph">
<p>If you&#8217;re using Protocol Buffers for your values, you can query them with the Ickle query language:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">result, _ := cache.Query(ctx, "FROM example.Person WHERE age &gt; :minAge",
	hotrod.WithQueryParam("minAge", int32(21)),
	hotrod.WithQueryMaxResults(10),
)</code></pre>
</div>
</div>
<div class="paragraph">
<p>And yes, we support <strong>continuous queries</strong> too&#8201;&#8212;&#8201;subscribe to a query and get notified as entries join, update, or leave the result set. Think of it as a live view of your data.</p>
</div>
</div>
<div class="sect2">
<h3 id="distributed-counters">Distributed counters</h3>
<div class="paragraph">
<p>Need a cluster-wide atomic counter? We&#8217;ve got strong counters (reliable, optionally bounded) and weak counters (high-throughput, eventually consistent):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">counters := client.Counters()
counters.Define(ctx, "page-views", &amp;hotrod.CounterConfiguration{
	Type:         hotrod.CounterStrong,
	InitialValue: 0,
	Storage:      hotrod.StoragePersistent,
})

counter := counters.Counter("page-views")
newVal, _ := counter.AddAndGet(ctx, 1)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="near-caching">Near caching</h3>
<div class="paragraph">
<p>For read-heavy workloads, the near cache keeps recently accessed entries on the client side with LRU eviction and automatic server-side invalidation via Bloom filters:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">nc, _ := hotrod.NewNearCache(ctx, client, "my-cache",
	hotrod.WithMaxNearCacheEntries(500),
)
// First Get goes to the server; subsequent ones are served locally
val, found, _ := nc.Get(ctx, []byte("hot-key"))</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="transactions">Transactions</h3>
<div class="paragraph">
<p>When you need atomicity across multiple operations:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">err := client.WithTransaction(ctx, "my-cache", func(tc *hotrod.TxCache) error {
	tc.Put(ctx, []byte("account-A"), []byte("900"))
	tc.Put(ctx, []byte("account-B"), []byte("1100"))
	return nil // commit; return an error to rollback
})</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="type-safe-caches-with-protocol-buffers">Type-safe caches with Protocol Buffers</h3>
<div class="paragraph">
<p>Tired of juggling <code>[]byte</code>? The typed cache gives you generics-powered, type-safe access with automatic marshalling:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-go hljs" data-lang="go">cache := hotrod.NewTypedCache[string, *pb.Person](
	client, "people",
	hotrod.ProtoStreamMarshaller(),
	func() *pb.Person { return &amp;pb.Person{} },
)

cache.Put(ctx, "john", &amp;pb.Person{Name: "John", Age: 30})
person, found, _ := cache.Get(ctx, "john")</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="and-theres-more">And there&#8217;s more</h3>
<div class="ulist">
<ul>
<li>
<p><strong>Iterators</strong>&#8201;&#8212;&#8201;scan through all entries in batches without blowing up your memory</p>
</li>
<li>
<p><strong>Multimap caches</strong>&#8201;&#8212;&#8201;map a single key to multiple values</p>
</li>
<li>
<p><strong>Cache administration</strong>&#8201;&#8212;&#8201;create, configure, and remove caches programmatically</p>
</li>
<li>
<p><strong>Schema management</strong>&#8201;&#8212;&#8201;register and manage Protobuf schemas</p>
</li>
<li>
<p><strong>Pipelined connections</strong>&#8201;&#8212;&#8201;multiple operations fly over a single TCP connection concurrently, so you don&#8217;t pay a round-trip per request</p>
</li>
<li>
<p><strong>Smart routing</strong>&#8201;&#8212;&#8201;topology-aware and hash-distribution-aware client intelligence, so your requests go straight to the node that owns the data</p>
</li>
<li>
<p><strong>TLS and mTLS</strong>&#8201;&#8212;&#8201;because security isn&#8217;t optional</p>
</li>
<li>
<p><strong>SCRAM-SHA-256, PLAIN, OAUTHBEARER, and EXTERNAL auth</strong>&#8201;&#8212;&#8201;pick your SASL flavor</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="the-connection-uri">The connection URI</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Connecting is as simple as a URI:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-none hljs">hotrod://admin:password@server1:11222,server2:11222
hotrods://admin:password@server:11222?trust_store_file_name=/path/to/ca.pem</code></pre>
</div>
</div>
<div class="paragraph">
<p>Use <code>hotrod://</code> for plain connections, <code>hotrods://</code> for TLS, and list as many servers as you like&#8201;&#8212;&#8201;the client discovers the rest through topology updates.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-started">Get started</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">go get infinispan.org/go-client/hotrod</code></pre>
</div>
</div>
<div class="paragraph">
<p>Head over to the <a href="https://github.com/infinispan/go-client">GitHub repository</a> for full documentation and examples.
Our <a href="https://infinispan.org/tutorials/?language=go">tutorials</a> are also packed with Go goodies now, so you can hit the ground running with hands-on, step-by-step guides.
We&#8217;d love to hear your feedback&#8201;&#8212;&#8201;open an issue, send a PR, or just say hi. Happy caching, Gophers!</p>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="hotrod" /><category term="go" /><category term="golang" /><category term="client" /><category term="release" /><summary type="html"><![CDATA[If you&#8217;ve ever thought "I wish I could talk to Infinispan from Go," today is your lucky day. We&#8217;re thrilled to announce the Infinispan Go Client&#8201;&#8212;&#8201;a brand-new, pure-Go client that speaks the Hot Rod 4.1 binary protocol and gives you access to all the good stuff: caching, counters, queries, transactions, listeners, and more.]]></summary></entry><entry><title type="html">Hot Rod Dissector for Wireshark</title><link href="https://infinispan.org/blog/2026/06/17/hot-rod-dissector-wireshark" rel="alternate" type="text/html" title="Hot Rod Dissector for Wireshark" /><published>2026-06-17T00:00:00+00:00</published><updated>2026-06-17T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/06/17/hot-rod-dissector-wireshark</id><content type="html" xml:base="https://infinispan.org/blog/2026/06/17/hot-rod-dissector-wireshark"><![CDATA[<div class="paragraph">
<p>Need to debug a Hot Rod conversation? Don&#8217;t do it the hard way anymore—
grab the right tool and be like Neo: able to decode the Matrix.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/2026-06-17-morpheus.png" alt="Alt text" width="width" height="height">
</div>
</div>
<div class="paragraph">
<p>No need for colorful pills—just install the Hot Rod dissector plugin in Wireshark and let it do the hard work. Hot Rod
conversations will appear in a clear, readable format. Now just relax, grab a coffee, and focus
on debugging your application instead of spending time decoding bits!</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/2026-06-17-dissector.gif" alt="Alt text" width="width" height="height">
</div>
</div>
<div class="paragraph">
<p>The Hot Rod dissector is available at: <a href="https://github.com/rigazilla/hotrod-dissector" class="bare">https://github.com/rigazilla/hotrod-dissector</a></p>
</div>]]></content><author><name>Vittorio Rigamonti</name></author><category term="hot rod" /><category term="dissector" /><category term="wireshark" /><summary type="html"><![CDATA[Need to debug a Hot Rod conversation? Don&#8217;t do it the hard way anymore— grab the right tool and be like Neo: able to decode the Matrix.]]></summary></entry><entry><title type="html">Beyond Per-Cache Eviction</title><link href="https://infinispan.org/blog/2026/06/16/beyond-per-cache-eviction" rel="alternate" type="text/html" title="Beyond Per-Cache Eviction" /><published>2026-06-16T00:00:00+00:00</published><updated>2026-06-16T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/06/16/beyond-per-cache-eviction</id><content type="html" xml:base="https://infinispan.org/blog/2026/06/16/beyond-per-cache-eviction"><![CDATA[<div class="sect1">
<h2 id="per-cache-memory-limits">Per-cache memory limits</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Up until now, Infinispan has required you to configure memory bounds for each cache individually.
You decide how much memory each cache is allowed to consume, and the eviction machinery enforces those limits independently.</p>
</div>
<div class="paragraph">
<p>Suppose you have a node with 4GB of heap available for caching and five caches to fill it.
You assign each cache its own slice of that budget — say 200MB for some and 300MB for others.
This works, but it means you need to know upfront how much each cache will need.
Caches that don&#8217;t use their full allocation leave memory on the table, while others may be constrained.
Every time you resize a cache you have to revisit the arithmetic.</p>
</div>
<div class="paragraph">
<p>This also means that creating a new cache at runtime requires you to carve out a separate memory allocation for it — memory that has to come from somewhere, potentially requiring you to shrink existing caches.
With shared containers, a new cache can simply join an existing container and immediately share its budget with no reconfiguration needed.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/per-cache-eviction.svg" alt="Per-cache eviction">
</div>
</div>
<div class="paragraph">
<p>Infinispan 16.1 introduced <strong>container eviction</strong> as an alternative approach.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="container-eviction">Container eviction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Container eviction lets you define a single memory boundary that is shared across multiple caches.
Instead of configuring limits per cache, you create a named eviction container at the cache-container level and reference it from any caches that should share that budget.</p>
</div>
<div class="paragraph">
<p>Infinispan supports two types of eviction containers:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>max-size-container</code> — bounds by total memory (e.g., <code>100MB</code>, <code>1GB</code>)</p>
</li>
<li>
<p><code>max-count-container</code> — bounds by total entry count (e.g., <code>1000</code> entries)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>You can define multiple containers if you need different budgets for different groups of caches.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/container-eviction.svg" alt="Container eviction">
</div>
</div>
<div class="paragraph">
<p>In the example below, caches A and B share a <code>hot</code> container (400MB) while caches C, D, and E share a <code>warm</code> container (900MB):</p>
</div>
<details open>
<summary><strong>XML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-xml hljs" data-lang="xml">&lt;cache-container&gt;
   &lt;eviction-containers&gt;
      &lt;max-size-container name="hot" size="400MB"/&gt;
      &lt;max-size-container name="warm" size="900MB"/&gt;
   &lt;/eviction-containers&gt;

   &lt;distributed-cache name="cache-a"&gt;
      &lt;memory eviction-container="hot"/&gt;
   &lt;/distributed-cache&gt;

   &lt;distributed-cache name="cache-b"&gt;
      &lt;memory eviction-container="hot"/&gt;
   &lt;/distributed-cache&gt;

   &lt;distributed-cache name="cache-c"&gt;
      &lt;memory eviction-container="warm"/&gt;
   &lt;/distributed-cache&gt;

   &lt;distributed-cache name="cache-d"&gt;
      &lt;memory eviction-container="warm"/&gt;
   &lt;/distributed-cache&gt;

   &lt;distributed-cache name="cache-e"&gt;
      &lt;memory eviction-container="warm"/&gt;
   &lt;/distributed-cache&gt;
&lt;/cache-container&gt;</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>JSON</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-json hljs" data-lang="json">{
  "infinispan": {
    "cacheContainer": {
      "evictionContainers": {
        "maxSizeContainer": [
          {
            "name": "hot",
            "size": "400MB"
          },
          {
            "name": "warm",
            "size": "900MB"
          }
        ]
      },
      "distributedCache": {
        "cache-a": {
          "memory": {
            "evictionContainer": "hot"
          }
        },
        "cache-b": {
          "memory": {
            "evictionContainer": "hot"
          }
        },
        "cache-c": {
          "memory": {
            "evictionContainer": "warm"
          }
        },
        "cache-d": {
          "memory": {
            "evictionContainer": "warm"
          }
        },
        "cache-e": {
          "memory": {
            "evictionContainer": "warm"
          }
        }
      }
    }
  }
}</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>YAML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-yaml hljs" data-lang="yaml">infinispan:
  cacheContainer:
    evictionContainers:
      maxSizeContainer:
        - name: "hot"
          size: "400MB"
        - name: "warm"
          size: "900MB"
    distributedCache:
      "cache-a":
        memory:
          evictionContainer: "hot"
      "cache-b":
        memory:
          evictionContainer: "hot"
      "cache-c":
        memory:
          evictionContainer: "warm"
      "cache-d":
        memory:
          evictionContainer: "warm"
      "cache-e":
        memory:
          evictionContainer: "warm"</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>Java</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-java hljs" data-lang="java">GlobalConfigurationBuilder global = new GlobalConfigurationBuilder();
global.containerMemoryConfiguration("hot")
      .maxSize("400MB");
global.containerMemoryConfiguration("warm")
      .maxSize("900MB");

ConfigurationBuilder cacheA = new ConfigurationBuilder();
cacheA.memory().containerMemory("hot");

ConfigurationBuilder cacheB = new ConfigurationBuilder();
cacheB.memory().containerMemory("hot");

ConfigurationBuilder cacheC = new ConfigurationBuilder();
cacheC.memory().containerMemory("warm");

ConfigurationBuilder cacheD = new ConfigurationBuilder();
cacheD.memory().containerMemory("warm");

ConfigurationBuilder cacheE = new ConfigurationBuilder();
cacheE.memory().containerMemory("warm");</code></pre>
</div>
</div>
</details>
<div class="sect2">
<h3 id="full-feature-compatibility">Full feature compatibility</h3>
<div class="paragraph">
<p>Shared containers are not a stripped-down mode — all of Infinispan&#8217;s regularly supported features continue to work with container eviction.
Indexing, persistence, transactions, expiration, and listeners all function exactly as they do with per-cache eviction.</p>
</div>
<div class="paragraph">
<p>This matters most when it comes to persistence.
We recommend pairing shared containers with a persistence store as a way to automatically alleviate memory pressure while still retaining access to all entries.
With a store configured, every entry is written through to disk, so when the container evicts an entry to stay within its memory budget the data is still available and can be loaded back on demand.</p>
</div>
<div class="paragraph">
<p>The Soft-Index File Store (SIFS) is a natural fit here.
SIFS is Infinispan&#8217;s default file-based store, and its index is designed to adapt to memory conditions.
Index nodes are held via soft references, so when memory is plentiful they stay cached in the heap for fast lookups.
When the JVM comes under memory pressure, the garbage collector can reclaim those soft references and SIFS transparently reloads the index nodes from disk on the next access.
This makes SIFS an ideal companion to shared container eviction — the store index itself cooperates with the JVM to balance performance and memory usage without any manual tuning.</p>
</div>
<div class="paragraph">
<p>Here is an example of a shared container with a SIFS store configured for the <code>my-cache</code>:</p>
</div>
<details open>
<summary><strong>XML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-xml hljs" data-lang="xml">&lt;cache-container&gt;
   &lt;eviction-containers&gt;
      &lt;max-size-container name="shared" size="1GB"/&gt;
   &lt;/eviction-containers&gt;

   &lt;distributed-cache name="my-cache"&gt;
      &lt;memory eviction-container="shared"/&gt;
      &lt;persistence&gt;
         &lt;file-store/&gt;
      &lt;/persistence&gt;
   &lt;/distributed-cache&gt;

   &lt;distributed-cache name="my-other-cache"&gt;
      &lt;memory eviction-container="shared"/&gt;
   &lt;/distributed-cache&gt;
&lt;/cache-container&gt;</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>JSON</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-json hljs" data-lang="json">{
  "infinispan": {
    "cacheContainer": {
      "evictionContainers": {
        "maxSizeContainer": [
          {
            "name": "shared",
            "size": "1GB"
          }
        ]
      },
      "distributedCache": {
        "my-cache": {
          "memory": {
            "evictionContainer": "shared"
          },
          "persistence": {
            "fileStore": {}
          }
        },
        "my-other-cache": {
          "memory": {
            "evictionContainer": "shared"
          }
        }
      }
    }
  }
}</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>YAML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-yaml hljs" data-lang="yaml">infinispan:
  cacheContainer:
    evictionContainers:
      maxSizeContainer:
        - name: "shared"
          size: "1GB"
    distributedCache:
      "my-cache":
        memory:
          evictionContainer: "shared"
        persistence:
          fileStore: ~
      "my-other-cache":
        memory:
          evictionContainer: "shared"</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>Java</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-java hljs" data-lang="java">GlobalConfigurationBuilder global = new GlobalConfigurationBuilder();
global.containerMemoryConfiguration("shared")
      .maxSize("1GB");

ConfigurationBuilder cache = new ConfigurationBuilder();
cache.memory()
     .containerMemory("shared");
cache.persistence()
     .addSoftIndexFileStore();

ConfigurationBuilder otherCache = new ConfigurationBuilder();
otherCache.memory()
          .containerMemory("shared");</code></pre>
</div>
</div>
</details>
</div>
<div class="sect2">
<h3 id="tradeoffs-to-keep-in-mind">Tradeoffs to keep in mind</h3>
<div class="paragraph">
<p>All caches sharing a container also share the same eviction policy.
This means that a burst of writes to one cache can cause entries from another cache to be evicted.
If you need strict control over a specific cache&#8217;s memory, per-cache eviction is still the right choice.</p>
</div>
<div class="paragraph">
<p>There is a minor memory overhead per entry.
Each entry in a shared container is stored with a wrapper that holds the cache name and value, adding two pointers and an object header.</p>
</div>
<div class="paragraph">
<p>Clearing a cache or removing segments is slower than with per-cache eviction, because Infinispan must iterate through the shared container to find entries belonging to a specific cache.
In practice, this overhead is typically negligible compared to the network cost of state transfer or rebalancing.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
Container eviction is configured per node, not cluster-wide.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>Infinispan 16.2 takes this a step further with <strong>dynamic eviction</strong> that automatically adapts to JVM memory pressure at runtime.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="dynamic-eviction">Dynamic eviction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Container eviction gives you a fixed memory budget, but what happens when the unexpected hits?
A sudden spike of large entries, a burst of temporary computation, or a background task that eats into heap — your fixed budget doesn&#8217;t know about any of it.</p>
</div>
<div class="paragraph">
<p>Starting in Infinispan 16.2, <strong>dynamic eviction</strong> automatically adjusts your container&#8217;s capacity in response to real-time JVM memory pressure.
When the memory monitor detects that the JVM is running low on heap or garbage collection is working too hard, Infinispan shrinks the container to free up space.
When pressure subsides, it gradually grows the container back to its original configured capacity.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/dynamic-eviction-states.svg" alt="Dynamic eviction state machine">
</div>
</div>
<div class="paragraph">
<p>Dynamic eviction operates in three states:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1">Stable</dt>
<dd>
<p>Normal operation. The container uses its full configured capacity.</p>
</dd>
<dt class="hdlist1">Shrinking</dt>
<dd>
<p>Memory pressure detected. The container capacity is progressively reduced to free heap space. Infinispan shrinks aggressively to respond quickly to pressure.</p>
</dd>
<dt class="hdlist1">Growing</dt>
<dd>
<p>Pressure has subsided. The container capacity is cautiously restored, with increasing delays between growth steps. If pressure returns during growth, Infinispan immediately switches back to shrinking.</p>
</dd>
</dl>
</div>
<div class="paragraph">
<p>To enable dynamic eviction, you need two things: a memory monitor on the cache container and the <code>dynamic-resize</code> attribute on your eviction container.</p>
</div>
<details open>
<summary><strong>XML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-xml hljs" data-lang="xml">&lt;cache-container&gt;
   &lt;memory-monitor enabled="true"
                   memory-threshold="0.85"
                   gc-pressure-threshold="0.20" /&gt;
   &lt;eviction-containers&gt;
      &lt;max-size-container name="my-container" size="1GB" dynamic-resize="true"/&gt;
   &lt;/eviction-containers&gt;

   &lt;distributed-cache name="my-cache"&gt;
      &lt;memory eviction-container="my-container"/&gt;
   &lt;/distributed-cache&gt;
&lt;/cache-container&gt;</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>JSON</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-json hljs" data-lang="json">{
  "infinispan": {
    "cacheContainer": {
      "memoryMonitor": {
        "enabled": true,
        "memoryThreshold": 0.85,
        "gcPressureThreshold": 0.20
      },
      "evictionContainers": {
        "maxSizeContainer": [
          {
            "name": "my-container",
            "size": "1GB",
            "dynamicResize": true
          }
        ]
      },
      "distributedCache": {
        "my-cache": {
          "memory": {
            "evictionContainer": "my-container"
          }
        }
      }
    }
  }
}</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>YAML</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-yaml hljs" data-lang="yaml">infinispan:
  cacheContainer:
    memoryMonitor:
      enabled: true
      memoryThreshold: 0.85
      gcPressureThreshold: 0.20
    evictionContainers:
      maxSizeContainer:
        - name: "my-container"
          size: "1GB"
          dynamicResize: true
    distributedCache:
      "my-cache":
        memory:
          evictionContainer: "my-container"</code></pre>
</div>
</div>
</details>
<details>
<summary><strong>Java</strong></summary>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight nowrap"><code class="language-java hljs" data-lang="java">GlobalConfigurationBuilder global = new GlobalConfigurationBuilder();
global.memoryMonitor()
      .enabled(true);
global.containerMemoryConfiguration("my-container")
      .maxSize("1GB")
      .dynamicResize(true);

ConfigurationBuilder cache = new ConfigurationBuilder();
cache.memory()
     .containerMemory("my-container");</code></pre>
</div>
</div>
</details>
<div class="sect2">
<h3 id="caveats">Caveats</h3>
<div class="paragraph">
<p>Dynamic eviction is designed to ease memory pressure, not guarantee prevention of <code>OutOfMemoryError</code>.
It works best for situations like temporary computation spikes or bursts of incoming data.</p>
</div>
<div class="paragraph">
<p>When using a shared container, prefer <code>max-size-container</code> over <code>max-count-container</code>.
A shared container may hold entries of varying sizes across different caches, so a count-based limit may not accurately reflect actual memory consumption.</p>
</div>
<div class="paragraph">
<p>If you use passivation with a dynamically resized container, be aware that evicted entries must be written to the persistence store.
Under memory pressure, this creates additional memory churn that can reduce the effectiveness of the resize.
Infinispan logs a warning at startup when this combination is detected.</p>
</div>
</div>
<div class="sect2">
<h3 id="the-memory-monitor">The memory monitor</h3>
<div class="paragraph">
<p>At the heart of dynamic eviction is the <strong>memory monitor</strong>, a cache-container-level component that continuously tracks JVM memory health.
It watches two signals:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Memory threshold</strong> — the fraction of old-generation heap in use. When usage exceeds this threshold (default: 85%), the monitor raises a low-memory alert.</p>
</li>
<li>
<p><strong>GC pressure threshold</strong> — the fraction of time spent in garbage collection over a rolling window (default: 60 seconds). When GC overhead exceeds this threshold (default: 20%), the monitor raises a GC pressure alert.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Dynamic eviction registers as a listener on the memory monitor and reacts to these alerts by shrinking or growing containers.
But the memory monitor is not exclusive to eviction — it is a general-purpose component available to any Infinispan subsystem.</p>
</div>
<div class="paragraph">
<p>For example, the query engine already uses the memory monitor.
Sorted non-indexed queries need to load all matching results into memory before sorting.
When the memory monitor reports pressure, Infinispan rejects these queries outright rather than risking an <code>OutOfMemoryError</code>.</p>
</div>
<div class="paragraph">
<p>Future Infinispan releases will extend this further, using the memory monitor to adapt additional subsystems under memory pressure.
The monitor provides a single, consistent view of JVM memory health that any component can subscribe to.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="getting-started">Getting started</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Container eviction is available in Infinispan 16.1 and later.
Dynamic eviction requires Infinispan 16.2 and later.</p>
</div>
<div class="paragraph">
<p>For full configuration reference and additional details, see the
<a href="https://infinispan.org/docs/stable/titles/configuring/configuring.html">Infinispan documentation</a>.</p>
</div>
</div>
</div>]]></content><author><name>William Burns</name></author><category term="eviction" /><category term="memory" /><category term="configuration" /><summary type="html"><![CDATA[Per-cache memory limits]]></summary></entry><entry><title type="html">Infinispan 16.2</title><link href="https://infinispan.org/blog/2026/06/03/infinispan-16-2" rel="alternate" type="text/html" title="Infinispan 16.2" /><published>2026-06-03T00:00:00+00:00</published><updated>2026-06-03T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/06/03/infinispan-16.2</id><content type="html" xml:base="https://infinispan.org/blog/2026/06/03/infinispan-16-2"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><strong><em>"Arctic Panzer Wolf"</em></strong></p>
</div>
<div class="paragraph">
<p>Infinispan 16.2 is here, and it is codenamed <a href="https://untappd.com/b/3-floyds-brewing-arctic-panzer-wolf/5776">"Arctic Panzer Wolf"</a>.
Like the beer, this release packs a punch: it&#8217;s bold, it&#8217;s fierce, and there&#8217;s a lot of it.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/arcticpanzerwolf.png" alt="Arctic Panzer Wolf" width="100" height="300">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="resp-endpoint">RESP endpoint</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This release is a huge step forward for our Redis-compatible RESP endpoint. So many new things that it deserves its
own section!</p>
</div>
<div class="sect2">
<h3 id="probabilistic-data-structures">Probabilistic data structures</h3>
<div class="paragraph">
<p>We&#8217;ve implemented a whole family of probabilistic data structures:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Bloom Filters</strong> (<code>BF.*</code>): space-efficient probabilistic membership testing. Add items and check whether they <em>might</em> be
in the set — with a configurable false positive rate.</p>
</li>
<li>
<p><strong>Cuckoo Filters</strong> (<code>CF.*</code>): like Bloom Filters, but they also support deletion. Handy when your data is more dynamic.</p>
</li>
<li>
<p><strong>Count-Min Sketch</strong> (<code>CMS.*</code>): estimate the frequency of items in a stream without storing all the data. Perfect for
anomaly detection and traffic analysis.</p>
</li>
<li>
<p><strong>Top-K</strong> (<code>TOPK.*</code>): maintain a list of the <em>K</em> most frequent items in a stream. While CMS can tell you <em>how many times</em>
an item has appeared, Top-K tells you <em>which items</em> appear most often — it tracks the heavy hitters so you don&#8217;t have to
query every item individually.</p>
</li>
<li>
<p><strong>HyperLogLog</strong> (<code>PFCOUNT</code>, <code>PFMERGE</code>): estimate the cardinality of large datasets using very little memory. Count unique
visitors, unique events, unique everything.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>All of these work in clustered mode too!</p>
</div>
</div>
<div class="sect2">
<h3 id="new-bitop-operations-from-redis-8-2">New BITOP operations from Redis 8.2</h3>
<div class="paragraph">
<p>Keeping up with the latest from Redis, we&#8217;ve added the four new bitwise operations introduced in Redis 8.2:
 These give you finer-grained control over bitwise set operations across keys.</p>
</div>
</div>
<div class="sect2">
<h3 id="geosearch-commands">GEOSEARCH commands</h3>
<div class="paragraph">
<p>The <code>GEOSEARCH</code> family of commands is now fully implemented, letting you perform radius and bounding-box queries on
geospatial data.</p>
</div>
</div>
<div class="sect2">
<h3 id="copy-delex-digest-and-more">COPY, DELEX, DIGEST, and more</h3>
<div class="ulist">
<ul>
<li>
<p><code>COPY</code>: copy a key to another key, optionally replacing the destination.</p>
</li>
<li>
<p><code>DELEX</code>: delete a key only if it exists (returning whether it was actually deleted).</p>
</li>
<li>
<p><code>DIGEST</code>: return a hash digest of a key&#8217;s value.</p>
</li>
<li>
<p><code>SET</code> conditional options: additional flags for conditional set operations.</p>
</li>
<li>
<p><code>PSUBSCRIBE</code> / <code>PUNSUBSCRIBE</code>: pattern-based Pub/Sub subscriptions.</p>
</li>
<li>
<p><code>AGGREGATE COUNT</code> option for <code>ZUNION</code>, <code>ZINTER</code>, <code>ZUNIONSTORE</code>, and <code>ZINTERSTORE</code>.</p>
</li>
<li>
<p><code>BITFIELD</code> / <code>BITOP</code>: implemented all operations.</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="local-resp-caches-in-clustered-mode">Local RESP caches in clustered mode</h3>
<div class="paragraph">
<p>You can now use local RESP caches in a clustered server. Previously, a local RESP cache created on an unclustered server
would prevent the server from starting in clustered mode. No more!</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="infinispan-server">Infinispan Server</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="granular-jvm-options">Granular JVM options</h3>
<div class="paragraph">
<p>The server startup scripts now split <code>JAVA_OPTS</code> into independently overridable categories:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>JAVA_OPTS_BASE</code> — essential JVM flags (headless, ExitOnOutOfMemoryError, incubator modules)</p>
</li>
<li>
<p><code>JAVA_OPTS_NETWORK</code> — network settings (IPv4/IPv6 stack preference)</p>
</li>
<li>
<p><code>JAVA_OPTS_MEMORY</code> — heap, metaspace, and RAM percentage configuration</p>
</li>
<li>
<p><code>JAVA_OPTS_DEBUG</code> — JPDA debug settings</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Want to tweak just the memory settings? Set <code>JAVA_OPTS_MEMORY</code> and leave the rest alone. Setting <code>JAVA_OPTS</code> directly
still overrides everything for backward compatibility.</p>
</div>
</div>
<div class="sect2">
<h3 id="ecs-logging-support">ECS logging support</h3>
<div class="paragraph">
<p>The server now bundles <code>log4j-layout-template-json</code>, enabling native
<a href="https://www.elastic.co/guide/en/ecs/current/index.html">Elastic Common Schema (ECS)</a> formatted logging out of the box.
Just point your Log4j2 config at the built-in ECS template:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;JsonTemplateLayout eventTemplateUri="classpath:EcsLayout.json"/&gt;</code></pre>
</div>
</div>
<div class="paragraph">
<p>No more custom images or runtime library hacking to get structured JSON logs into your Elasticsearch/Kibana stack.</p>
</div>
</div>
<div class="sect2">
<h3 id="simplified-pem-certificate-configuration">Simplified PEM certificate configuration</h3>
<div class="paragraph">
<p>TLS configuration with PEM certificates has been unified and simplified. The server now auto-detects keystore types,
so you no longer need to juggle different property names for PEM vs PKCS#12 certificates.</p>
</div>
</div>
<div class="sect2">
<h3 id="minimal-boot-logging">Minimal boot logging</h3>
<div class="paragraph">
<p>The server now uses minimal logging until the classpath is fully configured, preventing noisy or misleading log
messages during early startup.</p>
</div>
</div>
<div class="sect2">
<h3 id="configurable-backpressure-for-hot-rod-client-listeners">Configurable backpressure for Hot Rod client listeners</h3>
<div class="paragraph">
<p>The server now supports configurable backpressure for Hot Rod client event listeners. When a client cannot consume
events fast enough, the server buffers them up to a configurable limit instead of dropping events or blocking the
originating cache operation. This gives operators control over the trade-off between memory usage and event delivery
reliability for slow consumers.</p>
</div>
</div>
<div class="sect2">
<h3 id="backup-support-for-multimaps-resp-and-memcached-caches">Backup support for multimaps, RESP, and Memcached caches</h3>
<div class="paragraph">
<p>Server backups now include multimap caches as well as caches created via the RESP and Memcached endpoints. Previously,
these were silently skipped during backup and restore operations.</p>
</div>
</div>
<div class="sect2">
<h3 id="rocksdb-cache-store-removed-from-the-server-distribution">RocksDB cache store removed from the server distribution</h3>
<div class="paragraph">
<p>The RocksDB cache store has been moved out of the server distribution. It is still available as a separate module if
you need it, but the server image is now lighter. The bundled SIFS store is the recommended persistence option.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cli">CLI</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="suggestions">Suggestions</h3>
<div class="paragraph">
<p>The CLI now provides suggestions based on your command history and command syntax.</p>
</div>
</div>
<div class="sect2">
<h3 id="connection-bookmarks">Connection bookmarks</h3>
<div class="paragraph">
<p>Tired of typing long connection URLs and credentials every time? The new <code>bookmark</code> command lets you save named
connection bookmarks:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-shell hljs" data-lang="shell">bookmark set prod -u https://prod-server:11222 --username admin --password secret
connect prod</code></pre>
</div>
</div>
<div class="paragraph">
<p>Passwords are stored encrypted in a PKCS12 credential store. Bookmarks are also used by the new <code>mcp</code> CLI command for
a seamless AI integration experience.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="core">Core</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="json-configuration-schemas">JSON configuration schemas</h3>
<div class="paragraph">
<p>Infinispan now provides JSON schemas that mirror the existing XSD schemas, making it easier to author and validate
configuration in JSON and YAML formats with full editor support and autocompletion.</p>
</div>
</div>
<div class="sect2">
<h3 id="pull-based-state-transfer">Pull-based state transfer</h3>
<div class="paragraph">
<p>State transfer has been reworked to use a pull-based approach instead of the previous push model. This gives the
receiving node much better control over backpressure and memory consumption during rebalancing. Previously, all existing
owners would push data simultaneously, making memory pressure scale linearly with cluster size. The new approach is
smarter and more efficient.</p>
</div>
</div>
<div class="sect2">
<h3 id="memory-monitor">Memory monitor</h3>
<div class="paragraph">
<p>A new <code>MemoryMonitor</code> component tracks JVM memory and GC health with configurable thresholds:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Memory threshold alerts when old generation heap usage exceeds a percentage (default 85%)</p>
</li>
<li>
<p>GC duration alerts when a single GC pause is too long (default 5 seconds)</p>
</li>
<li>
<p>GC pressure tracking alerts when too much time is spent in GC over a rolling window</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>All thresholds are tunable at runtime and configurable via a new <code>&lt;memory-monitor&gt;</code> element.</p>
</div>
</div>
<div class="sect2">
<h3 id="dynamic-eviction-based-on-memory-pressure">Dynamic eviction based on memory pressure</h3>
<div class="paragraph">
<p>Eviction can now be driven by actual JVM memory pressure instead of relying solely on static entry counts or fixed
memory sizes. When enabled, Infinispan monitors old generation heap usage and dynamically adjusts eviction thresholds
to keep the JVM healthy. This means caches can make the most of available memory without the guesswork of manually
sizing <code>max-count</code> or <code>max-size</code> — the system responds to real conditions, evicting more aggressively when memory
is tight and relaxing when there is headroom.</p>
</div>
</div>
<div class="sect2">
<h3 id="serialized-cache-entries-on-heap">Serialized cache entries on heap</h3>
<div class="paragraph">
<p>You can now store cache entries in their serialized form even when using heap storage. This is particularly useful for
entries with many fields where the serialized byte representation is more memory-efficient than the full Java object
graph.</p>
</div>
</div>
<div class="sect2">
<h3 id="graceful-shutdown-and-network-partitions">Graceful shutdown and network partitions</h3>
<div class="paragraph">
<p>The graceful shutdown procedure has been hardened to handle network partitions correctly. Previously, star-shaped
partitions (where the coordinator could see all nodes, but nodes couldn&#8217;t see each other) during restart could lead to
data loss. This has been fixed.</p>
</div>
</div>
<div class="sect2">
<h3 id="orderly-cache-scale-down">Orderly cache scale-down</h3>
<div class="paragraph">
<p>Stopping a cache or (concurrently) scaling down a cluster node while state transfer is in progress could previously
result in data loss: the departing node would leave before its data had been fully redistributed to the remaining members.
New overloads on <code>Cache.stop(timeout, TimeUnit)</code>, <code>EmbeddedCacheManager.stop(timeout, TimeUnit)</code>, and
<code>EmbeddedCacheManager.stopCache(cacheName, timeout, TimeUnit)</code> let you specify how long to wait for any in-flight state
transfer to complete before leaving. If the timeout elapses, the method returns <code>false</code> so you can decide what to do next.
The existing no-arg <code>stop()</code> methods continue to work as before.</p>
</div>
</div>
<div class="sect2">
<h3 id="generic-micrometer-meterregistry-support">Generic Micrometer MeterRegistry support</h3>
<div class="paragraph">
<p>Infinispan now supports any Micrometer <code>MeterRegistry</code>, not just Prometheus. This means you can use OTLP, Simple, or any
other Micrometer registry implementation without needing a Prometheus dependency on the classpath.</p>
</div>
</div>
<div class="sect2">
<h3 id="counter-configuration-events">Counter configuration events</h3>
<div class="paragraph">
<p>Counter configuration changes (add/modify/remove) now generate container events and are streamed via the REST SSE
listeners. This enables the Infinispan Operator to implement Counter custom resources.</p>
</div>
</div>
<div class="sect2">
<h3 id="sifs-persistence-store-improvements">SIFS persistence store improvements</h3>
<div class="paragraph">
<p>The Soft-Index File Store (SIFS) has received several reliability and performance improvements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Buffered index updates</strong>: index writes are now batched in memory before being flushed to disk, reducing the number of
I/O operations and improving write throughput.</p>
</li>
<li>
<p><strong>B+ tree refactoring</strong>: the internal B+ tree used for the SIFS index has been extracted into a standalone, independently
testable class, resolving a number of hard-to-reproduce corruption issues related to soft reference reclamation and
concurrent access.</p>
</li>
<li>
<p><strong>Segment lifecycle fixes</strong>: fixed a race condition where rapid segment removal and re-addition could corrupt the index,
and ensured that segment removal completes fully before the segment can be reused.</p>
</li>
<li>
<p><strong>Persistence timeout support</strong>: persistence operations now have configurable timeouts, preventing the system from
hanging indefinitely when a store operation stalls or a lock is never released.</p>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="protostream">ProtoStream</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The ProtoStream serialization library has seen major improvements across performance, usability, and JSON support:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>ASCII string optimizations</strong>: writing ASCII strings now takes advantage of the JVM&#8217;s internal <code>String</code> coder and value
fields, and uses <code>VarHandle</code> for fixed-width writes, reducing encoding overhead.</p>
</li>
<li>
<p><strong>Kotlin support</strong>: the annotation processor now supports Kotlin classes and <code>data class</code> types.</p>
</li>
<li>
<p><strong>Copy-on-write serialization context</strong>: the serialization context state is now copy-on-write, improving thread safety
when schemas are registered concurrently as well as improving performance.</p>
</li>
<li>
<p><strong>JPMS support</strong>: ProtoStream is now fully modularized with proper <code>module-info</code> descriptors.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="query">Query</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="antlr-4">ANTLR 4</h3>
<div class="paragraph">
<p>The Ickle query parser has been migrated from the unmaintained ANTLR 3 to ANTLR 4, improving reproducible builds and
eliminating a stale runtime dependency.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="spring-boot">Spring Boot</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="automatic-schema-registration">Automatic schema registration</h3>
<div class="paragraph">
<p>The Spring Boot integration now automatically registers Protobuf schemas, matching the behavior that was already
available in the Quarkus integration.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="console">Console</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The web console has received a batch of usability improvements:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>User permissions page</strong>: a new page to display current user permissions, making it easier to understand who can
do what.</p>
</li>
</ul>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_My_Permissions_Menu.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_My_Permissions_Menu.png" alt="My Permissions menu" width="300"></a>
</div>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_My_Permissions.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_My_Permissions.png" alt="My Permissions page"></a>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Schema editor with syntax highlighting</strong>: the schema editor now uses the Monaco code editor with Protobuf syntax
highlighting and code assistance.</p>
</li>
</ul>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Edit_Schemas.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Edit_Schemas.png" alt="Schema editor with syntax highlighting"></a>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Manage columns in cache detail</strong>: customize which columns are displayed in the cache entries view.</p>
</li>
</ul>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Columns.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Columns.png" alt="Manage columns dialog"></a>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Query history</strong>: your recent queries are now remembered, so you can quickly re-run them.</p>
</li>
</ul>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Query.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Query.png" alt="Query values view"></a>
</div>
</div>
<div class="imageblock text-center">
<div class="content">
<a class="image" href="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Query_History.png"><img src="/assets/images/blog/2026-infinispan-16-2-release/16.2.Console_Query_History.png" alt="Query history view"></a>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Swagger UI and metrics links</strong>: direct links to the Swagger UI and metrics endpoints from the console.</p>
</li>
<li>
<p><strong>Truncate result values</strong>: a checkbox to truncate long values in query results for better readability.</p>
</li>
<li>
<p><strong>Persistent pagination settings</strong>: pagination preferences are now remembered across sessions.</p>
</li>
<li>
<p><strong>Cache deletion from detail view</strong>: delete a cache directly from its detail page.</p>
</li>
<li>
<p><strong>Internationalization</strong>: updated translations for French, Spanish, Italian, and Brazilian Portuguese.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="mcp">MCP</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="cli-stdio-transport">CLI stdio transport</h3>
<div class="paragraph">
<p>Connecting your AI tools to Infinispan just got a whole lot easier. We&#8217;ve added an <code>mcp</code> command to the CLI that acts as
a stdio transport bridge for the Model Context Protocol.</p>
</div>
<div class="paragraph">
<p>This means you can now use Infinispan as an MCP server from Claude Desktop, Claude Code, VS Code, or any other MCP
client:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-json hljs" data-lang="json">{
  "mcpServers": {
    "infinispan": {
      "command": "infinispan",
      "args": ["mcp", "--bookmark", "prod-cluster"]
    }
  }
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The CLI command connects to the server&#8217;s <code>/v3/mcp</code> HTTP endpoint and proxies JSON-RPC messages via stdin/stdout. Use it
with CLI bookmarks (we mentioned those above!) for a seamless, secure connection.</p>
</div>
</div>
<div class="sect2">
<h3 id="gc-log-as-mcp-resource">GC log as MCP resource</h3>
<div class="paragraph">
<p>The server&#8217;s GC log is now exposed as an MCP resource, so your AI tools can inspect garbage collection behavior directly.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="backwards-compatibility">Backwards compatibility</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan 16.2 is fully backwards compatible with 16.1 deployments.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="the-next-release">The next release</h2>
<div class="sectionbody">
<div class="paragraph">
<p>According to our <a href="/roadmap/">roadmap</a>, our next release will be 16.3, and it will happen on 2026-10-07.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="release-notes">Release notes</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can look at the <a href="https://github.com/infinispan/infinispan/releases/tag/16.2.0">release notes</a> to see what was changed
since our previous release.</p>
</div>
<div class="paragraph">
<p>Get them from our <a href="https://infinispan.org/download/">download page</a>.</p>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="release" /><category term="final" /><summary type="html"><![CDATA["Arctic Panzer Wolf"]]></summary></entry><entry><title type="html">Infinispan Hot Rod JS Client 0.14.0: Near Feature Parity with Java</title><link href="https://infinispan.org/blog/2026/05/06/hotrod-js-client-0-14-0" rel="alternate" type="text/html" title="Infinispan Hot Rod JS Client 0.14.0: Near Feature Parity with Java" /><published>2026-05-06T00:00:00+00:00</published><updated>2026-05-06T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/05/06/hotrod-js-client-014</id><content type="html" xml:base="https://infinispan.org/blog/2026/05/06/hotrod-js-client-0-14-0"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>We are happy to announce the release of the <a href="https://github.com/infinispan/js-client/releases/tag/v0.14.0">Infinispan Hot Rod JS Client 0.14.0</a>.
This release brings the JavaScript client very close to feature parity with the Java Hot Rod client, with support for near caching, distributed counters, admin operations, and protocol auto-negotiation.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="protocol-auto-negotiation">Protocol auto-negotiation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The client can now automatically negotiate the highest mutually-supported protocol version with the server.
No more guessing which version to use: just set <code>version: 'auto'</code> and the client will pick the best available.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">var infinispan = require('infinispan');

var client = await infinispan.client(
  {port: 11222, host: 'localhost'},
  {
    version: 'auto',
    authentication: {
      enabled: true,
      saslMechanism: 'DIGEST-MD5',
      userName: 'admin',
      password: 'changeit'
    }
  }
);

// Check which version was negotiated
console.log('Protocol:', client.getProtocolVersion());  // e.g. '4.1'</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="near-caching">Near caching</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Near caching stores recently accessed entries on the client side, dramatically reducing latency for read-heavy workloads.
The cache uses LRU eviction and is automatically invalidated when entries are modified.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">var client = await infinispan.client(
  {port: 11222, host: 'localhost'},
  {
    version: 'auto',
    nearCache: { maxEntries: 16 },
    authentication: { /* ... */ }
  }
);

await client.put('key', 'value');

// First get: fetches from server and populates the near cache
var v1 = await client.get('key');

// Second get: served from the local near cache, no server roundtrip
var v2 = await client.get('key');

// Writes automatically invalidate the near cache
await client.put('key', 'new-value');</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="distributed-counters">Distributed counters</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Distributed counters are cluster-wide atomic counters, available in two flavors: <strong>strong</strong> (linearizable, optionally bounded) and <strong>weak</strong> (higher concurrency, eventually consistent).</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// Strong unbounded counter
await client.counterCreate('page-views', {
  type: 'strong',
  initialValue: 0
});

// Strong bounded counter
await client.counterCreate('stock', {
  type: 'strong',
  initialValue: 50,
  lowerBound: 0,
  upperBound: 1000,
  storage: 'persistent'
});

// Weak counter for high-throughput scenarios
await client.counterCreate('impressions', {
  type: 'weak',
  initialValue: 0,
  concurrencyLevel: 4
});

// Atomic operations
var current = await client.counterGet('page-views');
var updated = await client.counterAddAndGet('page-views', 1);
var swapped = await client.counterCompareAndSwap('stock', 50, 49);
await client.counterReset('page-views');</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="admin-operations">Admin operations</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <code>client.admin</code> namespace provides cache lifecycle management and Protobuf schema administration, enabling full cluster management directly from JavaScript.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// Create a cache from XML configuration
await client.admin.createCache('sessions',
  '&lt;local-cache&gt;&lt;encoding media-type="text/plain"/&gt;&lt;/local-cache&gt;'
);

// Idempotent create: no error if it already exists
await client.admin.getOrCreateCache('sessions',
  '&lt;local-cache&gt;&lt;encoding media-type="text/plain"/&gt;&lt;/local-cache&gt;'
);

// List all caches
var names = await client.admin.cacheNames();

// Remove a cache
await client.admin.removeCache('sessions');

// Register a Protobuf schema
await client.admin.registerSchema('person.proto', `
  package example;
  message Person {
    required string name = 1;
    optional int32 age = 2;
  }
`);</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="protocol-3-1-4-0-and-4-1-support">Protocol 3.1, 4.0, and 4.1 support</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This release adds support for Hot Rod protocol versions 3.1, 4.0, and 4.1, each bringing new capabilities:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Protocol 3.1</strong>&#8201;&#8212;&#8201;Distributed counter operations</p>
</li>
<li>
<p><strong>Protocol 4.0</strong>&#8201;&#8212;&#8201;Previous value with metadata in mutation responses</p>
</li>
<li>
<p><strong>Protocol 4.1</strong>&#8201;&#8212;&#8201;JSON data format support</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-javascript hljs" data-lang="javascript">// Protocol 4.0+: mutation operations can return previous values
var client = await infinispan.client(
  {port: 11222, host: 'localhost'},
  { version: '4.0', authentication: { /* ... */ } }
);

await client.put('key', 'original');
var prev = await client.put('key', 'updated', { previous: true });
console.log(prev);  // 'original'</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="get-started">Get started</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Install the client via npm:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">npm install infinispan</code></pre>
</div>
</div>
<div class="paragraph">
<p>Check out the full <a href="https://github.com/infinispan/js-client">source code and documentation</a> on GitHub.</p>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="hotrod" /><category term="javascript" /><category term="client" /><category term="release" /><summary type="html"><![CDATA[We are happy to announce the release of the Infinispan Hot Rod JS Client 0.14.0. This release brings the JavaScript client very close to feature parity with the Java Hot Rod client, with support for near caching, distributed counters, admin operations, and protocol auto-negotiation.]]></summary></entry><entry><title type="html">Vector search quickstart with Infinispan</title><link href="https://infinispan.org/blog/2026/04/30/vector-search-quickstart" rel="alternate" type="text/html" title="Vector search quickstart with Infinispan" /><published>2026-04-30T00:00:00+00:00</published><updated>2026-04-30T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/04/30/vector-search-quickstart</id><content type="html" xml:base="https://infinispan.org/blog/2026/04/30/vector-search-quickstart"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Vector databases have become essential building blocks for AI-powered applications.
They let you store unstructured data — text, images, audio — as numerical embeddings that capture semantic meaning, and then find similar items using nearest-neighbour search.</p>
</div>
<div class="paragraph">
<p>Infinispan has supported vector search since version 15, and it does so with a distinctive approach: your data model is defined through <strong>ProtoStream annotations</strong> that generate Protobuf schemas automatically, your queries use the <strong>Ickle query language</strong> that seamlessly combines relational filters, full-text search, and kNN vector predicates, and your schemas can <strong>evolve over time</strong> without breaking existing clients.</p>
</div>
<div class="paragraph">
<p>This quickstart walks through a complete example — from defining a data model to running hybrid vector+metadata queries — using a catalogue of beers as our dataset.
If the beer names look familiar, that&#8217;s because every Infinispan release is named after a beer.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="defining-the-data-model">Defining the data model</h2>
<div class="sectionbody">
<div class="paragraph">
<p>In Infinispan, entity classes are plain Java records (or POJOs) annotated with ProtoStream and indexing annotations.
These annotations serve double duty: they define the Protobuf serialization schema <strong>and</strong> the search index mapping in a single place.</p>
</div>
<div class="paragraph">
<p>Here is our <code>Beer</code> entity:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Proto
@Indexed
public record Beer(
   @Keyword(projectable = true, sortable = true)
   String name,

   @Keyword(projectable = true, normalizer = "lowercase")
   String style,

   @Keyword(projectable = true, sortable = true, normalizer = "lowercase")
   String brewery,

   @Keyword(projectable = true, normalizer = "lowercase")
   String country,

   @Basic(projectable = true, sortable = true)
   Double abv,

   @Text
   String description,

   @Vector(dimension = 3, similarity = VectorSimilarity.COSINE)
   float[] descriptionEmbedding
) {
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>A few things to note:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>@Proto</code> generates the Protobuf schema from the record fields — no separate <code>.proto</code> file to maintain.</p>
</li>
<li>
<p><code>@Indexed</code> enables search indexing for the entity.</p>
</li>
<li>
<p><code>@Keyword</code> fields are stored as exact tokens — ideal for beer names, styles, and brewery names. The <code>normalizer</code> option allows case-insensitive matching.</p>
</li>
<li>
<p><code>@Text</code> fields are analyzed with a full-text tokenizer, enabling natural language search across tasting notes and descriptions.</p>
</li>
<li>
<p><code>@Vector(dimension = 3, similarity = VectorSimilarity.COSINE)</code> marks the embedding field for kNN search. The <code>dimension</code> must match your vector size — here we use 3 for illustration, but a real embedding model would produce 384 or more dimensions.</p>
</li>
<li>
<p><code>@Basic</code> handles numeric fields like ABV with support for range queries and sorting.</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="generating-the-protobuf-schema">Generating the Protobuf schema</h3>
<div class="paragraph">
<p>ProtoStream generates the schema and the marshaller at compile time.
All you need is an interface:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@ProtoSchema(includeClasses = Beer.class, schemaPackageName = "quickstart")
public interface BeerSchema extends GeneratedSchema {
   BeerSchema INSTANCE = new BeerSchemaImpl();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>The generated <code>.proto</code> schema is automatically registered with the server when your client connects.
This schema can evolve — you can add new fields, deprecate old ones — without breaking clients that are still using the previous version. This is a direct benefit of Protobuf&#8217;s forwards and backwards compatibility guarantees.</p>
</div>
<div class="paragraph">
<p>Imagine you later want to add food pairing suggestions or IBU ratings: just add the field to the record, and older clients that don&#8217;t know about it will continue to work.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="connecting-and-storing-data">Connecting and storing data</h2>
<div class="sectionbody">
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer().host("localhost").port(11222)
   .security().authentication().username("admin").password("secret");

RemoteCacheManager cacheManager = new RemoteCacheManager(builder.build());

RemoteCache&lt;String, Beer&gt; cache = cacheManager.administration()
   .getOrCreateCache("beers", new XMLStringConfiguration(
      "&lt;local-cache&gt;" +
      "  &lt;indexing storage=\"filesystem\"&gt;" +
      "    &lt;indexed-entities&gt;" +
      "      &lt;indexed-entity&gt;quickstart.Beer&lt;/indexed-entity&gt;" +
      "    &lt;/indexed-entities&gt;" +
      "  &lt;/indexing&gt;" +
      "&lt;/local-cache&gt;"));</code></pre>
</div>
</div>
<div class="paragraph">
<p>Now let&#8217;s populate the cache with some beers — all named after Infinispan releases.</p>
</div>
<div class="paragraph">
<p>In a production application you would generate embeddings from a model such as <code>all-MiniLM-L6-v2</code> (384 dimensions).
For this quickstart we use hand-crafted 3-dimensional vectors where each axis captures a flavour profile: <strong>dark/roasty</strong>, <strong>light/crisp</strong>, and <strong>hoppy/craft</strong>.
Beers of similar style naturally cluster together in this space — stouts near <code>[1, 0, 0]</code>, lagers near <code>[0, 1, 0]</code>, IPAs near <code>[0, 0, 1]</code> — so kNN queries return intuitive results.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">cache.put("beer:1", new Beer(
   "Guinness", "Stout", "Guinness Brewery", "Ireland", 4.2,
   "A rich, creamy stout with deep roasted barley flavours, hints of coffee and chocolate, and a velvety smooth finish.",
   new float[]{0.95f, 0.05f, 0.10f}
));

cache.put("beer:2", new Beer(
   "Delirium", "Belgian Strong Ale", "Brouwerij Huyghe", "Belgium", 8.5,
   "A complex strong blonde ale with fruity esters, spicy phenols, and a warming alcohol presence balanced by a dry finish.",
   new float[]{0.30f, 0.30f, 0.70f}
));

cache.put("beer:3", new Beer(
   "Estrella Galicia", "Lager", "Hijos de Rivera", "Spain", 5.5,
   "A crisp European lager with a balanced malt backbone, mild hop bitterness, and a clean refreshing finish.",
   new float[]{0.10f, 0.90f, 0.15f}
));

cache.put("beer:4", new Beer(
   "Mahou", "Pilsner", "Mahou San Miguel", "Spain", 5.5,
   "A golden pilsner with delicate floral hop aromas, light biscuity malt, and a bright effervescent character.",
   new float[]{0.05f, 0.85f, 0.25f}
));

cache.put("beer:5", new Beer(
   "Corona Extra", "Pale Lager", "Grupo Modelo", "Mexico", 4.5,
   "A light, easy-drinking pale lager with subtle sweetness, a hint of citrus, and a crisp dry finish best enjoyed ice-cold.",
   new float[]{0.05f, 0.95f, 0.10f}
));

cache.put("beer:6", new Beer(
   "Tactical Nuclear Penguin", "Imperial Stout", "BrewDog", "Scotland", 32.0,
   "An extreme imperial stout aged in whisky casks, intensely smoky with dark chocolate, coffee, and dried fruit notes.",
   new float[]{0.98f, 0.02f, 0.20f}
));

cache.put("beer:7", new Beer(
   "Brahma", "Lager", "Ambev", "Brazil", 4.3,
   "A light Brazilian lager, smooth and mildly sweet, brewed for easy drinking in warm weather.",
   new float[]{0.05f, 0.92f, 0.05f}
));

cache.put("beer:8", new Beer(
   "Radegast", "Czech Lager", "Radegast Brewery", "Czech Republic", 5.0,
   "A traditional Czech lager with a prominent Saaz hop aroma, bready malt character, and a crisp bitter finish.",
   new float[]{0.15f, 0.80f, 0.30f}
));

cache.put("beer:9", new Beer(
   "Turia", "Märzen", "Turia Brewery", "Spain", 5.4,
   "A toasted amber märzen from Valencia with caramel malt sweetness, a nutty aroma, and a smooth medium body.",
   new float[]{0.60f, 0.40f, 0.15f}
));

cache.put("beer:10", new Beer(
   "Hoptimus Prime", "IPA", "Hoptimus Brewing", "USA", 7.5,
   "An aggressively hopped American IPA bursting with tropical fruit, pine resin, and grapefruit citrus over a sturdy malt backbone.",
   new float[]{0.10f, 0.15f, 0.95f}
));

cache.put("beer:11", new Beer(
   "Pagoa", "Basque Ale", "Pagoa Brewery", "Spain", 5.0,
   "A craft ale from the Basque Country with earthy hops, a light fruity character, and a balanced malty sweetness.",
   new float[]{0.25f, 0.35f, 0.60f}
));</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="querying-with-ickle">Querying with Ickle</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Before we get to vector search, let&#8217;s look at what Ickle can do with traditional queries.
This is where Infinispan stands out: you don&#8217;t need to learn a separate query syntax for metadata filters and vector search — Ickle handles both.</p>
</div>
<div class="sect2">
<h3 id="full-text-search">Full-text search</h3>
<div class="paragraph">
<p>Find beers whose description mentions "chocolate":</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Beer&gt; query = cache.query(
   "from quickstart.Beer b where b.description : 'chocolate'");
List&lt;Beer&gt; results = query.list();
// Returns: Guinness, Tactical Nuclear Penguin</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="keyword-and-range-filters">Keyword and range filters</h3>
<div class="paragraph">
<p>Find session beers (under 5% ABV) from Spain:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Beer&gt; query = cache.query(
   "from quickstart.Beer b where b.country = 'Spain' and b.abv &lt; 5.0");
List&lt;Beer&gt; results = query.list();</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="projections-and-sorting">Projections and sorting</h3>
<div class="paragraph">
<p>Select specific fields and sort by ABV — a good way to build a menu card:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Object[]&gt; query = cache.query(
   "select b.name, b.style, b.brewery, b.abv from quickstart.Beer b " +
   "where b.country = 'Spain' order by b.abv");
List&lt;Object[]&gt; results = query.list();
// Turia (5.4), Estrella Galicia (5.5), Mahou (5.5), Pagoa (5.0)</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="vector-search-knn">Vector search (kNN)</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Now for the main event.
Vector search in Ickle uses the <code>&lt;-&gt;</code> operator to express a kNN predicate: find the <em>k</em> nearest neighbours of a given vector.</p>
</div>
<div class="sect2">
<h3 id="basic-knn-query">Basic kNN query</h3>
<div class="paragraph">
<p>Find the 3 beers closest to the "dark roasty" end of our flavour space:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Beer&gt; query = cache.query(
   "from quickstart.Beer b where b.descriptionEmbedding &lt;-&gt; [:v]~:k");
query.setParameter("v", new float[]{0.9f, 0.1f, 0.1f});
query.setParameter("k", 3);

List&lt;Beer&gt; results = query.list();
// Returns: Guinness, Tactical Nuclear Penguin, Turia</code></pre>
</div>
</div>
<div class="paragraph">
<p>Both the vector and <em>k</em> are parameterised — you don&#8217;t need to interpolate values into the query string.</p>
</div>
</div>
<div class="sect2">
<h3 id="score-projection">Score projection</h3>
<div class="paragraph">
<p>To see how close each result is to the query vector, project the score:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Object[]&gt; query = cache.query(
   "select b.name, b.style, score(b) from quickstart.Beer b " +
   "where b.descriptionEmbedding &lt;-&gt; [:v]~:k");
query.setParameter("v", new float[]{0.05f, 0.9f, 0.1f});
query.setParameter("k", 3);

List&lt;Object[]&gt; results = query.list();
for (Object[] row : results) {
   System.out.printf("%-30s %-20s score=%.4f%n", row[0], row[1], row[2]);
}
// Corona Extra  score=1.0000
// Brahma        score=0.9992
// Estrella      score=0.9985</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="hybrid-queries-vector-metadata">Hybrid queries: vector + metadata</h2>
<div class="sectionbody">
<div class="paragraph">
<p>This is where Ickle really shines.
You can combine kNN search with any classic predicate — keyword matches, range filters, full-text search — using a <code>filtering</code> clause.</p>
</div>
<div class="sect2">
<h3 id="something-like-a-lager-but-not-too-strong">"Something like a lager, but not too strong"</h3>
<div class="paragraph">
<p>Find the 3 beers closest to the "light crisp" vector, but only lagers under 5% ABV:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Object[]&gt; query = cache.query(
   "select score(b), b.name, b.style, b.abv from quickstart.Beer b " +
   "where b.descriptionEmbedding &lt;-&gt; [:v]~:k " +
   "filtering (b.style = 'Lager' and b.abv &lt; 5.0)");
query.setParameter("v", new float[]{0.05f, 0.95f, 0.05f});
query.setParameter("k", 3);

List&lt;Object[]&gt; results = query.list();
// Returns: Brahma (4.3%)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="vector-search-filtered-by-country">Vector search filtered by country</h3>
<div class="paragraph">
<p>Find beers from Spain closest to a "toasted malty" profile:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Object[]&gt; query = cache.query(
   "select score(b), b.name, b.style, b.abv from quickstart.Beer b " +
   "where b.descriptionEmbedding &lt;-&gt; [:v]~:k filtering b.country = 'Spain'");
query.setParameter("v", new float[]{0.7f, 0.3f, 0.1f});
query.setParameter("k", 3);

List&lt;Object[]&gt; results = query.list();
// Returns: Turia (0.99), Pagoa (0.80), Estrella Galicia (0.75)</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="combining-full-text-and-vector-search">Combining full-text and vector search</h3>
<div class="paragraph">
<p>Find beers whose descriptions mention "citrus" and are closest to the "hoppy craft" vector:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Query&lt;Object[]&gt; query = cache.query(
   "select score(b), b.name, b.brewery, b.abv from quickstart.Beer b " +
   "where b.descriptionEmbedding &lt;-&gt; [:v]~:k " +
   "filtering b.description : 'citrus'");
query.setParameter("v", new float[]{0.1f, 0.1f, 0.95f});
query.setParameter("k", 5);

List&lt;Object[]&gt; results = query.list();
// Returns: Hoptimus Prime (0.9993), Corona Extra (0.6061)</code></pre>
</div>
</div>
<div class="paragraph">
<p>The filtering clause accepts any valid Ickle predicate — including boolean combinations with <code>and</code> / <code>or</code> — so you can build arbitrarily complex filters. The filter is applied <strong>before</strong> the kNN search, narrowing the candidate set.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="why-infinispan-for-vector-search">Why Infinispan for vector search?</h2>
<div class="sectionbody">
<div class="paragraph">
<p>If you&#8217;re evaluating vector databases, here is what sets Infinispan apart:</p>
</div>
<div class="paragraph">
<p><strong>Unified query language.</strong>
Ickle gives you relational queries, full-text search, and vector kNN search in a single language.
No need for a separate "search module" with its own syntax — the same <code>cache.query(&#8230;&#8203;)</code> call handles everything.</p>
</div>
<div class="paragraph">
<p><strong>Type-safe data modelling.</strong>
ProtoStream annotations let you define your schema, serialization, and index mappings in one place.
The Protobuf schema is generated at compile time, so schema mismatches are caught before they reach production.</p>
</div>
<div class="paragraph">
<p><strong>Schema evolution.</strong>
Protobuf&#8217;s compatibility guarantees mean you can add new fields (like a vector embedding column) to an existing entity without breaking older clients.
Roll out vector search incrementally — existing applications keep working while new ones start populating and querying the embedding field.</p>
</div>
<div class="paragraph">
<p><strong>Distributed by design.</strong>
kNN queries work across a distributed cluster.
Infinispan scatters data across nodes and fans out vector searches in parallel, merging results transparently.</p>
</div>
<div class="paragraph">
<p><strong>Multiple access protocols.</strong>
The same indexed cache is accessible via Hot Rod (Java, C#, JS, Python), REST, and the RESP (Redis-compatible) protocol.
Your vector search investment is not locked into a single client ecosystem.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="tuning-vector-indexing">Tuning vector indexing</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <code>@Vector</code> annotation exposes HNSW graph parameters that let you trade indexing speed for search accuracy.
These become important with real embedding models where the dimension is much larger (e.g. 384 or 768):</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Vector(
   dimension = 384,                         // match your embedding model
   similarity = VectorSimilarity.COSINE,
   beamWidth = 512,                         // graph construction quality (default: 512)
   maxConnections = 16                      // neighbours per node (default: 16, range: 2-100)
)
float[] descriptionEmbedding;</code></pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><strong><code>beamWidth</code></strong> (efConstruction): higher values build a more accurate graph at the cost of slower indexing.</p>
</li>
<li>
<p><strong><code>maxConnections</code></strong> (m): controls memory consumption and search precision. Stay in the 2–100 range.</p>
</li>
<li>
<p><strong><code>similarity</code></strong>: choose from <code>L2</code> (default, euclidean), <code>COSINE</code>, <code>INNER_PRODUCT</code>, or <code>MAX_INNER_PRODUCT</code> depending on your embedding model&#8217;s recommendations.</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="run-it-yourself">Run it yourself</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A complete runnable version of this quickstart is available in the <a href="https://github.com/infinispan/infinispan-simple-tutorials/tree/main/infinispan-remote/vector-search">infinispan-simple-tutorials</a> repository.
It uses Testcontainers to start an Infinispan server automatically — all you need is Docker and Maven:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-bash hljs" data-lang="bash">git clone https://github.com/infinispan/infinispan-simple-tutorials.git
cd infinispan-simple-tutorials
mvn -pl infinispan-remote/vector-search compile exec:exec</code></pre>
</div>
</div>
<div class="paragraph">
<p>The tutorial uses the same 3-dimensional hand-crafted vectors shown in this post. To move to a real embedding model, change the <code>dimension</code> in <code>@Vector</code> and replace the <code>float[]</code> literals with vectors from your model — the query patterns stay identical.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="next-steps">Next steps</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Read the full <a href="https://infinispan.org/docs/stable/titles/query/query.html">Query guide</a> for Ickle syntax details.</p>
</li>
<li>
<p>Explore <a href="https://infinispan.org/docs/stable/titles/encoding/encoding.html">ProtoStream encoding</a> to learn about schema evolution and custom marshallers.</p>
</li>
<li>
<p>Check out the <a href="https://infinispan.org/tutorials/">tutorials</a> for more runnable examples.</p>
</li>
<li>
<p>Join the conversation on <a href="https://infinispan.zulipchat.com/">Zulip</a> or <a href="https://github.com/infinispan/infinispan-simple-tutorials/discussions">GitHub Discussions</a>.</p>
</li>
</ul>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="vector" /><category term="knn" /><category term="embeddings" /><category term="tutorial" /><summary type="html"><![CDATA[Vector databases have become essential building blocks for AI-powered applications. They let you store unstructured data — text, images, audio — as numerical embeddings that capture semantic meaning, and then find similar items using nearest-neighbour search.]]></summary></entry><entry><title type="html">Infinispan Joins the OGX Ecosystem as a Vector IO Provider</title><link href="https://infinispan.org/blog/2026/04/17/infinispan-joins-ogx-ecosystem" rel="alternate" type="text/html" title="Infinispan Joins the OGX Ecosystem as a Vector IO Provider" /><published>2026-04-17T10:00:00+00:00</published><updated>2026-04-17T10:00:00+00:00</updated><id>https://infinispan.org/blog/2026/04/17/infinispan-joins-ogx-ecosystem</id><content type="html" xml:base="https://infinispan.org/blog/2026/04/17/infinispan-joins-ogx-ecosystem"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan has been integrated into the <a href="https://ogx-ai.github.io/">OGX (Open GenAI Stack)</a>, formerly known as Llama Stack, as a vector IO provider, enabling developers to build RAG (Retrieval-Augmented Generation) applications with distributed vector search.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="what-is-ogx">What is OGX?</h2>
<div class="sectionbody">
<div class="paragraph">
<p><a href="https://ogx-ai.github.io/">OGX (Open GenAI Stack)</a> is an open-source agentic API server that composes inference providers, vector stores, safety backends, tool runtimes, and file storage into a single deployable server for building complete AI applications. It serves as a drop-in replacement for the OpenAI API and can run anywhere with any model and infrastructure.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="infinispans-vector-capabilities">Infinispan&#8217;s Vector Capabilities</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The integration brings Infinispan&#8217;s distributed caching architecture to RAG applications with three powerful search modes:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Vector Search</strong>: Embedding-based similarity search using cosine similarity</p>
</li>
<li>
<p><strong>Keyword Search</strong>: Full-text search via Infinispan Query DSL or Ickle</p>
</li>
<li>
<p><strong>Hybrid Search</strong>: Combined vector and keyword search with configurable reranking (RRF or weighted)</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Additional features include HTTPS/TLS support, Basic and Digest authentication, and seamless REST API integration.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="try-the-demo">Try the Demo</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Check out our demo project at <a href="https://github.com/rigazilla/infinispan-llama-stack-rag-demo" class="bare">https://github.com/rigazilla/infinispan-llama-stack-rag-demo</a> that shows how to configure Infinispan as the vector IO backend for OGX RAG workflows, with examples demonstrating document upload, vector embedding storage, and semantic search. As a bonus, you&#8217;ll also learn about the fascinating pataphysical science of Chelonofelodynamics!</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="learn-more">Learn More</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p><a href="https://ogx-ai.github.io/docs/providers/vector_io/remote_infinispan">OGX Infinispan Provider Documentation</a></p>
</li>
<li>
<p><a href="https://ogx-ai.github.io/docs/providers">OGX Providers Overview</a></p>
</li>
<li>
<p><a href="https://infinispan.org/docs/stable/titles/query/query.html#vector-search_searching-and-querying">Infinispan Vector Search Documentation</a></p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Try it out and let us know what you build!</p>
</div>
</div>
</div>]]></content><author><name>Vittorio Rigamonti</name></author><category term="vector-search" /><category term="ogx" /><category term="llama-stack" /><category term="rag" /><summary type="html"><![CDATA[Infinispan has been integrated into the OGX (Open GenAI Stack), formerly known as Llama Stack, as a vector IO provider, enabling developers to build RAG (Retrieval-Augmented Generation) applications with distributed vector search.]]></summary></entry><entry><title type="html">Exploring the New OpenAPI-Compliant REST API v3 in Infinispan 16.1</title><link href="https://infinispan.org/blog/2026/04/02/exploring-new-openapi-compliant-rest-api-v3-infinispan-161" rel="alternate" type="text/html" title="Exploring the New OpenAPI-Compliant REST API v3 in Infinispan 16.1" /><published>2026-04-02T18:00:00+00:00</published><updated>2026-04-02T18:00:00+00:00</updated><id>https://infinispan.org/blog/2026/04/02/exploring-new-openapi-compliant-rest-api-v3-infinispan-161</id><content type="html" xml:base="https://infinispan.org/blog/2026/04/02/exploring-new-openapi-compliant-rest-api-v3-infinispan-161"><![CDATA[<hr>
<h1 id="exploring-the-new-openapi-compliant-rest-api-v3-in-infinispan-16-1" class="sect0">Exploring the New OpenAPI-Compliant REST API v3 in Infinispan 16.1</h1>
<div class="paragraph">
<p>With Infinispan 16.1 "Polly Want a Pilsner", we introduce the OpenAPI-compliant v3 REST API, bringing modern API standards to Infinispan.</p>
</div>
<div class="sect1">
<h2 id="the-evolution">The Evolution</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan now embraces OpenAPI standards with the new v3 REST API, enabling better tooling, documentation, and developer experience through standardized API descriptions.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="whats-new-in-v3">What&#8217;s New in v3?</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Full OpenAPI 3.0 compliance</p>
</li>
<li>
<p>Interactive Swagger UI at <code>/swagger-ui</code></p>
</li>
<li>
<p>Improved endpoint organization and type safety</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="getting-started">Getting Started</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Start an Infinispan Server 16.1+ and visit <code><a href="http://127.0.0.1:11222/swagger-ui/" class="bare">http://127.0.0.1:11222/swagger-ui/</a></code> to explore and test the API. The OpenAPI schema is available at <code><a href="http://127.0.0.1:11222/rest/v3/openapi" class="bare">http://127.0.0.1:11222/rest/v3/openapi</a></code>.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="https://infinispan.org/assets/images/blog/swaggerui.png" alt="Infinispan v3 REST API Swagger UI">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="openapi-benefits">OpenAPI Benefits</h2>
<div class="sectionbody">
<div class="paragraph">
<p>A standardized, rich API description like OpenAPI brings numerous advantages for developers:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Enhanced Documentation</strong>: Interactive, always-up-to-date API docs via Swagger UI</p>
</li>
<li>
<p><strong>Automated Tooling</strong>: Generate client libraries in multiple languages automatically</p>
</li>
<li>
<p><strong>Improved Testing</strong>: Built-in API testing interfaces</p>
</li>
<li>
<p><strong>Polyglot Support</strong>: OpenAPI works across programming languages, enabling seamless integration regardless of your tech stack</p>
</li>
<li>
<p><strong>AI-Assisted Development</strong>: AI code assistants can generate accurate client code from the OpenAPI spec, reducing development time and errors</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Check out our demo at <a href="https://github.com/rigazilla/infinispan-sqlstore-demo/tree/openapi-demo" class="bare">https://github.com/rigazilla/infinispan-sqlstore-demo/tree/openapi-demo</a>, which demonstrates a live coding session where AI code assistants generate accurate Infinispan client code from the OpenAPI spec, showcasing rapid development and error reduction.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="conclusion">Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The v3 API brings modern API standards to Infinispan, enhancing developer productivity. Download 16.1, try the new OpenAPI v3 REST interface, and let us know your feedback!</p>
</div>
<div class="paragraph">
<p>For details, see <a href="https://infinispan.org/docs/stable/titles/rest/rest.html">REST API docs</a>.</p>
</div>
</div>
</div>]]></content><author><name>Vittorio Rigamonti</name></author><category term="infinispan" /><category term="rest" /><category term="openapi" /><category term="16.1" /><category term="api" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Infinispan 16.1</title><link href="https://infinispan.org/blog/2026/02/04/infinispan-16-1" rel="alternate" type="text/html" title="Infinispan 16.1" /><published>2026-02-04T00:00:00+00:00</published><updated>2026-02-04T00:00:00+00:00</updated><id>https://infinispan.org/blog/2026/02/04/infinispan-16.1</id><content type="html" xml:base="https://infinispan.org/blog/2026/02/04/infinispan-16-1"><![CDATA[<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p><strong><em>"Polly Want a Pilsner"</em></strong></p>
</div>
<div class="paragraph">
<p>Infinispan 16.1 is here, and it is codenamed <a href="https://untappd.com/b/hop-city-brewing-co-polly-want-a-pilsner/1372902">"Polly Want a Pilsner"</a>.</p>
</div>
<div class="paragraph">
<p>It is the first release that follows <a href="/roadmap/">our time-boxed plan</a>.</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/pollywantapilsner.png" alt="Polly Want a Pilsner" width="406" height="696">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="container-based-eviction">Container-based eviction</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Prior to this release, it was only possible to configure memory bounds for individual caches.</p>
</div>
<div class="paragraph">
<p>Infinispan 16.1 introduces the concept of bounded memory containers: configure a global container (you can have more
than one), and whether you want it to be bound by count or by memory and reference that container name in the cache
configuration.</p>
</div>
<div class="paragraph">
<p>The following diagram shows the difference between per-cache and container eviction</p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/memory-container.png" alt="Memory container">
</div>
</div>
<div class="paragraph">
<p>In the example below, the memory configuration for caches <code>a</code> and <code>b</code> combined will be bounded at <code>100MB</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml">&lt;infinispan&gt;
   &lt;cache-container&gt;
      &lt;eviction-containers&gt;
         &lt;max-size-container name="max-size" size="100MB"/&gt;
      &lt;/eviction-containers&gt;

      &lt;local-cache name="a"&gt;
          &lt;memory eviction-container="max-size"/&gt;
      &lt;/local-cache&gt;

      &lt;local-cache name="b"&gt;
          &lt;memory eviction-container="max-size"/&gt;
      &lt;/local-cache&gt;
   &lt;/cache-container&gt;
&lt;/infinispan&gt;</code></pre>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="openapi-swagger">OpenAPI / Swagger</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan 10 introduced the <code>v2</code> REST endpoint which aimed to improve the original API and expand it with all the
requirements we added for data and system management. Unfortunately that API was designed in a way that made it
impossible to represent via an OpenAPI descriptor.</p>
</div>
<div class="paragraph">
<p>Infinispan 16.1 introduces a new <code>v3</code> REST API which <strong>is</strong> OpenAPI-compliant.</p>
</div>
<div class="paragraph">
<p>To try it out, start an Infinispan Server and point to <a href="http://127.0.0.1:11222/swagger-ui/" class="bare">http://127.0.0.1:11222/swagger-ui/</a></p>
</div>
<div class="imageblock text-center">
<div class="content">
<img src="/assets/images/blog/swaggerui.png" alt="Infinispan v3 REST API">
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="query-and-lucene-10">Query and Lucene 10</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Thanks to the work done by our friends over at <a href="https://hibernate.org/search/">Hibernate Search</a>, Infinispan now supports
both Lucene 9 and 10.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="infinispan-server">Infinispan Server</h2>
<div class="sectionbody">
<div class="sect2">
<h3 id="server-now-requires-java-25">Server now requires Java 25</h3>
<div class="paragraph">
<p>Our server image has already included Java 25 since 16.0, but we&#8217;ve now made this a hard requirement for bare metal
deployments too.
Don&#8217;t worry: the clients can continue running with <em>ye olde</em> legacy Java versions of choice.</p>
</div>
</div>
<div class="sect2">
<h3 id="lucene-10-3">Lucene 10.3</h3>
<div class="paragraph">
<p>By requiring Java 25, the server can now include
<a href="https://lucene.apache.org/core/corenews.html#apache-lucenetm-1030-available">Lucene 10.3</a>, which can leverage SIMD to
increase performance in several areas.</p>
</div>
</div>
<div class="sect2">
<h3 id="pre-start-batch-scripts">Pre-start batch scripts</h3>
<div class="paragraph">
<p>The server can now automatically execute batch scripts before start.
This feature is primarily intended for our operator in order to prepare the server according to its configuration, but
it may also be useful for your own use-cases.</p>
</div>
</div>
<div class="sect2">
<h3 id="aot-cache-in-the-container-image">AOT cache in the container image</h3>
<div class="paragraph">
<p>The server image now includes an <a href="https://openjdk.org/jeps/483">AOT cache</a> to reduce startup and warmup time.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="cli-batch-error-handling">CLI batch error handling</h2>
<div class="sectionbody">
<div class="paragraph">
<p>The CLI has been enhanced to better report errors during batch execution: you can now decide to fail-fast, fail-at-end
or ignore batch errors altogether.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="default-mechanisms">Default mechanisms</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan Server no longer enables weak/vulnerable authentication mechanisms by default: algorithms which use
<code>MD5</code> and <code>SHA-1</code> must be explicitly enabled if you want to keep using them.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="backwards-compatibility">Backwards compatibility</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Infinispan 16.1 is fully backwards compatible with 16.0 deployments.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="the-next-release">The next release</h2>
<div class="sectionbody">
<div class="paragraph">
<p>According to our <a href="/roadmap/">roadmap</a>, our next release will be 16.2, and it will happen on 2026-06-03.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="release-notes">Release notes</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can look at the <a href="https://github.com/infinispan/infinispan/releases/tag/16.1.0">release notes</a> to see what was changed
since our previous release.</p>
</div>
<div class="paragraph">
<p>Get them from our <a href="https://infinispan.org/download/">download page</a>.</p>
</div>
</div>
</div>]]></content><author><name>Tristan Tarrant</name></author><category term="release" /><category term="final" /><summary type="html"><![CDATA["Polly Want a Pilsner"]]></summary></entry></feed>