Skip to content

Commit

Permalink
Ensure that the extended socket options TCP_KEEPXXX are available (#8…
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisHegarty committed Jul 29, 2022
1 parent a3a5332 commit 4e3b71b
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ private static Process createProcess(
command.addAll(jvmOptions);
command.add("--module-path");
command.add(esHome.resolve("lib").toString());
command.add("--add-modules=jdk.net"); // very special circumstance; explicit modules should typically not be added here
command.add("-m");
command.add("org.elasticsearch.server/org.elasticsearch.bootstrap.Elasticsearch");

Expand Down
6 changes: 6 additions & 0 deletions docs/changelog/88935.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pr: 88935
summary: Ensure that the extended socket options TCP_KEEPXXX are available
area: Network
type: bug
issues:
- 88897
1 change: 1 addition & 0 deletions modules/transport-netty4/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

module org.elasticsearch.transport.netty4 {
requires jdk.net;
requires org.elasticsearch.base;
requires org.elasticsearch.server;
requires org.elasticsearch.xcontent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import org.elasticsearch.xcontent.NamedXContentRegistry;

import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.util.concurrent.TimeUnit;

import static org.elasticsearch.http.HttpTransportSettings.SETTING_HTTP_MAX_CHUNK_SIZE;
Expand Down Expand Up @@ -215,25 +214,22 @@ protected void doStart() {
// Netty logs a warning if it can't set the option, so try this only on supported platforms
if (IOUtils.LINUX || IOUtils.MAC_OS_X) {
if (SETTING_HTTP_TCP_KEEP_IDLE.get(settings) >= 0) {
final SocketOption<Integer> keepIdleOption = NetUtils.getTcpKeepIdleSocketOptionOrNull();
if (keepIdleOption != null) {
serverBootstrap.childOption(NioChannelOption.of(keepIdleOption), SETTING_HTTP_TCP_KEEP_IDLE.get(settings));
}
serverBootstrap.childOption(
NioChannelOption.of(NetUtils.getTcpKeepIdleSocketOption()),
SETTING_HTTP_TCP_KEEP_IDLE.get(settings)
);
}
if (SETTING_HTTP_TCP_KEEP_INTERVAL.get(settings) >= 0) {
final SocketOption<Integer> keepIntervalOption = NetUtils.getTcpKeepIntervalSocketOptionOrNull();
if (keepIntervalOption != null) {
serverBootstrap.childOption(
NioChannelOption.of(keepIntervalOption),
SETTING_HTTP_TCP_KEEP_INTERVAL.get(settings)
);
}
serverBootstrap.childOption(
NioChannelOption.of(NetUtils.getTcpKeepIntervalSocketOption()),
SETTING_HTTP_TCP_KEEP_INTERVAL.get(settings)
);
}
if (SETTING_HTTP_TCP_KEEP_COUNT.get(settings) >= 0) {
final SocketOption<Integer> keepCountOption = NetUtils.getTcpKeepCountSocketOptionOrNull();
if (keepCountOption != null) {
serverBootstrap.childOption(NioChannelOption.of(keepCountOption), SETTING_HTTP_TCP_KEEP_COUNT.get(settings));
}
serverBootstrap.childOption(
NioChannelOption.of(NetUtils.getTcpKeepCountSocketOption()),
SETTING_HTTP_TCP_KEEP_COUNT.get(settings)
);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@

package org.elasticsearch.transport.netty4;

import jdk.net.ExtendedSocketOptions;

import org.elasticsearch.core.SuppressForbidden;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.NetworkChannel;
import java.util.Arrays;
import java.util.Objects;

/**
* Utilities for network-related methods.
Expand All @@ -22,37 +25,31 @@ public class NetUtils {

private NetUtils() {}

// Accessors to the extended socket options reduce the proliferation of the non-portable
// ExtendedSocketOptions type.

/**
* Returns the extended TCP_KEEPIDLE socket option, if available on this JDK
* Returns the extended TCP_KEEPIDLE socket option.
*/
public static SocketOption<Integer> getTcpKeepIdleSocketOptionOrNull() {
return getExtendedSocketOptionOrNull("TCP_KEEPIDLE");
@SuppressForbidden(reason = "access to non-portable socket option required")
public static SocketOption<Integer> getTcpKeepIdleSocketOption() {
return ExtendedSocketOptions.TCP_KEEPIDLE;
}

/**
* Returns the extended TCP_KEEPINTERVAL socket option, if available on this JDK
* Returns the extended TCP_KEEPINTERVAL socket option.
*/
public static SocketOption<Integer> getTcpKeepIntervalSocketOptionOrNull() {
return getExtendedSocketOptionOrNull("TCP_KEEPINTERVAL");
@SuppressForbidden(reason = "access to non-portable socket option required")
public static SocketOption<Integer> getTcpKeepIntervalSocketOption() {
return ExtendedSocketOptions.TCP_KEEPINTERVAL;
}

/**
* Returns the extended TCP_KEEPCOUNT socket option, if available on this JDK
* Returns the extended TCP_KEEPCOUNT socket option.
*/
public static SocketOption<Integer> getTcpKeepCountSocketOptionOrNull() {
return getExtendedSocketOptionOrNull("TCP_KEEPCOUNT");
}

@SuppressWarnings("unchecked")
private static <T> SocketOption<T> getExtendedSocketOptionOrNull(String fieldName) {
try {
final Class<?> extendedSocketOptionsClass = Class.forName("jdk.net.ExtendedSocketOptions");
final Field field = extendedSocketOptionsClass.getField(fieldName);
return (SocketOption<T>) field.get(null);
} catch (Exception t) {
// ignore
return null;
}
@SuppressForbidden(reason = "access to non-portable socket option required")
public static SocketOption<Integer> getTcpKeepCountSocketOption() {
return ExtendedSocketOptions.TCP_KEEPCOUNT;
}

/**
Expand All @@ -67,13 +64,9 @@ public static void tryEnsureReasonableKeepAliveConfig(NetworkChannel socketChann
if (socketChannel.supportedOptions().contains(StandardSocketOptions.SO_KEEPALIVE)) {
final Boolean keepalive = socketChannel.getOption(StandardSocketOptions.SO_KEEPALIVE);
assert keepalive != null;
if (keepalive.booleanValue()) {
for (SocketOption<Integer> option : Arrays.asList(
NetUtils.getTcpKeepIdleSocketOptionOrNull(),
NetUtils.getTcpKeepIntervalSocketOptionOrNull()
)) {
setMinValueForSocketOption(socketChannel, option, 300);
}
if (keepalive) {
setMinValueForSocketOption(socketChannel, getTcpKeepIdleSocketOption(), 300);
setMinValueForSocketOption(socketChannel, getTcpKeepIntervalSocketOption(), 300);
}
}
} catch (Exception e) {
Expand All @@ -84,7 +77,8 @@ public static void tryEnsureReasonableKeepAliveConfig(NetworkChannel socketChann
}

private static void setMinValueForSocketOption(NetworkChannel socketChannel, SocketOption<Integer> option, int minValue) {
if (option != null && socketChannel.supportedOptions().contains(option)) {
Objects.requireNonNull(option);
if (socketChannel.supportedOptions().contains(option)) {
try {
final Integer currentIdleVal = socketChannel.getOption(option);
assert currentIdleVal != null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketOption;
import java.util.Map;

import static org.elasticsearch.common.settings.Setting.byteSizeSetting;
Expand Down Expand Up @@ -165,22 +164,19 @@ private Bootstrap createClientBootstrap(SharedGroupFactory.SharedGroup sharedGro
if (TransportSettings.TCP_KEEP_ALIVE.get(settings)) {
// Note that Netty logs a warning if it can't set the option
if (TransportSettings.TCP_KEEP_IDLE.get(settings) >= 0) {
final SocketOption<Integer> keepIdleOption = NetUtils.getTcpKeepIdleSocketOptionOrNull();
if (keepIdleOption != null) {
bootstrap.option(NioChannelOption.of(keepIdleOption), TransportSettings.TCP_KEEP_IDLE.get(settings));
}
bootstrap.option(NioChannelOption.of(NetUtils.getTcpKeepIdleSocketOption()), TransportSettings.TCP_KEEP_IDLE.get(settings));
}
if (TransportSettings.TCP_KEEP_INTERVAL.get(settings) >= 0) {
final SocketOption<Integer> keepIntervalOption = NetUtils.getTcpKeepIntervalSocketOptionOrNull();
if (keepIntervalOption != null) {
bootstrap.option(NioChannelOption.of(keepIntervalOption), TransportSettings.TCP_KEEP_INTERVAL.get(settings));
}
bootstrap.option(
NioChannelOption.of(NetUtils.getTcpKeepIntervalSocketOption()),
TransportSettings.TCP_KEEP_INTERVAL.get(settings)
);
}
if (TransportSettings.TCP_KEEP_COUNT.get(settings) >= 0) {
final SocketOption<Integer> keepCountOption = NetUtils.getTcpKeepCountSocketOptionOrNull();
if (keepCountOption != null) {
bootstrap.option(NioChannelOption.of(keepCountOption), TransportSettings.TCP_KEEP_COUNT.get(settings));
}
bootstrap.option(
NioChannelOption.of(NetUtils.getTcpKeepCountSocketOption()),
TransportSettings.TCP_KEEP_COUNT.get(settings)
);
}
}

Expand Down Expand Up @@ -236,23 +232,16 @@ private void createServerBootstrap(ProfileSettings profileSettings, SharedGroupF
if (profileSettings.tcpKeepAlive) {
// Note that Netty logs a warning if it can't set the option
if (profileSettings.tcpKeepIdle >= 0) {
final SocketOption<Integer> keepIdleOption = NetUtils.getTcpKeepIdleSocketOptionOrNull();
if (keepIdleOption != null) {
serverBootstrap.childOption(NioChannelOption.of(keepIdleOption), profileSettings.tcpKeepIdle);
}
serverBootstrap.childOption(NioChannelOption.of(NetUtils.getTcpKeepIdleSocketOption()), profileSettings.tcpKeepIdle);
}
if (profileSettings.tcpKeepInterval >= 0) {
final SocketOption<Integer> keepIntervalOption = NetUtils.getTcpKeepIntervalSocketOptionOrNull();
if (keepIntervalOption != null) {
serverBootstrap.childOption(NioChannelOption.of(keepIntervalOption), profileSettings.tcpKeepInterval);
}

serverBootstrap.childOption(
NioChannelOption.of(NetUtils.getTcpKeepIntervalSocketOption()),
profileSettings.tcpKeepInterval
);
}
if (profileSettings.tcpKeepCount >= 0) {
final SocketOption<Integer> keepCountOption = NetUtils.getTcpKeepCountSocketOptionOrNull();
if (keepCountOption != null) {
serverBootstrap.childOption(NioChannelOption.of(keepCountOption), profileSettings.tcpKeepCount);
}
serverBootstrap.childOption(NioChannelOption.of(NetUtils.getTcpKeepCountSocketOption()), profileSettings.tcpKeepCount);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,38 @@

package org.elasticsearch.transport.netty4;

import org.apache.lucene.util.Constants;
import org.elasticsearch.core.IOUtils;
import org.elasticsearch.test.ESTestCase;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.channels.NetworkChannel;
import java.nio.channels.SocketChannel;

import static org.hamcrest.Matchers.hasItem;

public class NetUtilsTests extends ESTestCase {

public void testExtendedSocketOptions() {
assumeTrue("JDK possibly not supported", Constants.JVM_NAME.contains("HotSpot") || Constants.JVM_NAME.contains("OpenJDK"));
public void testExtendedSocketOptions() throws IOException {
assertTrue(
"jdk.net module not resolved",
ModuleLayer.boot().modules().stream().map(Module::getName).anyMatch(nm -> nm.equals("jdk.net"))
);

assumeTrue("Platform possibly not supported", IOUtils.LINUX || IOUtils.MAC_OS_X);
assertNotNull(NetUtils.getTcpKeepIdleSocketOptionOrNull());
assertNotNull(NetUtils.getTcpKeepIntervalSocketOptionOrNull());
assertNotNull(NetUtils.getTcpKeepCountSocketOptionOrNull());
try (var channel = networkChannel()) {
var options = channel.supportedOptions();
assertThat(options, hasItem(NetUtils.getTcpKeepIdleSocketOption()));
assertThat(options, hasItem(NetUtils.getTcpKeepIntervalSocketOption()));
assertThat(options, hasItem(NetUtils.getTcpKeepCountSocketOption()));
}
}

private static NetworkChannel networkChannel() {
try {
return SocketChannel.open();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,12 @@ private void checkDefaultKeepAliveOptions(TcpChannel channel) throws IOException
assertThat(nettyChannel.getNettyChannel(), instanceOf(Netty4NioSocketChannel.class));
Netty4NioSocketChannel netty4NioSocketChannel = (Netty4NioSocketChannel) nettyChannel.getNettyChannel();
SocketChannel socketChannel = netty4NioSocketChannel.javaChannel();
assertThat(socketChannel.supportedOptions(), hasItem(NetUtils.getTcpKeepIdleSocketOptionOrNull()));
Integer keepIdle = socketChannel.getOption(NetUtils.getTcpKeepIdleSocketOptionOrNull());
assertThat(socketChannel.supportedOptions(), hasItem(NetUtils.getTcpKeepIdleSocketOption()));
Integer keepIdle = socketChannel.getOption(NetUtils.getTcpKeepIdleSocketOption());
assertNotNull(keepIdle);
assertThat(keepIdle, lessThanOrEqualTo(500));
assertThat(socketChannel.supportedOptions(), hasItem(NetUtils.getTcpKeepIntervalSocketOptionOrNull()));
Integer keepInterval = socketChannel.getOption(NetUtils.getTcpKeepIntervalSocketOptionOrNull());
assertThat(socketChannel.supportedOptions(), hasItem(NetUtils.getTcpKeepIntervalSocketOption()));
Integer keepInterval = socketChannel.getOption(NetUtils.getTcpKeepIntervalSocketOption());
assertNotNull(keepInterval);
assertThat(keepInterval, lessThanOrEqualTo(500));
}
Expand Down

0 comments on commit 4e3b71b

Please sign in to comment.