From 379bc4d7210db8f98623ad9a61e80b8ea214a1f6 Mon Sep 17 00:00:00 2001 From: Igor Melnichenko Date: Sat, 10 Oct 2026 21:06:50 +0300 Subject: [PATCH 1/2] Share one SslContext between the channels of a factory NettyChannelFactory and ShadedNettyChannelFactory built a new SslContext for every TLS channel: the custom certificate was parsed again, or the trust managers were wrapped again, and a new (often native OpenSSL) context was allocated for each node channel, for every auth channel and for the discovery channel created every minute. SslContext is immutable and thread-safe, so create it lazily once per factory and reuse it. A failure to create it isn't cached and is still reported on every channel creation. Co-Authored-By: Claude Opus 5.5 --- .../java/tech/ydb/core/impl/pool/NettyChannelFactory.java | 4 +++- .../tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java b/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java index 9ab2737ea..91826ad78 100644 --- a/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java +++ b/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java @@ -39,6 +39,7 @@ public class NettyChannelFactory implements ManagedChannelFactory { private final boolean useDefaultGrpcResolver; private final Long grpcKeepAliveTimeMillis; private final List>> initializers; + private final SslContext sslContext; private NettyChannelFactory(GrpcTransportBuilder builder) { this.metadata = YdbHeaders.createMetadataInterceptor(builder); @@ -48,6 +49,7 @@ private NettyChannelFactory(GrpcTransportBuilder builder) { this.useDefaultGrpcResolver = builder.useDefaultGrpcResolver(); this.grpcKeepAliveTimeMillis = builder.getGrpcKeepAliveTimeMillis(); this.initializers = builder.getChannelInitializers(); + this.sslContext = createSslContext(); } @SuppressWarnings("deprecation") @@ -59,7 +61,7 @@ public ManagedChannel newManagedChannel(String host, int port, String sslHostOve if (useTLS) { channelBuilder .negotiationType(NegotiationType.TLS) - .sslContext(createSslContext()); + .sslContext(sslContext); if (sslHostOverride != null) { channelBuilder.overrideAuthority(sslHostOverride); } diff --git a/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java b/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java index 9da822fce..bf797a726 100644 --- a/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java +++ b/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java @@ -39,6 +39,7 @@ public class ShadedNettyChannelFactory implements ManagedChannelFactory { private final boolean useDefaultGrpcResolver; private final Long grpcKeepAliveTimeMillis; private final List>> initializers; + private final SslContext sslContext; private ShadedNettyChannelFactory(GrpcTransportBuilder builder) { this.metadata = YdbHeaders.createMetadataInterceptor(builder); @@ -48,6 +49,7 @@ private ShadedNettyChannelFactory(GrpcTransportBuilder builder) { this.useDefaultGrpcResolver = builder.useDefaultGrpcResolver(); this.grpcKeepAliveTimeMillis = builder.getGrpcKeepAliveTimeMillis(); this.initializers = builder.getChannelInitializers(); + this.sslContext = createSslContext(); } @SuppressWarnings("deprecation") @@ -59,7 +61,7 @@ public ManagedChannel newManagedChannel(String host, int port, String sslHostOve if (useTLS) { channelBuilder .negotiationType(NegotiationType.TLS) - .sslContext(createSslContext()); + .sslContext(sslContext); if (sslHostOverride != null) { channelBuilder.overrideAuthority(sslHostOverride); } From a240928fa69c33cc6cfbcde3484d4bfa4ede692a Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Fri, 16 Oct 2026 14:07:33 +0100 Subject: [PATCH 2/2] Style and test fixes --- .../core/impl/pool/NettyChannelFactory.java | 22 +++++++++---------- .../impl/pool/ShadedNettyChannelFactory.java | 22 +++++++++---------- .../impl/pool/DefaultChannelFactoryTest.java | 4 +--- .../impl/pool/NettyChannelFactoryTest.java | 4 +--- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java b/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java index 91826ad78..1c004b881 100644 --- a/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java +++ b/core/src/main/java/tech/ydb/core/impl/pool/NettyChannelFactory.java @@ -33,23 +33,19 @@ public class NettyChannelFactory implements ManagedChannelFactory { private final ClientInterceptor metadata; - private final boolean useTLS; - private final byte[] cert; private final boolean retryEnabled; private final boolean useDefaultGrpcResolver; private final Long grpcKeepAliveTimeMillis; private final List>> initializers; - private final SslContext sslContext; + private final SslContext sslCtx; private NettyChannelFactory(GrpcTransportBuilder builder) { this.metadata = YdbHeaders.createMetadataInterceptor(builder); - this.useTLS = builder.getUseTls(); - this.cert = builder.getCert(); this.retryEnabled = builder.isEnableRetry(); this.useDefaultGrpcResolver = builder.useDefaultGrpcResolver(); this.grpcKeepAliveTimeMillis = builder.getGrpcKeepAliveTimeMillis(); this.initializers = builder.getChannelInitializers(); - this.sslContext = createSslContext(); + this.sslCtx = createSslContext(builder.getUseTls(), builder.getCert()); } @SuppressWarnings("deprecation") @@ -58,10 +54,10 @@ public ManagedChannel newManagedChannel(String host, int port, String sslHostOve NettyChannelBuilder channelBuilder = NettyChannelBuilder .forAddress(host, port); - if (useTLS) { + if (sslCtx != null) { channelBuilder .negotiationType(NegotiationType.TLS) - .sslContext(sslContext); + .sslContext(sslCtx); if (sslHostOverride != null) { channelBuilder.overrideAuthority(sslHostOverride); } @@ -106,11 +102,15 @@ protected void configure(NettyChannelBuilder channelBuilder) { } - private SslContext createSslContext() { + private static SslContext createSslContext(boolean useTLS, byte[] customCert) { + if (!useTLS) { + return null; + } + try { SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient(); - if (cert != null) { - sslContextBuilder.trustManager(new ByteArrayInputStream(cert)); + if (customCert != null) { + sslContextBuilder.trustManager(new ByteArrayInputStream(customCert)); } else { sslContextBuilder.trustManager(new YandexTrustManagerFactory("")); } diff --git a/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java b/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java index bf797a726..091d6ebb4 100644 --- a/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java +++ b/core/src/main/java/tech/ydb/core/impl/pool/ShadedNettyChannelFactory.java @@ -33,23 +33,19 @@ public class ShadedNettyChannelFactory implements ManagedChannelFactory { private final ClientInterceptor metadata; - private final boolean useTLS; - private final byte[] cert; private final boolean retryEnabled; private final boolean useDefaultGrpcResolver; private final Long grpcKeepAliveTimeMillis; private final List>> initializers; - private final SslContext sslContext; + private final SslContext sslCtx; private ShadedNettyChannelFactory(GrpcTransportBuilder builder) { this.metadata = YdbHeaders.createMetadataInterceptor(builder); - this.useTLS = builder.getUseTls(); - this.cert = builder.getCert(); this.retryEnabled = builder.isEnableRetry(); this.useDefaultGrpcResolver = builder.useDefaultGrpcResolver(); this.grpcKeepAliveTimeMillis = builder.getGrpcKeepAliveTimeMillis(); this.initializers = builder.getChannelInitializers(); - this.sslContext = createSslContext(); + this.sslCtx = createSslContext(builder.getUseTls(), builder.getCert()); } @SuppressWarnings("deprecation") @@ -58,10 +54,10 @@ public ManagedChannel newManagedChannel(String host, int port, String sslHostOve NettyChannelBuilder channelBuilder = NettyChannelBuilder .forAddress(host, port); - if (useTLS) { + if (sslCtx != null) { channelBuilder .negotiationType(NegotiationType.TLS) - .sslContext(sslContext); + .sslContext(sslCtx); if (sslHostOverride != null) { channelBuilder.overrideAuthority(sslHostOverride); } @@ -106,11 +102,15 @@ protected void configure(NettyChannelBuilder channelBuilder) { } - private SslContext createSslContext() { + private static SslContext createSslContext(boolean useTLS, byte[] customCert) { + if (!useTLS) { + return null; + } + try { SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient(); - if (cert != null) { - sslContextBuilder.trustManager(new ByteArrayInputStream(cert)); + if (customCert != null) { + sslContextBuilder.trustManager(new ByteArrayInputStream(customCert)); } else { sslContextBuilder.trustManager(new YandexTrustManagerFactory("")); } diff --git a/core/src/test/java/tech/ydb/core/impl/pool/DefaultChannelFactoryTest.java b/core/src/test/java/tech/ydb/core/impl/pool/DefaultChannelFactoryTest.java index efb100316..322549ec7 100644 --- a/core/src/test/java/tech/ydb/core/impl/pool/DefaultChannelFactoryTest.java +++ b/core/src/test/java/tech/ydb/core/impl/pool/DefaultChannelFactoryTest.java @@ -244,10 +244,8 @@ public void invalidSslCert() { GrpcTransportBuilder builder = GrpcTransport.forHost(MOCKED_HOST, MOCKED_PORT, "/Root") .withSecureConnection(cert); - ManagedChannelFactory factory = ChannelFactoryLoader.load().buildFactory(builder); - RuntimeException ex = Assert.assertThrows(RuntimeException.class, - () -> factory.newManagedChannel(MOCKED_HOST, MOCKED_PORT, null)); + () -> ChannelFactoryLoader.load().buildFactory(builder)); Assert.assertEquals("cannot create ssl context", ex.getMessage()); Assert.assertNotNull(ex.getCause()); diff --git a/core/src/test/java/tech/ydb/core/impl/pool/NettyChannelFactoryTest.java b/core/src/test/java/tech/ydb/core/impl/pool/NettyChannelFactoryTest.java index 1edf8e2e5..885ad35dd 100644 --- a/core/src/test/java/tech/ydb/core/impl/pool/NettyChannelFactoryTest.java +++ b/core/src/test/java/tech/ydb/core/impl/pool/NettyChannelFactoryTest.java @@ -243,10 +243,8 @@ public void invalidSslCert() { GrpcTransportBuilder builder = GrpcTransport.forHost(MOCKED_HOST, MOCKED_PORT, "/Root") .withSecureConnection(cert); - ManagedChannelFactory factory = ChannelFactoryLoader.load().buildFactory(builder); - RuntimeException ex = Assert.assertThrows(RuntimeException.class, - () -> factory.newManagedChannel(MOCKED_HOST, MOCKED_PORT, null)); + () -> ChannelFactoryLoader.load().buildFactory(builder)); Assert.assertEquals("cannot create ssl context", ex.getMessage()); Assert.assertNotNull(ex.getCause());