From df150c0aa3be36fa16d3fdac72613efedd399542 Mon Sep 17 00:00:00 2001 From: xiaolongran Date: Tue, 28 Apr 2026 14:57:38 +0800 Subject: [PATCH 1/3] Support bookie server config tcp keep-alive Signed-off-by: xiaolongran --- .../bookkeeper/conf/ServerConfiguration.java | 66 +++++++ .../bookkeeper/proto/BookieNettyServer.java | 49 +++++ .../proto/PerChannelBookieClient.java | 11 ++ .../TestBookieNettyServerTcpKeepalive.java | 187 ++++++++++++++++++ ...estPerChannelBookieClientTcpKeepalive.java | 89 ++++++++- 5 files changed, 394 insertions(+), 8 deletions(-) create mode 100644 bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestBookieNettyServerTcpKeepalive.java diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java index 4f95443e311..a6ce37874db 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java @@ -178,6 +178,9 @@ public class ServerConfiguration extends AbstractConfiguration 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + } + if (conf.getServerTcpKeepIntvl() > 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + } + if (conf.getServerTcpKeepCnt() > 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + } + } else if (eventLoopGroup instanceof EpollEventLoopGroup) { + if (conf.getServerTcpKeepIdle() > 0) { + bootstrap.option(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + } + if (conf.getServerTcpKeepIntvl() > 0) { + bootstrap.option(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + } + if (conf.getServerTcpKeepCnt() > 0) { + bootstrap.option(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + } + } + if (EventLoopUtil.isIoUringGroup(eventLoopGroup)) { bootstrap.channel(IoUringServerSocketChannel.class); } else if (eventLoopGroup instanceof EpollEventLoopGroup) { @@ -391,6 +417,29 @@ protected void initChannel(SocketChannel ch) throws Exception { jvmBootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark( conf.getServerWriteBufferLowWaterMark(), conf.getServerWriteBufferHighWaterMark())); + // Set TCP keepalive parameters if configured + if (EventLoopUtil.isIoUringGroup(jvmEventLoopGroup)) { + if (conf.getServerTcpKeepIdle() > 0) { + jvmBootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + } + if (conf.getServerTcpKeepIntvl() > 0) { + jvmBootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + } + if (conf.getServerTcpKeepCnt() > 0) { + jvmBootstrap.option(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + } + } else if (jvmEventLoopGroup instanceof EpollEventLoopGroup) { + if (conf.getServerTcpKeepIdle() > 0) { + jvmBootstrap.option(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + } + if (conf.getServerTcpKeepIntvl() > 0) { + jvmBootstrap.option(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + } + if (conf.getServerTcpKeepCnt() > 0) { + jvmBootstrap.option(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + } + } + if (jvmEventLoopGroup instanceof DefaultEventLoopGroup) { jvmBootstrap.channel(LocalServerChannel.class); } else if (EventLoopUtil.isIoUringGroup(jvmEventLoopGroup)) { diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java index 25abe5e7031..69cb06bd5b8 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/PerChannelBookieClient.java @@ -572,6 +572,17 @@ protected ChannelFuture connect() { if (conf.getTcpKeepCnt() > 0) { bootstrap.option(EpollChannelOption.TCP_KEEPCNT, conf.getTcpKeepCnt()); } + } else if (EventLoopUtil.isIoUringGroup(eventLoopGroup)) { + // Set TCP keepalive parameters for IoUring if configured + if (conf.getTcpKeepIdle() > 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, conf.getTcpKeepIdle()); + } + if (conf.getTcpKeepIntvl() > 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, conf.getTcpKeepIntvl()); + } + if (conf.getTcpKeepCnt() > 0) { + bootstrap.option(IoUringChannelOption.TCP_KEEPCNT, conf.getTcpKeepCnt()); + } } // if buffer sizes are 0, let OS auto-tune it if (conf.getClientSendBufferSize() > 0) { diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestBookieNettyServerTcpKeepalive.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestBookieNettyServerTcpKeepalive.java new file mode 100644 index 00000000000..9497e5c96f3 --- /dev/null +++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestBookieNettyServerTcpKeepalive.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.bookkeeper.proto; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.uring.IoUring; +import java.util.concurrent.TimeUnit; +import org.apache.bookkeeper.conf.ServerConfiguration; +import org.apache.bookkeeper.util.EventLoopUtil; +import org.junit.Test; + +/** + * Unit tests for TCP keepalive configuration in BookieNettyServer. + * This test focuses on server-side TCP keepalive configuration logic + * without requiring actual network connections. + */ +public class TestBookieNettyServerTcpKeepalive { + + /** + * Test ServerConfiguration TCP keepalive getter methods. + */ + @Test + public void testServerConfigurationGetters() { + ServerConfiguration conf = new ServerConfiguration(); + + // Test default values (should be -1 as per implementation) + assertEquals("Default server TCP keep idle should be -1", -1, conf.getServerTcpKeepIdle()); + assertEquals("Default server TCP keep interval should be -1", -1, conf.getServerTcpKeepIntvl()); + assertEquals("Default server TCP keep count should be -1", -1, conf.getServerTcpKeepCnt()); + + // Test setter and getter methods + conf.setServerTcpKeepIdle(60); + conf.setServerTcpKeepIntvl(30); + conf.setServerTcpKeepCnt(5); + + assertEquals("Server TCP keep idle should be 60", 60, conf.getServerTcpKeepIdle()); + assertEquals("Server TCP keep interval should be 30", 30, conf.getServerTcpKeepIntvl()); + assertEquals("Server TCP keep count should be 5", 5, conf.getServerTcpKeepCnt()); + + // Test that -1 means use system default (as per implementation comments) + conf.setServerTcpKeepIdle(-1); + conf.setServerTcpKeepIntvl(-1); + conf.setServerTcpKeepCnt(-1); + + assertEquals("Server TCP keep idle should be -1 for system default", -1, conf.getServerTcpKeepIdle()); + assertEquals("Server TCP keep interval should be -1 for system default", -1, conf.getServerTcpKeepIntvl()); + assertEquals("Server TCP keep count should be -1 for system default", -1, conf.getServerTcpKeepCnt()); + } + + /** + * Test TCP keepalive configuration conditional logic for server side. + */ + @Test + public void testServerTcpKeepaliveConditionalLogic() { + ServerConfiguration conf = new ServerConfiguration(); + + // Test with positive values + conf.setServerTcpKeepIdle(60); + conf.setServerTcpKeepIntvl(30); + conf.setServerTcpKeepCnt(5); + + assertTrue("Server TCP keep idle should be > 0", conf.getServerTcpKeepIdle() > 0); + assertTrue("Server TCP keep interval should be > 0", conf.getServerTcpKeepIntvl() > 0); + assertTrue("Server TCP keep count should be > 0", conf.getServerTcpKeepCnt() > 0); + + // Test with zero values + conf.setServerTcpKeepIdle(0); + conf.setServerTcpKeepIntvl(0); + conf.setServerTcpKeepCnt(0); + + assertFalse("Server TCP keep idle should not be configured for zero", conf.getServerTcpKeepIdle() > 0); + assertFalse("Server TCP keep interval should not be configured for zero", conf.getServerTcpKeepIntvl() > 0); + assertFalse("Server TCP keep count should not be configured for zero", conf.getServerTcpKeepCnt() > 0); + + // Test with negative values (system default) + conf.setServerTcpKeepIdle(-1); + conf.setServerTcpKeepIntvl(-1); + conf.setServerTcpKeepCnt(-1); + + assertFalse("Server TCP keep idle should not be configured for negative", conf.getServerTcpKeepIdle() > 0); + assertFalse("Server TCP keep interval should not be configured for negative", conf.getServerTcpKeepIntvl() > 0); + assertFalse("Server TCP keep count should not be configured for negative", conf.getServerTcpKeepCnt() > 0); + + // Test partial configuration + conf.setServerTcpKeepIdle(60); + conf.setServerTcpKeepIntvl(0); + conf.setServerTcpKeepCnt(5); + + assertTrue("Server TCP keep idle should be configured", conf.getServerTcpKeepIdle() > 0); + assertFalse("Server TCP keep interval should not be configured", conf.getServerTcpKeepIntvl() > 0); + assertTrue("Server TCP keep count should be configured", conf.getServerTcpKeepCnt() > 0); + } + + /** + * Test EventLoopGroup type detection logic. + */ + @Test + public void testEventLoopGroupTypeDetection() { + // Test NIO event loop group detection + NioEventLoopGroup nioGroup = new NioEventLoopGroup(1); + assertFalse("NIO event loop group should not be detected as Epoll", + nioGroup.getClass().getName().contains("Epoll")); + assertFalse("NIO event loop group should not be detected as IoUring", + EventLoopUtil.isIoUringGroup(nioGroup)); + nioGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); + + // Test DefaultEventLoopGroup detection + DefaultEventLoopGroup defaultGroup = new DefaultEventLoopGroup(1); + assertFalse("Default event loop group should not be detected as Epoll", + defaultGroup.getClass().getName().contains("Epoll")); + assertFalse("Default event loop group should not be detected as IoUring", + EventLoopUtil.isIoUringGroup(defaultGroup)); + defaultGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); + + // Test Epoll support detection: use Epoll.isAvailable() to verify that the native + // library can actually be loaded, not just that the class is on the classpath. + boolean isEpollSupported = Epoll.isAvailable(); + + // Test IoUring support detection via runtime availability check. + boolean isIoUringSupported = IoUring.isAvailable(); + + System.out.println("Epoll support detected: " + isEpollSupported); + System.out.println("IoUring support detected: " + isIoUringSupported); + System.out.println("Operating system: " + System.getProperty("os.name")); + + // The important thing is that the detection logic works correctly + // The actual result depends on the platform + assertTrue("EventLoopUtil should be available", EventLoopUtil.class != null); + } + + /** + * Test TCP keepalive parameter validation logic. + */ + @Test + public void testTcpKeepaliveParameterValidation() { + ServerConfiguration conf = new ServerConfiguration(); + + // Test valid parameter ranges + conf.setServerTcpKeepIdle(1); + conf.setServerTcpKeepIntvl(1); + conf.setServerTcpKeepCnt(1); + + assertTrue("Minimum TCP keep idle should be valid", conf.getServerTcpKeepIdle() > 0); + assertTrue("Minimum TCP keep interval should be valid", conf.getServerTcpKeepIntvl() > 0); + assertTrue("Minimum TCP keep count should be valid", conf.getServerTcpKeepCnt() > 0); + + // Test typical production values + conf.setServerTcpKeepIdle(300); + conf.setServerTcpKeepIntvl(60); + conf.setServerTcpKeepCnt(3); + + assertEquals("Production TCP keep idle should be 300", 300, conf.getServerTcpKeepIdle()); + assertEquals("Production TCP keep interval should be 60", 60, conf.getServerTcpKeepIntvl()); + assertEquals("Production TCP keep count should be 3", 3, conf.getServerTcpKeepCnt()); + + // Test boundary values + conf.setServerTcpKeepIdle(Integer.MAX_VALUE); + conf.setServerTcpKeepIntvl(Integer.MAX_VALUE); + conf.setServerTcpKeepCnt(Integer.MAX_VALUE); + + assertTrue("Maximum TCP keep idle should be valid", conf.getServerTcpKeepIdle() > 0); + assertTrue("Maximum TCP keep interval should be valid", conf.getServerTcpKeepIntvl() > 0); + assertTrue("Maximum TCP keep count should be valid", conf.getServerTcpKeepCnt() > 0); + } +} \ No newline at end of file diff --git a/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestPerChannelBookieClientTcpKeepalive.java b/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestPerChannelBookieClientTcpKeepalive.java index 6ecf2d71d91..b35f2005b76 100644 --- a/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestPerChannelBookieClientTcpKeepalive.java +++ b/bookkeeper-server/src/test/java/org/apache/bookkeeper/proto/TestPerChannelBookieClientTcpKeepalive.java @@ -22,9 +22,14 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.uring.IoUring; import java.util.concurrent.TimeUnit; import org.apache.bookkeeper.conf.ClientConfiguration; +import org.apache.bookkeeper.util.EventLoopUtil; import org.junit.Test; /** @@ -117,14 +122,9 @@ public void testTcpKeepaliveConditionalLogic() { */ @Test public void testEpollSupportDetection() { - // Test Epoll support detection - boolean isEpollSupported = false; - try { - Class.forName("io.netty.channel.epoll.EpollEventLoopGroup"); - isEpollSupported = true; - } catch (ClassNotFoundException e) { - // Epoll not supported on this platform - } + // Test Epoll support detection: use Epoll.isAvailable() to verify that the native + // library can actually be loaded, not just that the class is on the classpath. + boolean isEpollSupported = Epoll.isAvailable(); // Test NIO event loop group detection NioEventLoopGroup nioGroup = new NioEventLoopGroup(1); @@ -138,4 +138,77 @@ public void testEpollSupportDetection() { System.out.println("Epoll support detected: " + isEpollSupported); System.out.println("Operating system: " + System.getProperty("os.name")); } + + /** + * Test IoUring support detection logic. + */ + @Test + public void testIoUringSupportDetection() { + // Use runtime availability checks to avoid UnsatisfiedLinkError on non-Linux platforms. + boolean isIoUringSupported = IoUring.isAvailable(); + boolean isEpollSupported = Epoll.isAvailable(); + + // Test NIO event loop group detection + NioEventLoopGroup nioGroup = new NioEventLoopGroup(1); + assertFalse("NIO event loop group should not be detected as IoUring", + EventLoopUtil.isIoUringGroup(nioGroup)); + + // Test DefaultEventLoopGroup detection + DefaultEventLoopGroup defaultGroup = new DefaultEventLoopGroup(1); + assertFalse("Default event loop group should not be detected as IoUring", + EventLoopUtil.isIoUringGroup(defaultGroup)); + + // Only instantiate EpollEventLoopGroup when the native library is available, + // otherwise the constructor will fail on non-Linux platforms. + if (isEpollSupported) { + EpollEventLoopGroup epollGroup = new EpollEventLoopGroup(1); + assertFalse("Epoll event loop group should not be detected as IoUring", + EventLoopUtil.isIoUringGroup(epollGroup)); + epollGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); + } + + nioGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); + defaultGroup.shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); + + // The important thing is that the detection logic works correctly + // The actual result depends on the platform + System.out.println("IoUring support detected: " + isIoUringSupported); + System.out.println("Epoll support detected: " + isEpollSupported); + System.out.println("Operating system: " + System.getProperty("os.name")); + } + + /** + * Test TCP keepalive parameter validation logic. + */ + @Test + public void testTcpKeepaliveParameterValidation() { + ClientConfiguration conf = new ClientConfiguration(); + + // Test valid parameter ranges + conf.setTcpKeepIdle(1); + conf.setTcpKeepIntvl(1); + conf.setTcpKeepCnt(1); + + assertTrue("Minimum TCP keep idle should be valid", conf.getTcpKeepIdle() > 0); + assertTrue("Minimum TCP keep interval should be valid", conf.getTcpKeepIntvl() > 0); + assertTrue("Minimum TCP keep count should be valid", conf.getTcpKeepCnt() > 0); + + // Test typical production values + conf.setTcpKeepIdle(300); + conf.setTcpKeepIntvl(60); + conf.setTcpKeepCnt(3); + + assertEquals("Production TCP keep idle should be 300", 300, conf.getTcpKeepIdle()); + assertEquals("Production TCP keep interval should be 60", 60, conf.getTcpKeepIntvl()); + assertEquals("Production TCP keep count should be 3", 3, conf.getTcpKeepCnt()); + + // Test boundary values + conf.setTcpKeepIdle(Integer.MAX_VALUE); + conf.setTcpKeepIntvl(Integer.MAX_VALUE); + conf.setTcpKeepCnt(Integer.MAX_VALUE); + + assertTrue("Maximum TCP keep idle should be valid", conf.getTcpKeepIdle() > 0); + assertTrue("Maximum TCP keep interval should be valid", conf.getTcpKeepIntvl() > 0); + assertTrue("Maximum TCP keep count should be valid", conf.getTcpKeepCnt() > 0); + } } From 5725ccc5082f27d999531fbf434e32c52491d746 Mon Sep 17 00:00:00 2001 From: xiaolongran Date: Tue, 28 Apr 2026 17:25:03 +0800 Subject: [PATCH 2/3] fix comments Signed-off-by: xiaolongran --- .../bookkeeper/proto/BookieNettyServer.java | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java index 2819d4eb471..ce48060bb21 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java @@ -326,26 +326,29 @@ private void listenOn(InetSocketAddress address, BookieSocketAddress bookieAddre bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark( conf.getServerWriteBufferLowWaterMark(), conf.getServerWriteBufferHighWaterMark())); - // Set TCP keepalive parameters if configured + // Set TCP keepalive parameters if configured. Use childOption() so that these + // options are applied to each accepted SocketChannel (not the listening + // ServerSocketChannel), which is the Netty-idiomatic approach and avoids + // relying on OS-level socket option inheritance behavior. if (EventLoopUtil.isIoUringGroup(eventLoopGroup)) { if (conf.getServerTcpKeepIdle() > 0) { - bootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); } if (conf.getServerTcpKeepIntvl() > 0) { - bootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); } if (conf.getServerTcpKeepCnt() > 0) { - bootstrap.option(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + bootstrap.childOption(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); } } else if (eventLoopGroup instanceof EpollEventLoopGroup) { if (conf.getServerTcpKeepIdle() > 0) { - bootstrap.option(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); } if (conf.getServerTcpKeepIntvl() > 0) { - bootstrap.option(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); } if (conf.getServerTcpKeepCnt() > 0) { - bootstrap.option(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + bootstrap.childOption(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); } } @@ -417,26 +420,29 @@ protected void initChannel(SocketChannel ch) throws Exception { jvmBootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark( conf.getServerWriteBufferLowWaterMark(), conf.getServerWriteBufferHighWaterMark())); - // Set TCP keepalive parameters if configured + // Set TCP keepalive parameters if configured. Use childOption() so that these + // options are applied to each accepted SocketChannel (not the listening + // ServerSocketChannel), which is the Netty-idiomatic approach and avoids + // relying on OS-level socket option inheritance behavior. if (EventLoopUtil.isIoUringGroup(jvmEventLoopGroup)) { if (conf.getServerTcpKeepIdle() > 0) { - jvmBootstrap.option(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); } if (conf.getServerTcpKeepIntvl() > 0) { - jvmBootstrap.option(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); } if (conf.getServerTcpKeepCnt() > 0) { - jvmBootstrap.option(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); } } else if (jvmEventLoopGroup instanceof EpollEventLoopGroup) { if (conf.getServerTcpKeepIdle() > 0) { - jvmBootstrap.option(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); + jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); } if (conf.getServerTcpKeepIntvl() > 0) { - jvmBootstrap.option(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); + jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); } if (conf.getServerTcpKeepCnt() > 0) { - jvmBootstrap.option(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); + jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); } } From c8f9841e9b412ec4bbc7df9f41aea8a8ee1cb863 Mon Sep 17 00:00:00 2001 From: xiaolongran Date: Tue, 28 Apr 2026 17:33:40 +0800 Subject: [PATCH 3/3] fix comments Signed-off-by: xiaolongran --- .../bookkeeper/proto/BookieNettyServer.java | 42 ++----------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java index ce48060bb21..56218286a19 100644 --- a/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java +++ b/bookkeeper-server/src/main/java/org/apache/bookkeeper/proto/BookieNettyServer.java @@ -411,50 +411,16 @@ protected void initChannel(SocketChannel ch) throws Exception { ServerBootstrap jvmBootstrap = new ServerBootstrap(); jvmBootstrap.childOption(ChannelOption.ALLOCATOR, new PooledByteBufAllocator(true)); jvmBootstrap.group(jvmEventLoopGroup, jvmEventLoopGroup); - jvmBootstrap.childOption(ChannelOption.TCP_NODELAY, conf.getServerTcpNoDelay()); - jvmBootstrap.childOption(ChannelOption.SO_KEEPALIVE, conf.getServerSockKeepalive()); - jvmBootstrap.childOption(ChannelOption.SO_LINGER, conf.getServerSockLinger()); jvmBootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(conf.getRecvByteBufAllocatorSizeMin(), conf.getRecvByteBufAllocatorSizeInitial(), conf.getRecvByteBufAllocatorSizeMax())); jvmBootstrap.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark( conf.getServerWriteBufferLowWaterMark(), conf.getServerWriteBufferHighWaterMark())); - // Set TCP keepalive parameters if configured. Use childOption() so that these - // options are applied to each accepted SocketChannel (not the listening - // ServerSocketChannel), which is the Netty-idiomatic approach and avoids - // relying on OS-level socket option inheritance behavior. - if (EventLoopUtil.isIoUringGroup(jvmEventLoopGroup)) { - if (conf.getServerTcpKeepIdle() > 0) { - jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); - } - if (conf.getServerTcpKeepIntvl() > 0) { - jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); - } - if (conf.getServerTcpKeepCnt() > 0) { - jvmBootstrap.childOption(IoUringChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); - } - } else if (jvmEventLoopGroup instanceof EpollEventLoopGroup) { - if (conf.getServerTcpKeepIdle() > 0) { - jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPIDLE, conf.getServerTcpKeepIdle()); - } - if (conf.getServerTcpKeepIntvl() > 0) { - jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPINTVL, conf.getServerTcpKeepIntvl()); - } - if (conf.getServerTcpKeepCnt() > 0) { - jvmBootstrap.childOption(EpollChannelOption.TCP_KEEPCNT, conf.getServerTcpKeepCnt()); - } - } - - if (jvmEventLoopGroup instanceof DefaultEventLoopGroup) { - jvmBootstrap.channel(LocalServerChannel.class); - } else if (EventLoopUtil.isIoUringGroup(jvmEventLoopGroup)) { - jvmBootstrap.channel(IoUringServerSocketChannel.class); - } else if (jvmEventLoopGroup instanceof EpollEventLoopGroup) { - jvmBootstrap.channel(EpollServerSocketChannel.class); - } else { - jvmBootstrap.channel(NioServerSocketChannel.class); - } + // Local transport (BOOKKEEPER-896) is an in-VM transport that does not involve + // the network stack, so network-related socket options such as TCP_NODELAY, + // SO_KEEPALIVE, SO_LINGER and TCP_KEEP* are not applicable and must not be set here. + jvmBootstrap.channel(LocalServerChannel.class); jvmBootstrap.childHandler(new ChannelInitializer() { @Override