CLOVER🍀

That was when it all began.

InfinispanのAffinity Index(AffinityIndexManager)を試す

Infinispan 9.0.0.Finalから、実験的な機能としてAffinityIndexManagerという機能が追加されました。

AffinityIndexManager

これがなんなのかですが、前提知識としてInfinispanの全文検索機能の話が出てきます。

Infinispanの全文検索機能

Infinispanの全文検索機能は、InfinispanがApache LuceneのDirectoryの実装を提供しており、この機能を使うことで
実現しています。

実際に検索を行う際には、Apache LuceneAPIを直接使ってもいいのですが、Hibernate SearchのAPI、もしくは
InfinispanのQuery DSL、Ickle Queryを使うと便利です。


厳密に言うと、Hibernate Searchを使う場合はApache Luceneのインデックスの保存先はRAM、ファイル、Infinispanの
3つから選ぶことになりますが、今回はInfinispanに保存した場合のみで考えることにします。

で、IndexManagerはHibernate Searchの話なので、AffinityIndexManagerの話については少なくともHibernate Searchを
使う前提で読む内容になります。

Infinispan側でもこれまでInfinispanIndexManagerというIndexManagerの実装を提供しており、Infinispanのクラスタ内で
全文検索を使うことができたのですが、Apache Luceneのインデックス更新の仕組み上、"ひとつのNodeしかインデックスを更新できない"
という制限がありました。

クラスタ内で分散ロックを取る期間は絞っているものの、これがよくアーキテクチャ的な制限としてInfinispanの
ドキュメントにも時々登場します。

Architectural limitations

This Directory implementation makes it possible to have almost real-time reads across multiple nodes. A fundamental limitation of the Lucene design is that only a single IndexWriter is allowed to make changes on the index: a pessimistic lock is acquired by the writer;

http://infinispan.org/docs/9.0.x/user_guide/user_guide.html#architectural_limitations

このため、書き込みが高い頻度で競合するとタイムアウトが発生する可能性がある、とドキュメントには書かれています。

この制限を緩和しようというのが、今回追加されたAffinityIndexManagerです。

AffinityIndexManagerとは?

AffinityIndexManagerは、簡単に言うとInfinispan上にあるApache Luceneのインデックスをシャード分割して管理しようという
IndexManagerの実装です。

これにより、複数のIndexWriterが使えるようになり、書き込みの制限が緩和されることが期待されます。

Infinispanでインデキシングを有効化した際に、クラスタ内のどのインデックスを更新するかを決める設定があるのですが、
こちらをPRIMARY_OWNERとし、さらにシャードの分割数を指定すること(しない場合はSegmentの数)、

なお、IndexのAffinityに関するデザインは、こちらに書かれています。

infinispan-designs/Index-affinity-proposal.asciidoc at master · infinispan/infinispan-designs · GitHub

準備

と、前置きはこれくらいにして、使っていってみましょう。

設定などの比較のため、

  • InfinispanIndexManagerを使った設定
  • AffinityIndexManagerを使いつつシャード数は未指定
  • AffinityIndexManagerを使いつつシャード数を指定

の3パターンを書いていきます。

まずは、依存関係としてこのように定義。

libraryDependencies ++= Seq(
  "org.infinispan" % "infinispan-query" % "9.0.0.Final" % Compile,
  "net.jcip" % "jcip-annotations" % "1.0" % Provided,
  "org.apache.lucene" % "lucene-analyzers-kuromoji" % "5.5.4" % Compile,
  "org.scalatest" %% "scalatest" % "3.0.3" % Test
)

検索機能を使うには、「infinispan-query」があればOKです。あとは、形態素解析用にKuromojiを追加しています。

検索対象のEntityの定義は、このような定義とします。お題は書籍で。
src/main/scala/org/littlewings/infinispan/query/affinityindex/Book.scala

package org.littlewings.infinispan.query.affinityindex

import org.apache.lucene.analysis.ja.JapaneseAnalyzer
import org.hibernate.search.annotations.{Analyze, Analyzer, Field, Indexed}

import scala.beans.BeanProperty

object Book {
  def apply(isbn: String, title: String, price: Int): Book = {
    val book = new Book
    book.isbn = isbn
    book.title = title
    book.price = price
    book
  }
}

@Indexed
@Analyzer(impl = classOf[JapaneseAnalyzer])
@SerialVersionUID(1L)
class Book extends Serializable {
  @Field(analyze = Analyze.NO)
  @BeanProperty
  var isbn: String = _

  @Field
  @BeanProperty
  var title: String = _

  @Field
  @BeanProperty
  var price: Int = _
}

テストコードの雛形。
src/test/scala/org/littlewings/infinispan/query/affinityindex/AffinityIndexQuerySpec.scala

package org.littlewings.infinispan.query.affinityindex

import org.infinispan.Cache
import org.infinispan.manager.DefaultCacheManager
import org.infinispan.query.Search
import org.infinispan.query.affinity.ShardAllocatorManager
import org.scalatest.{FunSuite, Matchers}

class AffinityIndexQuerySpec extends FunSuite with Matchers {
  val books: Array[Book] = Array(
    Book("978-4798142470", "Spring徹底入門 Spring FrameworkによるJavaアプリケーション開発", 4320),
    Book("978-4774182179", "[改訂新版]Spring入門 ――Javaフレームワーク・より良い設計とアーキテクチャ", 4104),
    Book("978-4774161631", "[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン", 3888),
    Book("978-4048662024", "高速スケーラブル検索エンジン ElasticSearch Server", 6915),
    Book("978-4774183169", "パーフェクト Java EE", 3456),
    Book("978-4798140926", "Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築", 4104)
  )

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

  protected def withCache[K, V](cacheName: String, numInstances: Int = 1)(fun: Cache[K, V] => Unit): Unit = {
    val managers = (1 to numInstances).map(_ => new DefaultCacheManager("infinispan.xml"))
    managers.foreach(_.getCache[K, V](cacheName))

    try {
      val cache = managers(0).getCache[K, V](cacheName)
      fun(cache)
      cache.stop()
    } finally {
      managers.foreach(_.stop())
    }
  }
}

テストデータと、簡単にクラスタを構成するヘルパーメソッド付き。

なお、今回の確認ではクラスタのサイズは3とするようにします。

Infinispanの設定はまずはこんな感じで、具体的な設定はまた都度書いていきます。
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:9.0 http://www.infinispan.org/schemas/infinispan-config-9.0.xsd"
        xmlns="urn:infinispan:config:9.0">
    <jgroups>
        <stack-file name="udp" path="default-configs/default-jgroups-udp.xml"/>
    </jgroups>
    <cache-container>
        <jmx duplicate-domains="true"/>
        <transport cluster="test-cluster" stack="udp"/>

    <!-- あとで -->

    </cache-container>
</infinispan>

ふつうの全文検索

まずは、AffinityIndexManagerを使わない、ある意味でふつうの設定をしてみたいと思います。

Cacheの定義は、こんな感じ。

        <distributed-cache name="bookCache">
            <indexing index="LOCAL">
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.affinityindex.Book</indexed-entity>
                </indexed-entities>

                <property name="hibernate.search.default.directory_provider">infinispan</property>
                <property name="hibernate.search.default.exclusive_index_use">true</property>
                <property name="hibernate.search.default.indexmanager">
                    org.infinispan.query.indexmanager.InfinispanIndexManager
                </property>
                <property name="hibernate.search.default.reader.strategy">shared</property>
                <property name="hibernate.search.lucene_version">LUCENE_CURRENT</property>
            </indexing>
        </distributed-cache>

indexing要素のauto-config属性を使ってもいいのですが、今回は明示しました。

auto-configについては、こちら。

Automatic configuration

これでインデキシング(Cache#put)や検索、

  test("indexing local only") {
    withCache[String, Book]("bookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val qf = Search.getQueryFactory(cache)
      val query =
        qf.create(s"from ${classOf[Book].getName} b where b.title: (+'検索' or +'構築') order by b.price desc")

      val results = query.list[Book]
      results should have size (3)
      results.get(0).title should be("高速スケーラブル検索エンジン ElasticSearch Server")
      results.get(1).title should be("Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築")
      results.get(2).title should be("[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン")
    }
  }

インデックスのリビルドができます。

  test("indexing local only, reindex") {
    withCache[String, Book]("bookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val searchManager = Search.getSearchManager(cache)
      searchManager.getMassIndexer.start()
    }
  }

AffinityIndexManagerを使う

では、AffinityIndexManagerを使ってみましょう。

こちらのドキュメントを参考にしつつ、

AffinityIndexManager

設定。
※この設定、特にkey-partitionerについては間違っている可能性があります。詳しくは、後半へ

        <distributed-cache name="affinityIndexBookCache"
                           key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
            <indexing index="PRIMARY_OWNER">
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.affinityindex.Book</indexed-entity>
                </indexed-entities>

                <property name="hibernate.search.default.directory_provider">infinispan</property>
                <property name="hibernate.search.default.exclusive_index_use">true</property>
                <property name="hibernate.search.default.indexmanager">
                    org.infinispan.query.affinity.AffinityIndexManager
                </property>
                <property name="hibernate.search.default.reader.strategy">shared</property>
                <property name="lhibernate.search.ucene_version">LUCENE_CURRENT</property>
            </indexing>
        </distributed-cache>

ポイントとなるのは、

  • indexingタグのindex要素が「PRIMARY_OWNER」
  • Hibernate SearchのIndexManagerの指定が「org.infinispan.query.affinity.AffinityIndexManager」

です。PRIMARY_OWNERを指定することで、該当のNodeがPrimary Ownerであるエントリのみをインデキシングします。

Index mode

あと、ドキュメントによるとkey-partitionerを指定すべきだということなので、「org.infinispan.distribution.ch.impl.AffinityPartitioner」を
Distributed Cacheのkey-partitionerに指定しました。
※ここについてはちょっと疑わしいので、後半で確認します

key-partitionerは、Replicated Cacheでも使うことができます。

使い方は、InfinispanIndexManagerを使っていた時となにも変わりません。

インデキシング+検索。

  test("using affinity-index-manager") {
    withCache[String, Book]("affinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val qf = Search.getQueryFactory(cache)
      val query =
        qf.create(s"from ${classOf[Book].getName} b where b.title: (+'検索' or +'構築') order by b.price desc")

      val results = query.list[Book]
      results should have size (3)
      results.get(0).title should be("高速スケーラブル検索エンジン ElasticSearch Server")
      results.get(1).title should be("Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築")
      results.get(2).title should be("[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン")
    }
  }

インデックスのリビルド。

  test("using affinity-index-manager, reindex") {
    withCache[String, Book]("affinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val searchManager = Search.getSearchManager(cache)
      searchManager.getMassIndexer.start()
    }
  }

シャード数を指定する

AffiinityIndexManagerを使う際には、シャード数の指定が重要のようです。

シャード数を多くすると、インデキシングのスループットは向上するものの、検索のパフォーマンスは悪くなってしまう、と。

The number of shards affects directly the query performance and writing throughput: generally speaking, a high number of shards offers better write throughput but has an adverse effect on query performance.

http://infinispan.org/docs/9.0.x/user_guide/user_guide.html#query.affinity-index-manager

今回は、シャード数を9としてみましょう。
※この設定、特にkey-partitionerについては間違っている可能性があります。詳しくは、後半へ

        <distributed-cache name="shardSpecAffinityIndexBookCache"
                           key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
            <indexing index="PRIMARY_OWNER">
                <indexed-entities>
                    <indexed-entity>org.littlewings.infinispan.query.affinityindex.Book</indexed-entity>
                </indexed-entities>

                <property name="hibernate.search.default.directory_provider">infinispan</property>
                <property name="hibernate.search.default.exclusive_index_use">true</property>
                <property name="hibernate.search.default.indexmanager">
                    org.infinispan.query.affinity.AffinityIndexManager
                </property>
                <property name="hibernate.search.default.reader.strategy">shared</property>
                <property name="hibernate.search.default.sharding_strategy.nbr_of_shards">9</property>
                <property name="hibernate.search.lucene_version">LUCENE_CURRENT</property>
            </indexing>
        </distributed-cache>

もちろん、使い方はなんら変わりません。

  test("using affinity-index-manager and shard spec") {
    withCache[String, Book]("shardSpecAffinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val qf = Search.getQueryFactory(cache)
      val query =
        qf.create(s"from ${classOf[Book].getName} b where b.title: (+'検索' or +'構築') order by b.price desc")

      val results = query.list[Book]
      results should have size (3)
      results.get(0).title should be("高速スケーラブル検索エンジン ElasticSearch Server")
      results.get(1).title should be("Java EE 7徹底入門 標準Javaフレームワークによる高信頼性Webシステムの構築")
      results.get(2).title should be("[改訂新版] Apache Solr入門 ~オープンソース全文検索エンジン")
    }
  }

  test("using affinity-index-manager and shard spec, reindex") {
    withCache[String, Book]("shardSpecAffinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val searchManager = Search.getSearchManager(cache)
      searchManager.getMassIndexer.start()
    }
  }

動いてそうな感じですよね?

シャード数を指定しない場合は?

こう書くと、シャード数を指定しない場合はどうなっているのかが気になってきます。

ShardAllocatorManagerというInfinispanのコンポーネントを使うことで、シャードの情報を確認することができます。

一応InfinispanIndexManagerでも動作し、256シャードだと。

  test("indexing local only, number of shards") {
    withCache[String, Book]("bookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val shardAllocatorManager =
        cache.getAdvancedCache.getComponentRegistry.getComponent(classOf[ShardAllocatorManager])
      shardAllocatorManager.getShards should have size (256)
      shardAllocatorManager.getShards should contain only ((0 until 256).map(_.toString): _*)
    }
  }

シャード数を指定しないAffinityIndexManagerでも、256シャードになります。

  test("using affinity-index-manager, number of shards") {
    withCache[String, Book]("affinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val shardAllocatorManager =
        cache.getAdvancedCache.getComponentRegistry.getComponent(classOf[ShardAllocatorManager])
      shardAllocatorManager.getShards should have size (256)
      shardAllocatorManager.getShards should contain only ((0 until 256).map(_.toString): _*)
    }
  }

これは、シャード数が与えられない場合は、InfinispanのSegmentの数がそのままシャード数になるからです。
シャードのIDも、0から255になります。

https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/ShardAllocationManagerImpl.java#L72
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/AffinityShardIdentifierProvider.java#L59-L61

シャード数を指定した場合は、もちろん指定した数になります。今回は9シャードに設定したので、ShardAllocatorManager#getShardsで返ってくる
Setのサイズが9になっています。

  test("using affinity-index-manager and shard spec, number of shards") {
    withCache[String, Book]("shardSpecAffinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val shardAllocatorManager =
        cache.getAdvancedCache.getComponentRegistry.getComponent(classOf[ShardAllocatorManager])
      shardAllocatorManager.getShards should have size (9)
      shardAllocatorManager.getShards should contain only ((0 until 9).map(_.toString): _*)
    }
  }

もう少し中身を見よう

では、表向きはこれくらいにして、もう少し中身を見ていってみましょう。

まずはIndexManagerの実装差。InfinispanIndexManagerとAffinityIndexManagerでだいぶ実装量は違うのですが(AffinityIndexManagerの方が多い)、
注目ポイントとしてはInfinispanIndexManagerはInfinispanDirectoryProviderをひとつしか作らないのに対して、AffinityIndexManagerは
シャードに応じたInfinispanDirectoryProviderを作成しようとします。

https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/indexmanager/InfinispanIndexManager.java#L32-L42
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/AffinityIndexManager.java#L199-L205

また、InfinispanIndexManagerの場合、Remoteのマスターにリクエストを転送しようとするような処理があるのもポイントです。

https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/indexmanager/InfinispanIndexManager.java#L23-L30

続いて、インデックスの更新について。要はPrimary Ownerに関する話題です。

indexingタグのindex属性に指定された値によって、どのようにインデックス更新を判定するかはIndexModificationStrategy列挙型に実装されています。

例えば、LOCAL_ONLYであればisOriginLocalがtrueかつ特定のフラグがオンでなければインデックス更新を行うと判断し、
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/backend/IndexModificationStrategy.java#L50-L58

PRIMARY_OWNERの場合は、clearおよび特定のフラグがオンでない、かつエントリのPrimary Ownerであればインデックス更新を行うと判断します。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/backend/IndexModificationStrategy.java#L63-L72

そして、シャーディングの話。

シャーディングの話題については、Hibernate Searchのシャードの指定と、KeyPartitionerが関連しそうですね。

Hibernate Searchに対してシャードを見せているのは、AffinityShardIdentifierProvider(ShardIdentifierProviderの実装:Hibernate Search)です。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/AffinityShardIdentifierProvider.java

ただ、Infinispan自身もシャードの管理をしており、それがShardAllocationManagerImpl(ShardAllocatorManagerの実装)となります。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/ShardAllocationManagerImpl.java

そして、プロパティによるシャードの指定の有無で、Infinispanの管理するシャードの内部構成が変わります。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/ShardAllocationManagerImpl.java#L45-L47
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/ShardDistributionFactory.java

具体的には、Distributed/Replicated Cacheでシャード数を指定しない場合はPerSegmentShardDistributionとなり、Segmentがそのまま
シャードになります。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/PerSegmentShardDistribution.java
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/PerSegmentShardDistribution.java#L48-L51

シャード数を指定した場合はFixedShardsDistributionとなり、内部的にシャードとSegmentのマッピングを管理するようになります。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/FixedShardsDistribution.java
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/FixedShardsDistribution.java#L122-L125

ひとつのシャードに対して、複数のSegmentがマッピングされるはずですからね。

実際、内部管理しているMapのキーがSegmentで、値がシャードのIDです。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/FixedShardsDistribution.java#L29

Local Cacheの場合は、LocalModeShardDistributionになるようです。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/main/java/org/infinispan/query/affinity/LocalModeShardDistribution.java

最後に、KeyPartitioner。

そもそもこのKeyPartitioner、存在を知らなかったんですけど、Infinispan 8.2から追加されたインターフェースのようです。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/core/src/main/java/org/infinispan/distribution/ch/KeyPartitioner.java

このKeyPartitionerを使うことで、データをどのSegmentに配置するのかをコントロールできるようです。

Grouping APIにも似た印象を受けますが、
The Grouping API

Grouping APIは「どの値でグルーピングするか」を指定するAPIであることに対して、KeyPartitionerは「どのキーをどのSegmentにマッピングするか」を
指定するAPIであるという点が異なります。

デフォルトのPartitinerは、HashFunctionPartitionerです。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/core/src/main/java/org/infinispan/distribution/ch/impl/HashFunctionPartitioner.java

今回、ドキュメントに例として書かれているAffinityPartitionerは、キーがAffinityTaggedKeyインターフェースを実装している
場合に、AffinityTaggedKey#getAffinitySegmentIdで返す値をSegmentとして使います。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/core/src/main/java/org/infinispan/distribution/ch/impl/AffinityPartitioner.java

なお、InfinispanでAffinityTaggedKeyを実装しているクラスは、以下となります。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/lucene-directory/src/main/java/org/infinispan/lucene/ChunkCacheKey.java
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/lucene-directory/src/main/java/org/infinispan/lucene/FileCacheKey.java
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/lucene-directory/src/main/java/org/infinispan/lucene/FileListCacheKey.java
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/lucene-directory/src/main/java/org/infinispan/lucene/FileReadLockKey.java

Apache LuceneのDirectoryを実装するにあたり、InfinispanがCacheのキーとして実装しているクラスです。

…と、ここまで書いて、AffinityPartitionerを指定する箇所が変なのでは?と思うわけです。

KeyPartitionerを本来指定すべきなのは、Apache LuceneのDirectory実装のために使っているCacheなのでは?という気がします。

デフォルトでは、これらのCacheはInfinispan/Hibernate Searchが実行時にデフォルト設定として定義します。設定は、こちら。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/directory-provider/src/main/resources/default-hibernatesearch-infinispan.xml

また、ChunkCacheKeyなどをtoStringすると、どのAffinitySegmentIdを持っているかわかるようです。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/lucene/lucene-directory/src/main/java/org/infinispan/lucene/ChunkCacheKey.java#L121

ちょっと確認してみましょう。

こんな感じで、各インデックス用のCacheに対して、キーとSegment、シャードIDを出力するコードを用意。

シャード指定なし。

  test("using affinity-index-manager, print shards and segments") {
    withCache[String, Book]("affinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val shardAllocatorManager =
        cache.getAdvancedCache.getComponentRegistry.getComponent(classOf[ShardAllocatorManager])
      val cacheManager = cache.getCacheManager

      println("===== LuceneIndexesLocking =====")
      val luceneIndexesLockingCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesLocking")
      luceneIndexesLockingCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesLockingCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
      println("===== LuceneIndexesMetadata =====")
      val luceneIndexesMetadataCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesMetadata")
      luceneIndexesMetadataCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesMetadataCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
      println("===== LuceneIndexesData =====")
      val luceneIndexesDataCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesData")
      luceneIndexesDataCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesDataCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
    }
  }

シャード指定あり。

  test("using affinity-index-manager and shard spec, print shards and segments") {
    withCache[String, Book]("shardSpecAffinityIndexBookCache", 3) { cache =>
      books.foreach(b => cache.put(b.isbn, b))

      val shardAllocatorManager =
        cache.getAdvancedCache.getComponentRegistry.getComponent(classOf[ShardAllocatorManager])
      val cacheManager = cache.getCacheManager

      println("===== LuceneIndexesLocking =====")
      val luceneIndexesLockingCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesLockingCustom")
      luceneIndexesLockingCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesLockingCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
      println("===== LuceneIndexesMetadata =====")
      val luceneIndexesMetadataCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesMetadataCustom")
      luceneIndexesMetadataCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesMetadataCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
      println("===== LuceneIndexesData =====")
      val luceneIndexesDataCache = cacheManager.getCache[AnyRef, AnyRef]("LuceneIndexesDataCustom")
      luceneIndexesDataCache
        .forEach { case (k, v) =>
          val cacheTopology = luceneIndexesDataCache.getAdvancedCache.getDistributionManager.getCacheTopology
          val shard = shardAllocatorManager.getShardFromKey(k)
          println(s"${k}, segment = ${cacheTopology.getSegment(k)}, shard = ${shard}")
        }
    }
  }

結果。

シャード数指定なし。

===== LuceneIndexesLocking =====
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 119, shard = 119
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 238, shard = 238
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 134, shard = 134
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 14, shard = 14
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 62, shard = 62
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 109, shard = 109
===== LuceneIndexesMetadata =====
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 137, shard = 137
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 37, shard = 37
M|_0.nvm|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 226, shard = 226
M|_0.fnm|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 22, shard = 22
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 138, shard = 138
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 146, shard = 146
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 205, shard = 205
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 142, shard = 142
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 162, shard = 162
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 250, shard = 250
M|_0.fdx|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 228, shard = 228
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 80, shard = 80
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 160, shard = 160
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 126, shard = 126
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 141, shard = 141
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 98, shard = 98
M|segments_1|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 6, shard = 6
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 242, shard = 242
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 147, shard = 147
*|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 232, shard = 232
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 121, shard = 121
*|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 109, shard = 109
*|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 95, shard = 95
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 215, shard = 215
*|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 168, shard = 168
M|_0.fdt|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 119, shard = 119
*|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 35, shard = 35
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 191, shard = 191
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 121, shard = 121
M|_0_Lucene50_0.tip|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 57, shard = 57
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 235, shard = 235
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 60, shard = 60
*|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 150, shard = 150
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 148, shard = 148
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 41, shard = 41
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 38, shard = 38
===== LuceneIndexesData =====
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 144, shard = 144
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 144, shard = 144
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 144, shard = 144
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 144, shard = 144
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 144, shard = 144
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 144, shard = 144
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 241, shard = 241
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 241, shard = 241
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 241, shard = 241
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 241, shard = 241
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 241, shard = 241
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 241, shard = 241
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 189, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 189, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 189, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 189, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 189, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 189, shard = 189
C|segments_1|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 239, shard = 239
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 146, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 146, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 146, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 146, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 146, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 146, shard = 146

シャード数指定あり。

===== LuceneIndexesLocking =====
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 114, shard = 1
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 97, shard = 7
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 191, shard = 2
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 248, shard = 6
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 11, shard = 6
===== LuceneIndexesMetadata =====
M|_0.fnm|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 223, shard = 5
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 123, shard = 6
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 36, shard = 6
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 62, shard = 0
*|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 125, shard = 7
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 234, shard = 0
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 171, shard = 7
*|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 191, shard = 2
*|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 121, shard = 1
*|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 247, shard = 3
*|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 46, shard = 1
M|_1.cfe|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 5, shard = 3
M|segments_3|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 240, shard = 2
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 250, shard = 1
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 30, shard = 6
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 36, shard = 6
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 85, shard = 5
M|_1.cfs|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 194, shard = 7
M|segments_1|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 252, shard = 3
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 143, shard = 3
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 210, shard = 3
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 162, shard = 0
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 102, shard = 6
M|_0.cfs|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 67, shard = 4
M|_1.si|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 230, shard = 8
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 208, shard = 4
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 71, shard = 8
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 199, shard = 6
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 249, shard = 4
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 252, shard = 3
===== LuceneIndexesData =====
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 146, shard = 6
C|segments_1|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 239, shard = 8
C|_1.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 251, shard = 5
C|segments_3|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 143, shard = 3
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 144, shard = 4
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 144, shard = 4
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 144, shard = 4
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 144, shard = 4
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 144, shard = 4
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 189, shard = 1
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 189, shard = 1
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 189, shard = 1
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 189, shard = 1
C|_1.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 44, shard = 2
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 241, shard = 0
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 241, shard = 0
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 241, shard = 0
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 241, shard = 0
C|_0.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.6|6, segment = 241, shard = 0
C|_1.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 57, shard = 1

シャード数の指定は反映できている気がしますが、よくよく見るとAffinitySegmentIdと実際のSegmentの値がずれています。

===== LuceneIndexesData =====
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.1|1, segment = 146, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 146, shard = 6

例えば、以下だとAffinitySegmentIdは7なのに、キーから引いたSegmentは146ということになっています。

C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 146, shard = 6

KeyPartitionerを使ったんなら、これが一緒にならないといけないのではないでしょうか?

なお、AffinityIndexManagerを使わなかった場合は、AffinitySegmentIdが「-1」になります。

===== LuceneIndexesLocking =====
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 109, shard = 109
===== LuceneIndexesMetadata =====
M|_5.cfs|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 142, shard = 142
M|_4.cfs|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 99, shard = 99
M|_1.si|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 63, shard = 63
M|_3.cfs|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 177, shard = 177
〜省略〜
===== LuceneIndexesData =====
C|_4.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 117, shard = 117
C|_5.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 86, shard = 86
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 146, shard = 146
C|_2.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 98, shard = 98
C|_2.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book|-1, segment = 4, shard = 4
〜省略〜

ということは、KeyPartitionerは本体のCacheには不要で、Apache Luceneの関連のCacheに設定するのが正しそうです。

AffinityIndexManagerを適用したCacheの設定から、key-partitionerを外します。

        <!--
        <distributed-cache name="affinityIndexBookCache"
                           key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
        -->
        <distributed-cache name="affinityIndexBookCache">
            <!-- 省略 -->
        </distributed-cache>
        <!--
        <distributed-cache name="shardSpecAffinityIndexBookCache"
                           key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
        -->
        <distributed-cache name="shardSpecAffinityIndexBookCache">
            <!-- 省略 -->
        </distributed-cache>

そして、設定ファイル内に、Apache Lucene関連のCacheをkey-partitioner付きで再定義します。

        <!-- *************************************** -->
        <!--  Cache to store Lucene's file metadata  -->
        <!-- *************************************** -->
        <replicated-cache name="LuceneIndexesMetadata"
                          mode="SYNC"
                          remote-timeout="25000"
                          key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
            <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/>
            <transaction mode="NONE"/>
            <expiration max-idle="-1"/>
            <indexing index="NONE"/>
            <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/>
        </replicated-cache>

        <!-- **************************** -->
        <!--  Cache to store Lucene data  -->
        <!-- **************************** -->
        <distributed-cache name="LuceneIndexesData"
                           mode="SYNC"
                           remote-timeout="25000"
                           key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
            <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/>
            <transaction mode="NONE"/>
            <expiration max-idle="-1"/>
            <indexing index="NONE"/>
            <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/>
        </distributed-cache>

        <!-- ***************************** -->
        <!--  Cache to store Lucene locks  -->
        <!-- ***************************** -->
        <replicated-cache name="LuceneIndexesLocking"
                          mode="SYNC"
                          remote-timeout="25000"
                          key-partitioner="org.infinispan.distribution.ch.impl.AffinityPartitioner">
            <locking striping="false" acquire-timeout="10000" concurrency-level="500" write-skew="false"/>
            <transaction mode="NONE"/>
            <expiration max-idle="-1"/>
            <indexing index="NONE"/>
            <state-transfer enabled="true" timeout="480000" await-initial-transfer="true"/>
        </replicated-cache>

なお、今回はデフォルトのCache名をそのまま使っていますが、こんな感じで自分の好きなCache名を付けることもできます。
あとは、この名前を付けたCacheを定義すればOKです。

                <property name="hibernate.search.default.locking_cachename">LuceneIndexesLockingCustom</property>
                <property name="hibernate.search.default.data_cachename">LuceneIndexesDataCustom</property>
                <property name="hibernate.search.default.metadata_cachename">LuceneIndexesMetadataCustom</property>

ドキュメント上のサンプルは、こちら。
InfinispanIndexManager

で、この状態で再度実行するとどうなるかですが…。

シャード数指定なしの場合。

===== LuceneIndexesLocking =====
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 48, shard = 119
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 238
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 36, shard = 134
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 171, shard = 14
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.149|149, segment = 149, shard = 62
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.144|144, segment = 144, shard = 109
===== LuceneIndexesMetadata =====
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.48|48, segment = 48, shard = 137
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 37
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 138
M|segments_2|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 146
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 36, shard = 205
M|_0.cfe|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 36, shard = 142
〜省略〜
===== LuceneIndexesData =====
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 171, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 146
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 36, shard = 146
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 171, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.42|42, segment = 42, shard = 189
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.36|36, segment = 36, shard = 189
C|_0.cfe|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.171|171, segment = 171, shard = 144
〜省略〜

シャード数指定ありの場合。

===== LuceneIndexesLocking =====
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 0, shard = 1
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 4, shard = 3
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 7, shard = 0
M|write.lock|org.littlewings.infinispan.query.affinityindex.Book.8|8, segment = 8, shard = 6
===== LuceneIndexesMetadata =====
M|_0.fnm|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 0, shard = 2
M|_0.nvm|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 0, shard = 8
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 4, shard = 7
M|_0.fdt|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 0, shard = 7
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 7, shard = 4
M|_0.si|org.littlewings.infinispan.query.affinityindex.Book.8|8, segment = 8, shard = 8
*|org.littlewings.infinispan.query.affinityindex.Book.0|0, segment = 0, shard = 4
〜省略〜
===== LuceneIndexesData =====
C|_1.si|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 7, shard = 1
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.7|7, segment = 7, shard = 6
C|_0.cfs|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 4, shard = 6
C|segments_2|0|1048576|org.littlewings.infinispan.query.affinityindex.Book.4|4, segment = 4, shard = 5
〜省略〜

今度は、AffinitySegmentIdとSegmentがキレイに揃いました。

InfinispanのAffinityIndexのテストでも、確認しているのはデータ本体のCacheではなくてApache Luceneのインデックスの方の
Cacheなので、こちらの設定が正解なのでは?と思うのですが、どうなのでしょう…。
https://github.com/infinispan/infinispan/blob/9.0.0.Final/query/src/test/java/org/infinispan/query/affinity/AffinityTest.java#L107-L117

でも、もし正解だとすると本体のテストも微妙な感じが…。

まとめ

Infinispan 9.0.0.Finalで追加された、AffinityIndexManagerを試してみました。

Infinispan/Hibernate SearchとApache Luceneのシャードから、KeyPartitionerまでいろいろ追うことができて、けっこう楽しかったです。
ですが、Affinityしたい内容ってたぶんApache Luceneのインデックスの方なので、今のドキュメントの書き方ってどうなのかなぁって
思ったり。

今回作成したコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/embedded-query-affinity-index