これは、なにをしたくて書いたもの?
前に、NettyのincubatorプロジェクトであるNetty io_uringを試してみるエントリーを書きました。
TCP Echo Server/Clientを書いて、Netty io_uringを試してみる - CLOVER🍀
Infinispanでも14.0.0.Finalから、Netty io_uringを使えるようになっているということなので、試してみたいと思います。
io_uringについて
io_uringは、Linuxカーネル5.1から導入された、非同期IO用のAPIです。こちらにも、少し書いておきました。
TCP Echo Server/Clientを書いて、Netty io_uringを試してみる - CLOVER🍀
Infinispan 14.0とNetty io_uring
Infinispan 14.0.0.Finalのリリース時のブログエントリーを見てみると、Infinispan Serverで実験的にio_uringをサポートしたことが
書かれています。
Server
- Experimental IO_Uring support
情報は、例によってこれだけです。どうやったらio_uringを使うようになるかですら、情報がありません。
とりあえず、Infinispan Serverをダウンロードしてみましょう。
$ curl -LO https://downloads.jboss.org/infinispan/14.0.6.Final/infinispan-server-14.0.6.Final.zip $ unzip infinispan-server-14.0.6.Final.zip $ cd infinispan-server-14.0.6.Final
lib
ディレクトリには、Netty io_uringのライブラリがありました。
$ ll lib/*io_uring* -rw-r--r-- 1 xxxxx xxxxx 99211 1月 19 03:23 lib/netty-incubator-transport-classes-io_uring-0.0.14.Final.jar -rw-r--r-- 1 xxxxx xxxxx 5563 1月 19 03:23 lib/netty-incubator-transport-native-io_uring-0.0.14.Final.jar -rw-r--r-- 1 xxxxx xxxxx 37477 1月 19 03:23 lib/netty-incubator-transport-native-io_uring-linux-aarch_64-0.0.14.Final.jar -rw-r--r-- 1 xxxxx xxxxx 36251 1月 19 03:23 lib/netty-incubator-transport-native-io_uring-linux-x86_64-0.0.14.Final.jar
というわけで、Infinispan ServerでもNetty io_uringを使うようですね。
なにも考えずに、Infinispan Serverを起動してみます。
$ bin/server.sh
すると、ログの中にこんな表記が混じっていることに気づきます。
2023-02-05 18:24:12,669 INFO (main) [org.infinispan.SERVER] Using transport: Epoll
デフォルトではepollを使うようなので、これをio_uringに変更してみるのが今回のお題です。
環境
今回の環境は、こちら。
Ubuntu Linux 22.04 LTSで、カーネルは5.15です。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.1 LTS Release: 22.04 Codename: jammy $ uname -srmvpio Linux 5.15.0-58-generic #64-Ubuntu SMP Thu Jan 5 11:43:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
Java。
$ java --version openjdk 17.0.5 2022-10-18 OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04) OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing)
Infinispan ServerでNetty io_uringを使う
Infinispan Serverのソースコードを見ていると、以下の3つの条件を満たしているとNetty io_uringを使ってくれるようですね。
- epollを使わないようにしている(システムプロパティ
infinispan.server.channel.epoll
をfalse
にする) - システムプロパティ
infinispan.server.channel.iouring
がtrue
になっている(デフォルト値) - 実行環境(OS)がLinuxである
io.netty.incubator.channel.uring.IOUring
クラスが、クラスパス上に存在する- Netty io_uringがio_uringを使える状態だと認識している
優先順位を見てみると、EpollServerSocketChannel
→ IOUringServerSocketChannel
→ NioServerSocketChannel
となっているので、
epollを無効にしないとio_uringが選ばれないことになります。
これらの条件を満たしていると、IOUringEventLoopGroup
やIOUringServerSocketChannel
を使ってくれることになります。
というわけで、システムプロパティinfinispan.server.channel.epoll
をfalse
にして、epollを使わないようにして起動してみましょう。
$ bin/server.sh -Dinfinispan.server.channel.epoll=false
すると、ログから以下のようにNetty io_uringを使うように変わったことがわかります。
2023-02-05 18:38:35,690 INFO (main) [org.infinispan.SERVER] Using transport: IOUring
確認のために、strace
でシステムコールを見てみましょう。
$ strace -f -tt bin/server.sh -Dinfinispan.server.channel.epoll=false 2>&1 | grep io_uring
見てみると、io_uringのシステムコールを使っていることが確認できます。
[pid 7926] 18:44:13.963512 io_uring_setup(4096, {flags=0, sq_thread_cpu=0, sq_thread_idle=0, sq_entries=4096, cq_entries=8192, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS, sq_off={head=0, tail=64, ring_mask=256, ring_entries=264, flags=276, dropped=272, array=131392}, cq_off={head=128, tail=192, ring_mask=260, ring_entries=268, overflow=284, cqes=320, flags=280}}) = 185 [pid 7926] 18:44:13.970822 io_uring_register(185, IORING_REGISTER_PROBE, {last_op=IORING_OP_LINKAT, ops_len=40, ops=[{op=IORING_OP_NOP, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_READV, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_WRITEV, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_FSYNC, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_READ_FIXED, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_WRITE_FIXED, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_POLL_ADD, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_POLL_REMOVE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_SYNC_FILE_RANGE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_SENDMSG, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_RECVMSG, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_TIMEOUT, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_TIMEOUT_REMOVE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_ACCEPT, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_ASYNC_CANCEL, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_LINK_TIMEOUT, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_CONNECT, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_FALLOCATE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_OPENAT, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_CLOSE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_FILES_UPDATE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_STATX, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_READ, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_WRITE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_FADVISE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_MADVISE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_SEND, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_RECV, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_OPENAT2, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_EPOLL_CTL, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_SPLICE, flags=IO_URING_OP_SUPPORTED}, {op=IORING_OP_PROVIDE_BUFFERS, flags=IO_URING_OP_SUPPORTED}, ...]}, 256) = 0 [pid 7926] 18:44:14.041339 io_uring_setup(4096, {flags=0, sq_thread_cpu=0, sq_thread_idle=0, sq_entries=4096, cq_entries=8192, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS, sq_off={head=0, tail=64, ring_mask=256, ring_entries=264, flags=276, dropped=272, array=131392}, cq_off={head=128, tail=192, ring_mask=260, ring_entries=268, overflow=284, cqes=320, flags=280}}) = 185 [pid 7926] 18:44:14.042557 io_uring_setup(4096, {flags=0, sq_thread_cpu=0, sq_thread_idle=0 <unfinished ...> [pid 7926] 18:44:14.043615 <... io_uring_setup resumed>, sq_entries=4096, cq_entries=8192, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS, sq_off={head=0, tail=64, ring_mask=256, ring_entries=264, flags=276, dropped=272, array=131392}, cq_off={head=128, tail=192, ring_mask=260, ring_entries=268, overflow=284, cqes=320, flags=280}}) = 188 [pid 7926] 18:44:14.044349 io_uring_setup(4096, {flags=0, sq_thread_cpu=0, sq_thread_idle=0, sq_entries=4096, cq_entries=8192, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS, sq_off={head=0, tail=64, ring_mask=256, ring_entries=264, flags=276, dropped=272, array=131392}, cq_off={head=128, tail=192, ring_mask=260, ring_entries=268, overflow=284, cqes=320, flags=280}}) = 189 [pid 7926] 18:44:14.045776 io_uring_setup(4096, {flags=0, sq_thread_cpu=0, sq_thread_idle=0, sq_entries=4096, cq_entries=8192, features=IORING_FEAT_SINGLE_MMAP|IORING_FEAT_NODROP|IORING_FEAT_SUBMIT_STABLE|IORING_FEAT_RW_CUR_POS|IORING_FEAT_CUR_PERSONALITY|IORING_FEAT_FAST_POLL|IORING_FEAT_POLL_32BITS|IORING_FEAT_SQPOLL_NONFIXED|IORING_FEAT_EXT_ARG|IORING_FEAT_NATIVE_WORKERS|IORING_FEAT_RSRC_TAGS, sq_off={head=0, tail=64, ring_mask=256, ring_entries=264, flags=276, dropped=272, array=131392}, cq_off={head=128, tail=192, ring_mask=260, ring_entries=268, overflow=284, cqes=320, flags=280}}) = 191 [pid 7957] 18:44:18.154084 io_uring_enter(185, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7958] 18:44:18.164140 io_uring_enter(188, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7959] 18:44:18.443682 io_uring_enter(189, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7961] 18:44:18.445582 io_uring_enter(191, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7957] 18:44:19.048567 <... io_uring_enter resumed>) = 1 [pid 7957] 18:44:19.048858 io_uring_enter(185, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7958] 18:44:19.156755 <... io_uring_enter resumed>) = 1 [pid 7958] 18:44:19.157795 io_uring_enter(188, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7959] 18:44:19.160497 <... io_uring_enter resumed>) = 1 [pid 7959] 18:44:19.160678 io_uring_enter(189, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...> [pid 7961] 18:44:19.391462 <... io_uring_enter resumed>) = 1 [pid 7957] 18:44:19.464994 <... io_uring_enter resumed>) = 1 [pid 7957] 18:44:19.465651 io_uring_enter(185, 1, 1, IORING_ENTER_GETEVENTS, NULL, 139637976727560 <unfinished ...>
ちなみに、興味本位ですが、AlmaLinux 8のような、カーネルが5.1よりも低いLinuxで試すとこうなりました。
$ uname -srvmpio Linux 4.18.0-425.3.1.el8.x86_64 #1 SMP Tue Nov 8 14:08:25 EST 2022 x86_64 x86_64 x86_64 GNU/Linux $ bin/server.sh -Dinfinispan.server.channel.epoll=false
NioServerSocketChannel
が選ばれています。
2023-02-05 18:53:47,839 INFO (main) [org.infinispan.SERVER] Using transport: NIO
これは、io_uringが使える環境ではないとNetty io_uringが判定するからですね。
Hot Rod Clientは?
ところで、Infinispanのリポジトリをio_uringで調べていると、どうもInfinispan Server以外にもNetty io_uringを使っていると思われる箇所が
見つかります。
Hot Rod Clientですね。
というわけで、こちらも試してみましょう。
使用するMavenのバージョン。
$ mvn --version Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.5, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-58-generic", arch: "amd64", family: "unix"
Infinispan Serverは、172.18.0.2〜172.18.0.4で3ノード起動させ、クラスターを構成しておくことにします。
起動コマンドは、こちら。
$ bin/server.sh \ -b 0.0.0.0 \ -Djgroups.tcp.address=$(hostname -i) \ -Dinfinispan.server.channel.epoll=false
各Infinispan Serverには、管理用ユーザー、アプリケーション用ユーザーをそれぞれ作成しておきます。
$ bin/cli.sh user create -g admin -p password ispn-admin $ bin/cli.sh user create -g application -p password ispn-user
Maven依存関係等は、まずはこんな感じで用意。
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-client-hotrod</artifactId> <version>14.0.6.Final</version> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> <version>14.0.6.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> </plugin> </plugins> </build>
簡単なテストコードを書いて、確認してみます。
src/test/java/org/littlewings/infinspan/remote/iouring/HotRodClientIoUringTest.java
package org.littlewings.infinspan.remote.iouring; import java.util.function.Consumer; import java.util.stream.IntStream; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.client.hotrod.RemoteCacheManager; import org.infinispan.client.hotrod.RemoteCacheManagerAdmin; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class HotRodClientIoUringTest { static String createUri(String userName, String password) { return String.format( "hotrod://%s:%s@172.18.0.2:11222,172.18.0.3:11222,172.18.0.4:11222", userName, password ); } @BeforeAll static void setUpAll() { String uri = createUri("ispn-admin", "password"); try (RemoteCacheManager manager = new RemoteCacheManager(uri)) { RemoteCacheManagerAdmin admin = manager.administration(); admin.removeCache("distCache"); org.infinispan.configuration.cache.Configuration configuration = new org.infinispan.configuration.cache.ConfigurationBuilder() .clustering() .cacheMode(org.infinispan.configuration.cache.CacheMode.DIST_SYNC) .encoding().key().mediaType("application/x-protostream") .encoding().value().mediaType("application/x-protostream") .build(); admin.getOrCreateCache("distCache", configuration); } } <K, V> void withCache(String cacheName, Consumer<RemoteCache<K, V>> func) { String uri = createUri("ispn-user", "password"); try (RemoteCacheManager manager = new RemoteCacheManager(uri)) { RemoteCache<K, V> cache = manager.getCache(cacheName); func.accept(cache); } } @Test void transportIoUring() { this.<String, String>withCache("distCache", cache -> { IntStream .rangeClosed(1, 100) .forEach(i -> cache.put("key" + i, "value" + i)); IntStream .rangeClosed(1, 100) .forEach(i -> assertThat(cache.get("key" + i)).isEqualTo("value" + i)); }); } }
確認。
$ mvn test
このテスト自体は動くのですが、io_uringは使えないといった感じのログが出ます。
2月 05, 2023 7:37:16 午後 org.infinispan.client.hotrod.impl.transport.netty.NativeTransport useNativeIOUring INFO: ISPN004108: Native IOUring transport not available, using NIO instead: io.netty.incubator.channel.uring.IOUring
どうやらクラスパス上にio.netty.incubator.channel.uring.IOUring
クラスがなく、かつHot Rod Clientのpom.xml
を見ると
Netty io_uringはオプションになっているようです。
https://github.com/infinispan/infinispan/blob/14.0.6.Final/client/hotrod-client/pom.xml#L103-L121
というわけで、依存関係にNetty io_uringを追加します。
<dependency> <groupId>io.netty.incubator</groupId> <artifactId>netty-incubator-transport-native-io_uring</artifactId> <version>0.0.14.Final</version> <classifier>linux-x86_64</classifier> </dependency>
バージョンは、Infinispanが使用しているものに合わせておきます。
https://github.com/infinispan/infinispan/blob/14.0.6.Final/build/configuration/pom.xml#L184
再度実行。
$ mvn test
transportに関するログがなくなったので、io_uringが使われているかどうかもわからなくなりました…。
Infinispan Serverと違って、どのトランスポートを選択したのかを示すログは出力されないんですよね。
strace
で確認すると、io_uringを使っていなさそうだったので
$ strace -f mvn test 2>&1 | grep io_uring
ソースコードの感じからして、やっぱりepollを無効にする必要がありそうです。
システムプロパティで指定して実行。
$ mvn test -Dinfinispan.server.channel.epoll=false
やっぱり見かけ上はわからないので、strace
で確認すると
$ strace -f mvn test -Dinfinispan.server.channel.epoll=false 2>&1 | grep io_uring
io_uringに関するシステムコールが使われていることが確認できます。
[pid 25604] <... io_uring_enter resumed>) = 2 [pid 25604] io_uring_enter(93, 2, 1, IORING_ENTER_GETEVENTS, NULL, 140217797312520 <unfinished ...> [pid 25604] <... io_uring_enter resumed>) = 2 [pid 25604] io_uring_enter(93, 2, 1, IORING_ENTER_GETEVENTS, NULL, 140217797312520 <unfinished ...> [pid 25604] <... io_uring_enter resumed>) = 2 [pid 25604] io_uring_enter(93, 0, 1, IORING_ENTER_GETEVENTS, NULL, 30064771080 <unfinished ...> [pid 25604] <... io_uring_enter resumed>) = 0
最終的なMaven依存関係などを書くと、こうなりました。
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-client-hotrod</artifactId> <version>14.0.6.Final</version> </dependency> <dependency> <groupId>io.netty.incubator</groupId> <artifactId>netty-incubator-transport-native-io_uring</artifactId> <version>0.0.14.Final</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.infinispan</groupId> <artifactId>infinispan-core</artifactId> <version>14.0.6.Final</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> </plugin> </plugins> </build>
それから、システムプロパティの指定は今回の例だとソースコードにそのまま書いても良いかなと。
public class HotRodClientIoUringTest { static { System.setProperty("infinispan.server.channel.epoll", "false"); }
とりあえず、io_uringを使うようにはできたので、良しとしましょう。
まとめ
Infinispanで、io_uringを使うようにしてみました。
Infinispan Serverだけかなと思っていたのですが、Hot Rod Client側も対応していたんですね。ただ、新しいHot Rod Clientの方は対応して
いないようでしたが。
今回作成したソースコードは、こちらに置いています。
https://github.com/kazuhira-r/infinispan-getting-started/tree/master/remote-experimental-io_uring