CLOVER🍀

That was when it all began.

Infinispan 11でApache LuceneのDirectory実装が削除されたという話

これは、なにをしたくて書いたもの?

なんとなく、ちょっと前から気づいていたのですが、InfinispanからApache LuceneのDirectoryに関する実装が削除されています。

ISPN-11568 Remove InfinispanIndexManager and dependencies by gustavonalle · Pull Request #8137 · infinispan/infinispan · GitHub

[ISPN-11568] Remove InfinispanIndexManager and dependencies - Red Hat Issue Tracker

Infinispan 11.0.0.Finalで完全に削除されていますね。

削除された結果、どうなっていたかを見ていなかったのですが、今回少し見てみたので。

InfinispanとApache Lucene

Infinispanは、ずいぶん前のバージョンから、Apache LuceneのDirectoryの実装を持っていました。

Infinispanクラスター上に展開されたApache LuceneのDirectoryを使うこともできますし、Hibernate Searchと合わせた
検索機能を実現することもできます。

Infinispan 10.1の時点だと、検索に関するドキュメントはこんな感じになっています。

Indexing and Querying

f:id:Kazuhira:20200823143753p:plain

Apache Luceneとのインテグレーションに関しては、こんな感じのドキュメントになっていました。

Apache Lucene

f:id:Kazuhira:20200823144036p:plain

InfinispanとApache Luceneを統合する場合、Apache Luceneのインデックスの配置先は以下の3つから選ぶことができます。

  • local-heap
  • filesystem
  • infinispan

最初の2つはApache Lucene自身によるものですが、最後の「infinispan」はInfinispanが実装しているDirectory実装です。

InfinispanのDirectory実装を使う時には、Apache LuceneのDirectoryを実現するための3つのCacheの設定、インデックスに
格納するデータのコントロール(Index mode)、InfinispanIndexManagerの利用など、Infinispan上でどのように
インデックスを構築するかを設定していっていました。

experimentalな機能として、Elasticsearchにインデックスを持たせることもできましたね。

このあたりが、Infinispan 11で削除されているようです。

Infinispan 11の検索機能

Infinispan 11では、Apache LuceneのDirectoryに関する機能が削除されています。

ISPN-11568 Remove InfinispanIndexManager and dependencies by gustavonalle · Pull Request #8137 · infinispan/infinispan · GitHub

[ISPN-11568] Remove InfinispanIndexManager and dependencies - Red Hat Issue Tracker

アーティファクトでいくと、infinispan-lucene-directoryとinfinispan-directory-providerの2つがなくなっています。

インデックスとクエリに関するドキュメントも、Infinispan 11ではだいぶすっきりしました。

Indexing and Searching

f:id:Kazuhira:20200823022405p:plain

インテグレーションのドキュメントには、まだApache Luceneの記述が残っているんですけどね…。

Apache Lucene

とはいえ、これらのアーティファクトはInfinispan 11.0.0.Dev03が最後になっており、Finalになる前に削除されています。

 curl -s 'http://search.maven.org/solrsearch/select?q=a:infinispan-lucene-directory' | jq '.response.docs[] | {id, g, latestVersion}'
{
  "id": "org.infinispan:infinispan-lucene-directory",
  "g": "org.infinispan",
  "latestVersion": "11.0.0.Dev03"
}


$ curl -s 'http://search.maven.org/solrsearch/select?q=a:infinispan-directory-provider' | jq '.response.docs[] | {id, g, latestVersion}'
{
  "id": "org.infinispan:infinispan-directory-provider",
  "g": "org.infinispan",
  "latestVersion": "11.0.0.Dev03"
}

で、Infinispan 11でどうなったかというと、シンプルにHibernate Searchに任せる感じになったみたいです。

よって、インデックスの格納先は

  • local-heap
  • filesystem

の2つとなります。

Infinispan 11と、依存するHibernate Search 5.7ではApache Lucene 5.5が利用されるのですが、Infinispan 12ではHibernate Searchの
アップグレードに伴ってApache Lucene 8が使えるように進めているようなので、期待を…。

The first of our development releases of Infinispan 12 is here and it features our upgrade to Hibernate Search 6 which finally allows us to upgrade to Lucene 8.x.

Blog: Infinispan 12.0.0.Dev01 - Infinispan

クエリに関しては、過去はQuery DSL、Ickle QueryとAPIがあったのですが、Infinispan 11の時点でドキュメントに残っているのは
Ickle Queryのみですね。

infinispan-search-mapperという新しいモジュールも作られているようなので、12の様子をみておきましょう。

$ curl -s 'http://search.maven.org/solrsearch/select?q=a:infinispan-search-mapper&core=gav' | jq '.response.docs[] | {id, g}'
{
  "id": "org.infinispan:infinispan-search-mapper:12.0.0.Dev02",
  "g": "org.infinispan"
}
{
  "id": "org.infinispan:infinispan-search-mapper:12.0.0.Dev01",
  "g": "org.infinispan"
}

と、現状の整理はこれくらいにして、ちょっと使ってみましょうか。Embedded ModeでInfinispanの検索機能を使ってみます。

環境

今回の環境は、こちら。

$ java --version
openjdk 11.0.8 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 11.0.8, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-112-generic", arch: "amd64", family: "unix"

準備

Maven依存関係は、こちら。

    <dependencies>
        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-query</artifactId>
            <version>11.0.3.Final</version>
        </dependency>
        <dependency>
            <groupId>org.infinispan.protostream</groupId>
            <artifactId>protostream-processor</artifactId>
            <version>4.3.3.Final</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-kuromoji</artifactId>
            <version>5.5.5</version>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.6.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.6.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.16.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.30</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

使用するInfinispanは、11.0.3.Finalとします。

Infinispan 11で検索機能を使うにあたり、最低限必要なのはこのあたりですね。

        <dependency>
            <groupId>org.infinispan</groupId>
            <artifactId>infinispan-query</artifactId>
            <version>11.0.3.Final</version>
        </dependency>
        <dependency>
            <groupId>org.infinispan.protostream</groupId>
            <artifactId>protostream-processor</artifactId>
            <version>4.3.3.Final</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
            <optional>true</optional>
        </dependency>

その他は、日本語検索やテストライブラリです。

テストコードの雛形

確認は、テストコードで行っていきます。

最初に雛形を作成しましょう。
src/test/java/org/littlewings/infinispan/query/simply/QueryTest.java

package org.littlewings.infinispan.query.simply;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.List;
import java.util.OptionalLong;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.infinispan.Cache;
import org.infinispan.manager.DefaultCacheManager;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.query.Indexer;
import org.infinispan.query.Search;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.dsl.QueryResult;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.in;

public class QueryTest {
    <K, V> void withCache(String cacheName, int numInstances, Consumer<Cache<K, V>> func) {
        List<EmbeddedCacheManager> managers =
                IntStream
                        .rangeClosed(1, numInstances)
                        .mapToObj(i -> {
                            try {
                                System.setProperty("node.index", Integer.toString(i));
                                System.setProperty("node.start.timestamp", Long.toString(System.currentTimeMillis()));
                                return new DefaultCacheManager("infinispan.xml");
                            } catch (IOException e) {
                                throw new UncheckedIOException(e);
                            }
                        })
                        .collect(Collectors.toList());

        managers.forEach(m -> m.getCache(cacheName));

        try {
            Cache<K, V> cache = managers.get(0).getCache(cacheName);

            func.accept(cache);
        } finally {
            managers.forEach(m -> m.stop());
        }
    }

    // ここに、テストを書く!
}

テスト内でクラスターを簡単に構築するようにしています。システムプロパティをなんか触っていますが、それはまた後で。

設定ファイルについては、こんな感じで。
src/test/resources/infinispan.xml

<?xml version="1.0" encoding="UTF-8"?>
<infinispan
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="urn:infinispan:config:11.0 https://infinispan.org/schemas/infinispan-config-11.0.xsd"
        xmlns="urn:infinispan:config:11.0">

    <cache-container shutdown-hook="REGISTER">
        <transport cluster="udp"/>

        <!-- SerializationとCacheの定義 -->

    </cache-container>
</infinispan>

コメントにしている部分は、これから追って記載します。

インデックスなしでの検索

最初に、インデックスなしでクエリを使ってみましょう。

扱うお題は書籍にして、エンティティはこんな感じで用意。
src/test/java/org/littlewings/infinispan/query/simply/Book.java

package org.littlewings.infinispan.query.simply;

import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

public class Book {
    @ProtoField(number = 1, type = Type.STRING)
    String isbn;

    @ProtoField(number = 2, type = Type.STRING)
    String title;

    @ProtoField(number = 3, type = Type.INT32, defaultValue = "0")
    int price;

    @ProtoFactory
    public static Book create(String isbn, String title, int price) {
        Book book = new Book();

        book.setIsbn(isbn);
        book.setTitle(title);
        book.setPrice(price);

        return book;
    }

    // getter/setterは省略
}

ProtoStreamを使用するので、SerializationContextInitializerを継承したインターフェースが必要になります。
src/test/java/org/littlewings/infinispan/query/simply/EntitiesInitializer.java

package org.littlewings.infinispan.query.simply;

import org.infinispan.protostream.SerializationContextInitializer;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;

@AutoProtoSchemaBuilder(
        includeClasses = {Book.class},
        schemaFileName = "entities.proto",
        schemaFilePath = "proto/",
        schemaPackageName = "entities"
)
public interface EntitiesInitializer extends SerializationContextInitializer {
}

これで、Protocol BuffersのIDLが自動生成されます。

Marshalling Custom Java Objects with ProtoStream

こちらは、ビルド時に実装クラスが生成されるので、設定ファイルのSerializationに関する部分に追加。

    <cache-container shutdown-hook="REGISTER">
        <transport cluster="udp"/>

        <serialization>
            <context-initializer class="org.littlewings.infinispan.query.simply.EntitiesInitializerImpl"/>
        </serialization>

    </cache-container>

Cacheの定義も行いましょう。

    <cache-container shutdown-hook="REGISTER">
        <transport cluster="udp"/>

        <serialization>
            <context-initializer class="org.littlewings.infinispan.query.simply.EntitiesInitializerImpl"/>
        </serialization>

        <distributed-cache name="bookCache"/>
    </cache-container>

Distributed Cacheをひとつ追加です。

テストコード。

    @Test
    public void nonIndexingSearch() {
        this.<String, Book>withCache("bookCache", 3, cache -> {
            List<Book> books = List.of(
                    Book.create("978-1782169970", "Infinispan Data Grid Platform Definitive Guide", 5242),
                    Book.create("978-4048917353", "Redis入門 インメモリKVSによる高速データ管理", 3400),
                    Book.create("978-4798045733", "RDB技術者のためのNoSQLガイド", 3400),
                    Book.create("978-1785285332", "Getting Started with Hazelcast - Second Edition", 4129),
                    Book.create("978-1789347531", "Apache Ignite Quick Start Guide: Distributed data caching and processing made easy", 3476)
            );

            books.forEach(book -> cache.put(book.getIsbn(), book));

            QueryFactory qf = Search.getQueryFactory(cache);

            Query<Book> query =
                    qf.create("from " + Book.class.getName()
                            + " where price > :price order by price desc");
            query.setParameter("price", 4000);

            List<Book> results = query.execute().list();

            assertThat(results).hasSize(2);
            assertThat(results.get(0).getTitle()).isEqualTo("Infinispan Data Grid Platform Definitive Guide");
            assertThat(results.get(1).getTitle()).isEqualTo("Getting Started with Hazelcast - Second Edition");
        });
    }

クエリは、Ickcle Queryの構文で書きます。こちらを参考に。

Ickle Query Language Parser Syntax

Ickle Queryは、JPQLのサブセットみたいなものです。JPQLライクな検索と、全文検索の両方を利用することができます。

The Ickle query language is small subset of the JPQL query language, with some extensions for full-text.

今回のテストコードのパターンだと、インデックスの定義は行っていないので、全文検索のクエリは使えないのですが。

インデックスありでの検索

次に、インデックスありでの検索を行ってみます。

エンティティの定義が変わるので、新しいものをひとつ作成。
src/test/java/org/littlewings/infinispan/query/simply/IndexedBook.java

package org.littlewings.infinispan.query.simply;

import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.Analyzer;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.infinispan.protostream.annotations.ProtoFactory;
import org.infinispan.protostream.annotations.ProtoField;
import org.infinispan.protostream.descriptors.Type;

@Indexed
public class IndexedBook {
    @Field(analyze = Analyze.NO)
    @ProtoField(number = 1, type = Type.STRING)
    String isbn;

    @Field(analyzer = @Analyzer(impl = JapaneseAnalyzer.class))
    @ProtoField(number = 2, type = Type.STRING)
    String title;

    @Field
    @ProtoField(number = 3, type = Type.INT32, defaultValue = "0")
    int price;

    @ProtoFactory
    public static IndexedBook create(String isbn, String title, int price) {
        IndexedBook book = new IndexedBook();

        book.setIsbn(isbn);
        book.setTitle(title);
        book.setPrice(price);

        return book;
    }

    // getter/setterは省略
}

Hibernate Searchに関するアノテーションが追加されます。

Mapping entities to the index structure

titleに関しては、JapaneseAnalyzerを使って日本語検索ができるようにしておきました(大した意味はありませんが)。

エンティティを追加したので、SerializationContextInitializerを拡張したインターフェースにも追加しておきます。
src/test/java/org/littlewings/infinispan/query/simply/EntitiesInitializer.java

package org.littlewings.infinispan.query.simply;

import org.infinispan.protostream.SerializationContextInitializer;
import org.infinispan.protostream.annotations.AutoProtoSchemaBuilder;

@AutoProtoSchemaBuilder(
        includeClasses = {Book.class, IndexedBook.class},
        schemaFileName = "entities.proto",
        schemaFilePath = "proto/",
        schemaPackageName = "entities"
)
public interface EntitiesInitializer extends SerializationContextInitializer {
}

続いて、Cacheの設定を行います。「indexedBookCache」という名のDistributed Cacheを追加。

    <cache-container shutdown-hook="REGISTER">
        <transport cluster="udp"/>

        <serialization>
            <context-initializer class="org.littlewings.infinispan.query.simply.EntitiesInitializerImpl"/>
        </serialization>

        <distributed-cache name="bookCache"/>

        <distributed-cache name="indexedBookCache">
            <indexing>
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.simply.IndexedBook</indexed-entity>
                </indexed-entities>

                <property name="default.indexmanager">near-real-time</property>
                <property name="default.directory_provider">filesystem</property>
                <property name="default.indexBase">target/infinispan-lucene-index/index-${node.index}-${node.start.timestamp}</property>
            </indexing>
        </distributed-cache>
    </cache-container>

インデックスの設定は、indexingで行います。

            <indexing>

              ...

            </indexing>

Configuration

現在のInfinispanでは、インデックスに登録する型を明示的に指定するよう推奨されているので、こちらも記述しておきます。

Specifying Indexed Entities

            <indexing>
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.simply.IndexedBook</indexed-entity>
                </indexed-entities>
              ...

            </indexing>

インデックスの保存先と、IndexManagerの設定はpropertyで行います。

Index Storage

Index Manager

            <indexing>
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.simply.IndexedBook</indexed-entity>
                </indexed-entities>

                <property name="default.indexmanager">near-real-time</property>
                <property name="default.directory_provider">filesystem</property>
                <property name="default.indexBase">target/infinispan-lucene-index/index-${node.index}-${node.start.timestamp}</property>
            </indexing>

今回、インデックスの保存先としてはローカルファイルシステムを選びました。テストコード内でクラスターを組むため、
異なるNodeがApache LuceneのDirectoryを見てしまうと困るので、インデックスの配置先パスはシステムプロパティで
制御しています。

プログラムとしては、EmbeddedCacheManagerを作成していたこの部分ですね。

        List<EmbeddedCacheManager> managers =
                IntStream
                        .rangeClosed(1, numInstances)
                        .mapToObj(i -> {
                            try {
                                System.setProperty("node.index", Integer.toString(i));
                                System.setProperty("node.start.timestamp", Long.toString(System.currentTimeMillis()));
                                return new DefaultCacheManager("infinispan.xml");
                            } catch (IOException e) {
                                throw new UncheckedIOException(e);
                            }
                        })
                        .collect(Collectors.toList());

IndexManagerには、「near-real-time」を選びました。「near-real-time」なIndexManagerは、Apache LuceneのDirectoryへの
データのflush頻度を下げて書き込みパフォーマンスを上げるものですね。反対は「directory-based」なIndexManagerです。
それぞれ、トレードオフがあります。

クエリを使ったコードを書いてみます。

    @Test
    public void indexingSearch1() {
        this.<String, IndexedBook>withCache("indexedBookCache", 3, cache -> {
            List<IndexedBook> books = List.of(
                    IndexedBook.create("978-1782169970", "Infinispan Data Grid Platform Definitive Guide", 5242),
                    IndexedBook.create("978-4048917353", "Redis入門 インメモリKVSによる高速データ管理", 3400),
                    IndexedBook.create("978-4798045733", "RDB技術者のためのNoSQLガイド", 3400),
                    IndexedBook.create("978-1785285332", "Getting Started with Hazelcast - Second Edition", 4129),
                    IndexedBook.create("978-1789347531", "Apache Ignite Quick Start Guide: Distributed data caching and processing made easy", 3476)
            );

            books.forEach(book -> cache.put(book.getIsbn(), book));

            QueryFactory qf = Search.getQueryFactory(cache);

            Query<IndexedBook> query =
                    qf.create("from " + IndexedBook.class.getName()
                            + " where title: 'guide' and price: [4000 to *] order by price desc");

            List<IndexedBook> results = query.execute().list();

            assertThat(results).hasSize(1);
            assertThat(results.get(0).getTitle()).isEqualTo("Infinispan Data Grid Platform Definitive Guide");
        });
    }

APIの使い方はインデックスなしの場合と変わりませんが、クエリの構文が変化します。

            Query<IndexedBook> query =
                    qf.create("from " + IndexedBook.class.getName()
                            + " where title: 'guide' and price: [4000 to *] order by price desc");

全文検索向けのものになりますね。

Using Full-text search

これは、インデックスの設定を使い、エンティティのフィールドをインデックスに保存したりしていないと使えません。

反対に、インデックスの設定を入れているエンティティおよびフィールドに対して、JPQLライクな検索条件で検索することも
できません。

日本語検索が有効になっていることを確認するために、もうひとつテストコードを。

    @Test
    public void indexingSearch2() {
        this.<String, IndexedBook>withCache("indexedBookCache", 3, cache -> {
            List<IndexedBook> books = List.of(
                    IndexedBook.create("978-1782169970", "Infinispan Data Grid Platform Definitive Guide", 5242),
                    IndexedBook.create("978-4048917353", "Redis入門 インメモリKVSによる高速データ管理", 3400),
                    IndexedBook.create("978-4798045733", "RDB技術者のためのNoSQLガイド", 3400),
                    IndexedBook.create("978-1785285332", "Getting Started with Hazelcast - Second Edition", 4129),
                    IndexedBook.create("978-1789347531", "Apache Ignite Quick Start Guide: Distributed data caching and processing made easy", 3476)
            );

            books.forEach(book -> cache.put(book.getIsbn(), book));

            QueryFactory qf = Search.getQueryFactory(cache);

            Query<IndexedBook> badQuery =
                    qf.create("from " + IndexedBook.class.getName()
                            + " where title: 'メモ' and price: [* to 3500] order by price desc");

            List<IndexedBook> badResults = badQuery.execute().list();
            assertThat(badResults).isEmpty();

            Query<IndexedBook> query =
                    qf.create("from " + IndexedBook.class.getName()
                            + " where title: 'メモリ' and price: [* to 3500] order by price desc");

            List<IndexedBook> results = query.execute().list();

            assertThat(results).hasSize(1);
            assertThat(results.get(0).getTitle()).isEqualTo("Redis入門 インメモリKVSによる高速データ管理");
        });
    }

このドキュメントに対して、「メモ」ではヒットしませんが、「メモリ」ではヒットすることが確認できます。

                    IndexedBook.create("978-4048917353", "Redis入門 インメモリKVSによる高速データ管理", 3400),

これで、インデックスありの場合も確認できました、と。

インデックスのリビルド

最後に、インデックスのリビルドを行うAPIも使ってみます。

インデックスのリビルドを行うと、Cacheに保存されているデータから、インデックスが再構築されます。インデックスに保存する
エンティティの設定、たとえばインデックスへの保存有無、Analyzerの設定などを変更した場合はインデックスのリビルドが
必要になります。

Rebuilding Indexes

今回は扱うデータ量が少ないのであっさりと終わりますが、データが多い場合は時間がかかるはずなので注意が必要です。

インデックスのリビルドを動かしてみるコードは、こちら。

    @Test
    public void rebuildIndex() {
        this.<String, IndexedBook>withCache("indexedBookCache", 3, cache -> {
            List<IndexedBook> books = List.of(
                    IndexedBook.create("978-1782169970", "Infinispan Data Grid Platform Definitive Guide", 5242),
                    IndexedBook.create("978-4048917353", "Redis入門 インメモリKVSによる高速データ管理", 3400),
                    IndexedBook.create("978-4798045733", "RDB技術者のためのNoSQLガイド", 3400),
                    IndexedBook.create("978-1785285332", "Getting Started with Hazelcast - Second Edition", 4129),
                    IndexedBook.create("978-1789347531", "Apache Ignite Quick Start Guide: Distributed data caching and processing made easy", 3476)
            );

            books.forEach(book -> cache.put(book.getIsbn(), book));

            Indexer indexer = Search.getIndexer(cache);

            CompletionStage<Void> stage = indexer.run();
            stage.toCompletableFuture().join();

            assertThat(indexer.isRunning()).isFalse();
        });
    }

リビルドの実行自体はすごく簡単です。

まとめ

Infinispan 11で、Apache LuceneのDirectory実装が削除され、検索やインデックスまわりがどう変わったのかを見てみましたが、
Infinispanの実装がなくなった分だけだいぶすっきりした感じがしますね。

個人的には、InfinispanのDirectory実装という機能はなかなか面白かったのですが、なくなってしまったのはまあ仕方がないかなとも。
今後は、Elasticsearchを使った検索も復活…するのかな?

今回作成したソースコードは、こちらに置いています。

https://github.com/kazuhira-r/infinispan-getting-started/tree/master/embedded-query-simply