CLOVER🍀

That was when it all began.

Infinispan 14.0のRESPエンドポイントをRedis CLIで試す

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

Infinispan 14.0で「RESP endpoint」というものが実装され、Redis互換のエンドポイントをサポートするようになったというのでちょっと
試してみました。

Infinispan 14.0.0.Final

なお、このブログには書かれていませんが、現時点でRESPエンドポイントは「実験的モジュール」扱いです。

RESPエンドポイント

RESPエンドポイントに関するドキュメントは、こちら。

Using the RESP protocol endpoint with Infinispan

以下のようなドキュメントの一覧ページには載っていないのですが、

Guides

Infinispan 14.0 documentation index

各ドキュメント内の「Document Index」の中から「RESP protocol endpoint」を選択すると見ることができます。

ドキュメントによると、RESPエンドポイントはRESP3を実装した実験的モジュールのようです。RESPエンドポイントを使うと、
Infinispan ServerをバックエンドとするRESPサーバーに対してRedisクライアントから接続し、Cache操作を行うことができます。

Infinispan Server includes an experimental module that implements the RESP3 protocol. The RESP endpoint allows Redis clients to connect to one or more one or several Infinispan-backed RESP servers and perform cache operations.

Using the RESP endpoint

RESPエンドポイントのモジュールは、こちら。

https://github.com/infinispan/infinispan/tree/14.0.0.Final/server/resp

RESP3自体については、RedisのGitHubリポジトリに記載があります。

https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md

RESPというのは、「Redis Serialization Protocol」の略のようです。

Redis Serialization Protocol (RESP)

https://github.com/redis/redis-specifications/blob/master/protocol/README.md

RESPとは、クライアントとサーバーの間のリクエスト、レスポンスをどのように扱うかを設計したプロトコルです。クライアントがなんらかの
リクエストを実行し、サーバーがなんらかのデータを返すようなものです。

The protocol is designed to handle request-response chats between clients and servers, where the client performs some kind of request, and the server replies with some data.

RESP3 overview

RESP3では、以下のデータ型を扱えます。

  • 配列
  • Blob
  • 文字列
  • エラー
  • Number
  • Null
  • Double
  • Boolean
  • Blobエラー
  • Verbatim string
  • Map
  • Set
  • Attribute
  • Push
  • Hello

RESP3 types

Infinispan ServerのRESP3エンドポイントでサポートされているコマンドは、以下になります。

  • AUTH
  • DECR
  • DEL
  • ECHO
  • GET
  • HELLO
  • INCR
  • MGET
  • MSET
  • PING
  • PUBLISH
  • QUIT
  • RESET
  • SET
  • SUBSCRIBE
  • UNSUBSCRIBE

Using the RESP protocol endpoint with Infinispan / Redis commands

Redis本家と比べると、まだまだ少ないですね。

Commands | Redis

InfinispanとRedis

RESPエンドポイントに対するチケットはこちらです。

[ISPN-12439] Add server endpoint that implements RESP protocol from Redis - Red Hat Issue Tracker

このチケットにはInfinispan ServerにRESPを使ってRedisクライアントから接続できれば便利だ、と書かれているのですが。

It could be beneficial to have a server endpoint that we can enable to allow a Redis client to connect to Infinispan Server using the RESP protocol to perform cache operations.

Redisとして使えた方が、Infinispan Serverのユースケースが増えるんですかね…?
Redisの代替として使いたいというリクエストが多かったのでしょうか?

個人的には「今、この機能を追加するんだ…?」と思ったので、もう少し背景が知りたかったですね。

それはさておき、ちょっと試してみるとしましょう。

環境

今回の環境は、こちら。

$ java --version
openjdk 17.0.4.1 2022-08-12
OpenJDK Runtime Environment Temurin-17.0.4.1+1 (build 17.0.4.1+1)
OpenJDK 64-Bit Server VM Temurin-17.0.4.1+1 (build 17.0.4.1+1, mixed mode, sharing)


$ bin/server.sh --version

Infinispan Server 14.0.0.Final (Flying Saucer)
Copyright (C) Red Hat Inc. and/or its affiliates and other contributors
License Apache License, v. 2.0. http://www.apache.org/licenses/LICENSE-2.0

Infinispan Serverは、172.17.0.2で動作しているものとします。

起動は、以下のコマンドで行っておきます。

$ bin/server.sh \
    -b 0.0.0.0 \
    -Djgroups.tcp.address=`hostname -i`

RedisはCLIのみ使います。

$ bin/redis-cli --version
redis-cli 7.0.5

RESPエンドポイントにRedis CLIから接続する

では、RESPエンドポイントにRedis CLIから接続してみましょう。

なお、Infinispan Serverを起動すると気づきますが、RESPエンドポイントはデフォルトで有効になっているようです。

2022-10-02 14:44:12,868 INFO  (ForkJoinPool.commonPool-worker-1) [org.infinispan.SERVER] ISPN080018: Started connector Resp (internal)

デフォルトの状態では、認証情報なしで接続しようとするとエラーになります。

$ bin/redis-cli -h 172.17.0.2 -p 11222
172.17.0.2:11222> set key1 value1
(error) WRONGPASS invalid username-password pair or user is disabled.

そこで、まずはInfinispan Serverにユーザーを追加します。今回は、管理ユーザーとアプリケーションユーザーを追加しました。

$ bin/cli.sh user create -g admin -p password ispn-admin
$ bin/cli.sh user create -g application -p password ispn-user

アプリケーションユーザーのユーザー名、パスワードを使って、もう1度Redis CLIから接続。

$ bin/redis-cli -h 172.17.0.2 -p 11222 --user ispn-user --pass password
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

今度は接続できたので、setおよびgetコマンドで確認。

172.17.0.2:11222> set key1 value1
OK

172.17.0.2:11222> get key1
"value1"

すごく、あっさり動きました…。

このデータが永続化されるかどうかなどは、RedisのふりをしているInfinispan ServerのCacheの設定次第ということになるわけですが。

RESPエンドポイントが使用するCacheについて

こうあっさり動いてしまうと、いろいろ気になります。まず、データはどのCacheに保存されているのでしょうか?

Infinispanの管理CLIでログイン。

$ bin/cli.sh -c-
Username: ispn-admin
Password:

Cacheを見ると、respCacheという見慣れないCacheがあります。

[c6b2176b23ba-18766@cluster//containers/default]> ls caches
___script_cache
respCache

データを見てみます。

[c6b2176b23ba-18766@cluster//containers/default]> ls caches/respCache
Key
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
key1


[c6b2176b23ba-18766@cluster//containers/default]> cache respCache
[c6b2176b23ba-18766@cluster//containers/default]> get key1
value1

確かに、REPSエンドポイントで使われているCacheのようです。

Cacheの種類は、Replicated Cacheでした。

describe caches/respCache
{
  "respCache" : {
    "replicated-cache" : {
      "mode" : "SYNC",
      "encoding" : {
        "key" : {
          "media-type" : "text/plain"
        },
        "value" : {
          "media-type" : "application/octet-stream"
        }
      }
    }
  }
}

このCache名は、RESPエンドポイントのドキュメントには記載がありませんでしたが、XML Schemaの方には記載がありました。

Names the cache that the RESP connector exposes. The default cache name is respCache.

urn:infinispan:server:14.0

respCacheというのは、RESPエンドポイントが使うデフォルトのCache名のようです。

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/configuration/RespServerConfiguration.java#L23

このReplicated Cacheは、起動時に作成するようです。

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/RespServer.java#L43-L56

そして、クラスタリングが有効になっている場合はReplicated Cacheに設定されます。

            if (cacheManager.getCacheManagerConfiguration().isClustered()) { // We are running in clustered mode
               builder.clustering().cacheMode(CacheMode.REPL_SYNC);
            }

メディアタイプは、明示的に指定されていますね。キーはtext/plain、値はapplication/octet-streamですね。

            builder.encoding().key().mediaType(MediaType.TEXT_PLAIN_TYPE);
            builder.encoding().value().mediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE);

RESPエンドポイントの設定を変えてみる

ドキュメントを見ていると、resp-connectorcache属性で使用するCacheを設定できそうです。

Using the RESP protocol endpoint with Infinispan / Enabling the RESP endpoint

といっても、起動時にCacheを作成してしまうのでした。

実際、デフォルトのinfinispan.xmlにはRESPエンドポイント用のCacheの定義はありません。

   <cache-container name="default" statistics="true">
      <transport cluster="${infinispan.cluster.name:cluster}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>
      <security>
         <authorization/>
      </security>
   </cache-container>

なんなら、エンドポイントの定義もなにもないわけですが。

      <endpoints socket-binding="default" security-realm="default" />

ということは、Cacheを明示的に定義する場合はInfinispan Serverの起動前に行う必要がありますね。

まずは、ドキュメントに習ってRESPエンドポイントを設定してみます。myRespCacheというCacheを使うように設定してみました。

      <endpoints socket-binding="default" security-realm="default">
        <endpoint>
          <resp-connector cache="myRespCache">
            <authentication/>
          </resp-connector>
          <hotrod-connector>
            <authentication>
              <sasl mechanisms="SCRAM-SHA-512 SCRAM-SHA-384 SCRAM-SHA-256
                                SCRAM-SHA-1 DIGEST-SHA-512 DIGEST-SHA-384
                                DIGEST-SHA-256 DIGEST-SHA DIGEST-MD5 PLAIN"
                    server-name="infinispan"
                    qop="auth"/>
            </authentication>
          </hotrod-connector>
          <rest-connector>
            <authentication mechanisms="DIGEST BASIC"/>
          </rest-connector>
        </endpoint>
      </endpoints>

Hot RodエンドポイントとRESTエンドポイントは明示的に設定が必要です。

認証が有効になっているInfinispan Serverでは、Hot RodエンドポイントまたはRESTエンドポイントの少なくともどちらかは認証の設定が
行われている必要があるようです。

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/runtime/src/main/java/org/infinispan/server/configuration/endpoint/EndpointsConfigurationBuilder.java#L77-L86

単にRESPエンドポイントの設定だけすれば良いと思って、以下のような状態にしてしまうと

      <endpoints socket-binding="default" security-realm="default">
        <endpoint>
          <resp-connector cache="myRespCache">
            <authentication/>
          </resp-connector>
        </endpoint>
      </endpoints>

Infinispan Serverが起動に失敗します。

2022-10-02 16:09:28,754 FATAL (main) [org.infinispan.SERVER] ISPN080028: Infinispan Server failed to start org.infinispan.commons.CacheConfigurationException: ISPN080070: The cache container requires authorization, but none of the connectors enable authentication

endpointを定義したら、Hot RodエンドポイントもRESTエンドポイントも明示的に設定しなければいけません。

気を取り直して、Infinispan Serverを再起動します。

すると、RESPエンドポイントに指定した名前でCacheが作成されていました。

Redis CLIでデータを保存したりするとこのCacheに格納されますが、確認は省略。

次に、infinispan.xmlに明示的にCacheを定義して、そちらを使うようにしてみましょう。

今回はDistributed Cacheを定義。

   <cache-container name="default" statistics="true">
      <transport cluster="${infinispan.cluster.name:cluster}" stack="${infinispan.cluster.stack:tcp}" node-name="${infinispan.node.name:}"/>

      <distributed-cache name="distributedRespCache">
        <encoding>
          <key media-type="text/plain"/>
          <value media-type="application/octet-stream"/>
        </encoding>
      </distributed-cache>

      <security>
         <authorization/>
      </security>
   </cache-container>

RESPエンドポイントは、distributedRespCacheを参照するように設定。

      <endpoints socket-binding="default" security-realm="default">
        <endpoint>
          <resp-connector cache="distributedRespCache">
            <authentication/>
          </resp-connector>
          <hotrod-connector>
            <authentication>
              <sasl mechanisms="SCRAM-SHA-512 SCRAM-SHA-384 SCRAM-SHA-256
                                SCRAM-SHA-1 DIGEST-SHA-512 DIGEST-SHA-384
                                DIGEST-SHA-256 DIGEST-SHA DIGEST-MD5 PLAIN"
                    server-name="infinispan"
                    qop="auth"/>
            </authentication>
          </hotrod-connector>
          <rest-connector>
            <authentication mechanisms="DIGEST BASIC"/>
          </rest-connector>
        </endpoint>
      </endpoints>

これで、Infinispan Serverを再起動。

定義したDistributed Cacheが現れます。

[3f666276f239-57422@cluster//containers/default]> ls caches
distributedRespCache
___script_cache

定義の確認。

[3f666276f239-57422@cluster//containers/default]> describe caches/distributedRespCache
{
  "distributedRespCache" : {
    "distributed-cache" : {
      "mode" : "SYNC",
      "remote-timeout" : "17500",
      "statistics" : true,
      "encoding" : {
        "key" : {
          "media-type" : "text/plain"
        },
        "value" : {
          "media-type" : "application/octet-stream"
        }
      },
      "locking" : {
        "concurrency-level" : "1000",
        "acquire-timeout" : "15000",
        "striping" : false
      },
      "state-transfer" : {
        "timeout" : "60000"
      }
    }
  }
}

Redis CLIから、再度データを保存してみましょう。

172.17.0.2:11222> set key1 value1
OK
172.17.0.2:11222> get key1
"value1"

Infinispan側からも確認してみます。

[3f666276f239-57422@cluster//containers/default]> ls caches/distributedRespCache
Key
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
key1
[3f666276f239-57422@cluster//containers/default]> cache distributedRespCache
[3f666276f239-57422@cluster//containers/default/caches/distributedRespCache]> get key1
value1

OKですね。

オマケ

RESPエンドポイントで実装されているコマンドを確認するは、このあたりを見るとよさそうです。

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/Resp3AuthHandler.java

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/Resp3Handler.java

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/SubscriberHandler.java

また、内部的にはLettuceも使われていたりして、ちょっと面白いです。

https://github.com/infinispan/infinispan/blob/14.0.0.Final/server/resp/src/main/java/org/infinispan/server/resp/RespLettuceHandler.java

まとめ

Infinispan 14.0で追加された、RESPエンドポイントを試してみました。

割と簡単に使えるのですが、ちょっと動きを変えようとすると癖がある…というかドキュメントがあまり書かれていない感じがするので
ちょっと注意ですね。

個人的には、この機能の使いどころが気になるところですが…実験的モジュールのようなので、もう少し様子をみましょうか。