From c598140c3e3ed863601c2cfd036125797003d058 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Mon, 20 Jul 2026 10:33:13 +0100 Subject: [PATCH 1/3] Added buildInfo customization --- .../java/tech/ydb/core/grpc/YdbHeaders.java | 1 - .../tech/ydb/core/impl/BaseGrpcTransport.java | 7 +- .../core/impl/FixedCallOptionsTransport.java | 8 +- .../tech/ydb/core/impl/YdbTransportImpl.java | 14 +++- .../ydb/core/impl/auth/AuthCallOptions.java | 4 +- .../tech/ydb/core/impl/auth/GrpcAuthRpc.java | 4 + .../java/tech/ydb/core/impl/MockedCall.java | 6 ++ .../tech/ydb/core/impl/YdbDiscoveryTest.java | 3 +- .../ydb/core/impl/YdbTransportImplTest.java | 75 ++++++++++--------- .../impl/pool/DefaultChannelFactoryTest.java | 18 ----- 10 files changed, 72 insertions(+), 68 deletions(-) diff --git a/core/src/main/java/tech/ydb/core/grpc/YdbHeaders.java b/core/src/main/java/tech/ydb/core/grpc/YdbHeaders.java index 4479b93c2..6e1208a96 100644 --- a/core/src/main/java/tech/ydb/core/grpc/YdbHeaders.java +++ b/core/src/main/java/tech/ydb/core/grpc/YdbHeaders.java @@ -41,7 +41,6 @@ private YdbHeaders() { } public static ClientInterceptor createMetadataInterceptor(GrpcTransportBuilder builder) { Metadata extraHeaders = new Metadata(); extraHeaders.put(YdbHeaders.DATABASE, builder.getDatabase()); - extraHeaders.put(YdbHeaders.BUILD_INFO, builder.getBuildInfo()); String appName = builder.getApplicationName(); if (appName != null) { extraHeaders.put(YdbHeaders.APPLICATION_NAME, appName); diff --git a/core/src/main/java/tech/ydb/core/impl/BaseGrpcTransport.java b/core/src/main/java/tech/ydb/core/impl/BaseGrpcTransport.java index eaf2ff069..bdd3504f5 100644 --- a/core/src/main/java/tech/ydb/core/impl/BaseGrpcTransport.java +++ b/core/src/main/java/tech/ydb/core/impl/BaseGrpcTransport.java @@ -56,13 +56,15 @@ public abstract class BaseGrpcTransport implements GrpcTransport { private final AtomicBoolean isClosed = new AtomicBoolean(false); protected final EndpointRecord serverEndpoint; + private final String buildInfo; protected BaseGrpcTransport(GrpcTransportBuilder builder) { - this.serverEndpoint = getDiscoveryEndpoint(builder); + this(getDiscoveryEndpoint(builder), builder.getBuildInfo()); } - protected BaseGrpcTransport(EndpointRecord serverEndpoint) { + protected BaseGrpcTransport(EndpointRecord serverEndpoint, String buildInfo) { this.serverEndpoint = serverEndpoint; + this.buildInfo = buildInfo; } protected abstract AuthCallOptions getAuthCallOptions(); @@ -244,6 +246,7 @@ private static Status deadlineExpiredStatus(MethodDescriptor method, GrpcR private Metadata makeMetadataFromSettings(GrpcRequestSettings settings, EndpointRecord endpoint) { Metadata metadata = new Metadata(); + metadata.put(YdbHeaders.BUILD_INFO, buildInfo); String token = getAuthCallOptions().getToken(); if (token != null) { metadata.put(YdbHeaders.AUTH_TICKET, token); diff --git a/core/src/main/java/tech/ydb/core/impl/FixedCallOptionsTransport.java b/core/src/main/java/tech/ydb/core/impl/FixedCallOptionsTransport.java index c93f1576a..4a0edfc48 100644 --- a/core/src/main/java/tech/ydb/core/impl/FixedCallOptionsTransport.java +++ b/core/src/main/java/tech/ydb/core/impl/FixedCallOptionsTransport.java @@ -2,9 +2,6 @@ import java.util.concurrent.ScheduledExecutorService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import tech.ydb.core.grpc.GrpcRequestSettings; import tech.ydb.core.impl.auth.AuthCallOptions; import tech.ydb.core.impl.pool.EndpointRecord; @@ -16,8 +13,6 @@ * @author Aleksandr Gorshenin */ public class FixedCallOptionsTransport extends BaseGrpcTransport { - private static final Logger logger = LoggerFactory.getLogger(FixedCallOptionsTransport.class); - private final ScheduledExecutorService scheduler; private final AuthCallOptions callOptions; private final String database; @@ -27,9 +22,10 @@ public FixedCallOptionsTransport( ScheduledExecutorService scheduler, AuthCallOptions callOptions, String database, + String buildInfo, EndpointRecord endpoint, ManagedChannelFactory channelFactory) { - super(endpoint); + super(endpoint, buildInfo); this.scheduler = scheduler; this.callOptions = callOptions; this.database = database; diff --git a/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java b/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java index 52d4ba018..de1d7fa01 100644 --- a/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java +++ b/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java @@ -54,7 +54,8 @@ public YdbTransportImpl(GrpcTransportBuilder builder) { channelFactory, builder); this.channelPool = new GrpcChannelPool(channelFactory, scheduler); this.endpointPool = new EndpointPool(balancingSettings); - this.discovery = new YdbDiscovery(new DiscoveryHandler(), scheduler, database, discoveryTimeout); + DiscoveryHandler handler = new DiscoveryHandler(builder.getBuildInfo()); + this.discovery = new YdbDiscovery(handler, scheduler, database, discoveryTimeout); } public void start(GrpcTransportBuilder.InitMode mode) { @@ -149,6 +150,12 @@ protected void pessimizeEndpoint(EndpointRecord endpoint, String reason) { } private class DiscoveryHandler implements YdbDiscovery.Handler { + private final String baseBuildInfo; + + DiscoveryHandler(String buildInfo) { + this.baseBuildInfo = buildInfo; + } + @Override public Instant instant() { return Instant.now(); @@ -167,7 +174,10 @@ public CompletableFuture handleEndpoints(List endpoints @Override public GrpcTransport createDiscoveryTransport() { - return new FixedCallOptionsTransport(scheduler, callOptions, database, serverEndpoint, channelFactory); + String buildInfo = baseBuildInfo; + return new FixedCallOptionsTransport( + scheduler, callOptions, database, buildInfo, serverEndpoint, channelFactory + ); } } } diff --git a/core/src/main/java/tech/ydb/core/impl/auth/AuthCallOptions.java b/core/src/main/java/tech/ydb/core/impl/auth/AuthCallOptions.java index 925a36be3..f30529c4c 100644 --- a/core/src/main/java/tech/ydb/core/impl/auth/AuthCallOptions.java +++ b/core/src/main/java/tech/ydb/core/impl/auth/AuthCallOptions.java @@ -38,7 +38,9 @@ public AuthCallOptions( AuthRpcProvider authProvider = builder.getAuthProvider(); if (authProvider != null) { - GrpcAuthRpc rpc = new GrpcAuthRpc(endpoints, scheduler, builder.getDatabase(), channelFactory); + String database = builder.getDatabase(); + String buildInfo = builder.getBuildInfo(); + GrpcAuthRpc rpc = new GrpcAuthRpc(endpoints, scheduler, database, buildInfo, channelFactory); authIdentity = builder.getAuthProvider().createAuthIdentity(rpc); } else { authIdentity = null; diff --git a/core/src/main/java/tech/ydb/core/impl/auth/GrpcAuthRpc.java b/core/src/main/java/tech/ydb/core/impl/auth/GrpcAuthRpc.java index dd0b1a1df..8a7afcf5c 100644 --- a/core/src/main/java/tech/ydb/core/impl/auth/GrpcAuthRpc.java +++ b/core/src/main/java/tech/ydb/core/impl/auth/GrpcAuthRpc.java @@ -18,6 +18,7 @@ public class GrpcAuthRpc { private final List endpoints; private final ScheduledExecutorService scheduler; private final String database; + private final String buildInfo; private final ManagedChannelFactory channelFactory; private final AtomicInteger endpointIdx = new AtomicInteger(); @@ -25,6 +26,7 @@ public GrpcAuthRpc( List endpoints, ScheduledExecutorService scheduler, String database, + String buildInfo, ManagedChannelFactory channelFactory) { if (endpoints == null || endpoints.isEmpty()) { throw new IllegalStateException("Empty endpoints list for auth rpc"); @@ -32,6 +34,7 @@ public GrpcAuthRpc( this.endpoints = endpoints; this.scheduler = scheduler; this.database = database; + this.buildInfo = buildInfo; this.channelFactory = channelFactory; } @@ -60,6 +63,7 @@ public GrpcTransport createTransport() { scheduler, new AuthCallOptions(), database, + buildInfo, endpoints.get(endpointIdx.get()), channelFactory ); diff --git a/core/src/test/java/tech/ydb/core/impl/MockedCall.java b/core/src/test/java/tech/ydb/core/impl/MockedCall.java index 9b4bcf8e4..285a57451 100644 --- a/core/src/test/java/tech/ydb/core/impl/MockedCall.java +++ b/core/src/test/java/tech/ydb/core/impl/MockedCall.java @@ -15,6 +15,7 @@ public abstract class MockedCall extends ClientCall { private final Executor executor; + private volatile Metadata lastMetadata = null; protected MockedCall(Executor executor) { this.executor = executor; @@ -22,8 +23,13 @@ protected MockedCall(Executor executor) { protected abstract void complete(Listener listener); + public Metadata getLastCallMetadata() { + return lastMetadata; + } + @Override public void start(Listener listener, Metadata headers) { + lastMetadata = headers; executor.execute(() -> complete(listener)); } diff --git a/core/src/test/java/tech/ydb/core/impl/YdbDiscoveryTest.java b/core/src/test/java/tech/ydb/core/impl/YdbDiscoveryTest.java index cde86d055..756f11065 100644 --- a/core/src/test/java/tech/ydb/core/impl/YdbDiscoveryTest.java +++ b/core/src/test/java/tech/ydb/core/impl/YdbDiscoveryTest.java @@ -255,7 +255,8 @@ public Instant instant() { @Override public GrpcTransport createDiscoveryTransport() { EndpointRecord discovery = new EndpointRecord("unknown", 1234); - return new FixedCallOptionsTransport(scheduler, new AuthCallOptions(), "/test", discovery, channelFactory); + AuthCallOptions options = new AuthCallOptions(); + return new FixedCallOptionsTransport(scheduler, options, "/test", "test_sdk", discovery, channelFactory); } @Override diff --git a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java index b3e3566b2..e79c0fb9a 100644 --- a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java +++ b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java @@ -16,7 +16,6 @@ import io.grpc.ConnectivityState; import io.grpc.ManagedChannel; -import io.grpc.Metadata; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -35,6 +34,7 @@ import tech.ydb.core.tracing.NoopTracer; import tech.ydb.core.tracing.Span; import tech.ydb.core.tracing.Tracer; +import tech.ydb.core.utils.Version; import tech.ydb.proto.discovery.DiscoveryProtos; import tech.ydb.proto.discovery.v1.DiscoveryServiceGrpc; @@ -239,6 +239,35 @@ public void customSchedulerTest() { YdbSchedulerFactory.shutdownScheduler(custom); } + @Test + public void buildInfoTest() { + MockedCall.DiscoveryCall discoveryCall = MockedCall.discovery("self", new EndpointRecord("node", 2136)); + MockedCall.WhoAmICall whoAmICall = MockedCall.whoAmICall("i am node"); + + Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getListEndpointsMethod()), Mockito.any())) + .thenReturn(discoveryCall); + Mockito.when(transportChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getWhoAmIMethod()), Mockito.any())) + .thenReturn(whoAmICall); + + GrpcTransport transport = GrpcTransport.forEndpoint("grpc://mocked:2136", "/Root") + .withChannelFactoryBuilder(builder -> channelFactory) + .withExtraBuildInfo("driver/1.0.0") + .withExtraBuildInfo("test-app/1.0.0") + .build(); + + String sdk = "ydb-java-sdk/" + Version.getVersion().get(); + String customVersion = sdk + ";driver/1.0.0;test-app/1.0.0"; + + Result call = whoAmI(transport).join(); + + Assert.assertTrue(call.isSuccess()); + Assert.assertNotNull(discoveryCall.getLastCallMetadata()); + Assert.assertNotNull(whoAmICall.getLastCallMetadata()); + + Assert.assertEquals(customVersion, discoveryCall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); + Assert.assertEquals(customVersion, whoAmICall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); + } + @Test public void defaultTracerFactoryTest() { Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getListEndpointsMethod()), Mockito.any())) @@ -278,24 +307,10 @@ public void customTracerTest() { @Test public void spanAttributesAndTraceparentAreSetInMetadata() { EndpointRecord endpoint = new EndpointRecord("node", 2136, 42, "dc-a", null); + MockedCall.WhoAmICall whoAmICall = MockedCall.whoAmICall("i am node"); + Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getListEndpointsMethod()), Mockito.any())) .thenReturn(MockedCall.discovery("self", endpoint)); - - Metadata[] captured = new Metadata[1]; - MockedCall.WhoAmICall whoAmICall = new MockedCall.WhoAmICall(Runnable::run) { - @Override - public void start(Listener listener, Metadata headers) { - captured[0] = headers; - super.start(listener, headers); - } - - @Override - protected void complete(Listener listener) { - listener.onClose(io.grpc.Status.OK, new Metadata()); - } - }; - Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getWhoAmIMethod()), Mockito.any())) - .thenReturn(whoAmICall); Mockito.when(transportChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getWhoAmIMethod()), Mockito.any())) .thenReturn(whoAmICall); @@ -324,8 +339,8 @@ protected void complete(Listener listener) { Assert.assertEquals(Long.valueOf(42), span.longAttrs.get("ydb.node.id")); Assert.assertEquals("dc-a", span.stringAttrs.get("ydb.node.dc")); - Assert.assertNotNull(captured[0]); - Assert.assertEquals(span.id, captured[0].get(YdbHeaders.TRACEPARENT)); + Assert.assertNotNull(whoAmICall.getLastCallMetadata()); + Assert.assertEquals(span.id, whoAmICall.getLastCallMetadata().get(YdbHeaders.TRACEPARENT)); transport.close(); } @@ -333,24 +348,10 @@ protected void complete(Listener listener) { @Test public void noSpanDoesNotSetTraceparentHeader() { EndpointRecord endpoint = new EndpointRecord("node", 2136, 42, "dc-a", null); + MockedCall.WhoAmICall whoAmICall = MockedCall.whoAmICall("i am node"); Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getListEndpointsMethod()), Mockito.any())) .thenReturn(MockedCall.discovery("self", endpoint)); - Metadata[] captured = new Metadata[1]; - MockedCall.WhoAmICall whoAmICall = new MockedCall.WhoAmICall(Runnable::run) { - @Override - public void start(Listener listener, Metadata headers) { - captured[0] = headers; - super.start(listener, headers); - } - - @Override - protected void complete(Listener listener) { - listener.onClose(io.grpc.Status.OK, new Metadata()); - } - }; - Mockito.when(discoveryChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getWhoAmIMethod()), Mockito.any())) - .thenReturn(whoAmICall); Mockito.when(transportChannel.newCall(Mockito.eq(DiscoveryServiceGrpc.getWhoAmIMethod()), Mockito.any())) .thenReturn(whoAmICall); @@ -368,9 +369,9 @@ protected void complete(Listener listener) { DiscoveryProtos.WhoAmIRequest.newBuilder().build() ).join(); - Assert.assertNotNull(captured[0]); - Assert.assertNull(captured[0].get(YdbHeaders.TRACEPARENT)); - Assert.assertEquals("test-trace-id-without-span", captured[0].get(YdbHeaders.TRACE_ID)); + Assert.assertNotNull(whoAmICall.getLastCallMetadata()); + Assert.assertNull(whoAmICall.getLastCallMetadata().get(YdbHeaders.TRACEPARENT)); + Assert.assertEquals("test-trace-id-without-span", whoAmICall.getLastCallMetadata().get(YdbHeaders.TRACE_ID)); transport.close(); } 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 dd92b391c..efb100316 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 @@ -30,7 +30,6 @@ import tech.ydb.core.grpc.GrpcTransport; import tech.ydb.core.grpc.GrpcTransportBuilder; import tech.ydb.core.grpc.YdbHeaders; -import tech.ydb.core.utils.Version; /** * @@ -107,7 +106,6 @@ public void defaultParams() { Metadata metadata = metadataCapture.getValue(); Assert.assertEquals("/Root", metadata.get(YdbHeaders.DATABASE)); - Assert.assertEquals("ydb-java-sdk/" + Version.getVersion().get(), metadata.get(YdbHeaders.BUILD_INFO)); Assert.assertNull(metadata.get(YdbHeaders.APPLICATION_NAME)); Assert.assertNull(metadata.get(YdbHeaders.CLIENT_PROCESS_ID)); } @@ -149,26 +147,10 @@ public void customHeadersTest() { Metadata metadata = metadataCapture.getValue(); Assert.assertEquals("/Root", metadata.get(YdbHeaders.DATABASE)); - Assert.assertEquals("ydb-java-sdk/" + Version.getVersion().get(), metadata.get(YdbHeaders.BUILD_INFO)); Assert.assertEquals("test-application", metadata.get(YdbHeaders.APPLICATION_NAME)); Assert.assertEquals("client-hostname", metadata.get(YdbHeaders.CLIENT_PROCESS_ID)); } - @Test - public void customBuildInfoTest() { - GrpcTransportBuilder builder = GrpcTransport.forHost(MOCKED_HOST, MOCKED_PORT, "/Root") - .withExtraBuildInfo("driver/1.0.0") - .withExtraBuildInfo("test-app/1.0.0"); - ManagedChannelFactory factory = ChannelFactoryLoader.load().buildFactory(builder); - - Assert.assertSame(channelMock, factory.newManagedChannel(MOCKED_HOST, MOCKED_PORT, null)); - channelStaticMock.verify(FOR_ADDRESS, Mockito.times(1)); - - String version = "ydb-java-sdk/" + Version.getVersion().get(); - Metadata metadata = metadataCapture.getValue(); - Assert.assertEquals(version + ";driver/1.0.0;test-app/1.0.0", metadata.get(YdbHeaders.BUILD_INFO)); - } - @Test public void customChannelInitializer() { GrpcTransportBuilder builder = GrpcTransport.forHost(MOCKED_HOST, MOCKED_PORT, "/Root") From 8b498828600f2b4d3fd99d2090b9061f0dece5e0 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Mon, 20 Jul 2026 10:59:28 +0100 Subject: [PATCH 2/3] Added Observability class with custom discovery buildInfo headers --- .../tech/ydb/core/impl/Observability.java | 40 +++++++++++++++++ .../tech/ydb/core/impl/YdbTransportImpl.java | 3 +- .../tech/ydb/core/impl/ObservabilityTest.java | 45 +++++++++++++++++++ .../ydb/core/impl/YdbTransportImplTest.java | 12 +++-- .../java/tech/ydb/query/impl/SessionPool.java | 2 + .../tech/ydb/table/impl/pool/SessionPool.java | 2 + 6 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/tech/ydb/core/impl/Observability.java create mode 100644 core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java diff --git a/core/src/main/java/tech/ydb/core/impl/Observability.java b/core/src/main/java/tech/ydb/core/impl/Observability.java new file mode 100644 index 000000000..8694fcabd --- /dev/null +++ b/core/src/main/java/tech/ydb/core/impl/Observability.java @@ -0,0 +1,40 @@ +package tech.ydb.core.impl; + +import com.google.common.annotations.VisibleForTesting; + +import tech.ydb.core.metrics.Meter; +import tech.ydb.core.tracing.NoopTracer; +import tech.ydb.core.tracing.Tracer; + +/** + * + * @author Aleksandr Gorshenin {@literal } + */ +public final class Observability { + public static final String TRACING_CHAIN = ";ydb-sdk-tracing/0.1.0"; + public static final String METRICS_CHAIN = ";ydb-sdk-metrics/0.1.0"; + + private static volatile boolean isTracingEnabled = false; + private static volatile boolean isMetricsEnabled = false; + + private Observability() { + } + + public static void reportTracingUsage(Tracer tracer) { + isTracingEnabled = isTracingEnabled || (tracer != NoopTracer.getInstance()); + } + + public static void reportMetricsUsage(Meter meter) { + isMetricsEnabled = isMetricsEnabled || (meter != Meter.NOOP); + } + + static String getDiscoveryBuildInfo(String base) { + return base + (isTracingEnabled ? TRACING_CHAIN : "") + (isMetricsEnabled ? METRICS_CHAIN : ""); + } + + @VisibleForTesting + static void reset() { + isTracingEnabled = false; + isMetricsEnabled = false; + } +} diff --git a/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java b/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java index de1d7fa01..a8bc6255a 100644 --- a/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java +++ b/core/src/main/java/tech/ydb/core/impl/YdbTransportImpl.java @@ -45,6 +45,7 @@ public YdbTransportImpl(GrpcTransportBuilder builder) { this.database = Strings.nullToEmpty(builder.getDatabase()); this.tracer = builder.getTracer(); + Observability.reportTracingUsage(this.tracer); logger.info("Create YDB transport with endpoint {} and {}", serverEndpoint, balancingSettings); @@ -174,7 +175,7 @@ public CompletableFuture handleEndpoints(List endpoints @Override public GrpcTransport createDiscoveryTransport() { - String buildInfo = baseBuildInfo; + String buildInfo = Observability.getDiscoveryBuildInfo(baseBuildInfo); return new FixedCallOptionsTransport( scheduler, callOptions, database, buildInfo, serverEndpoint, channelFactory ); diff --git a/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java b/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java new file mode 100644 index 000000000..024b1b4e2 --- /dev/null +++ b/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java @@ -0,0 +1,45 @@ +package tech.ydb.core.impl; + +import org.junit.Assert; +import org.junit.Test; + +import tech.ydb.core.metrics.Meter; +import tech.ydb.core.tracing.NoopTracer; +import tech.ydb.core.tracing.Span; +import tech.ydb.core.tracing.SpanKind; + +/** + * + * @author Aleksandr Gorshenin {@literal } + */ +public class ObservabilityTest { + private static final String BASE = "ydb-java-sdk/1.2.3"; + + @Test + public void baseTest() { + Observability.reset(); + + Assert.assertEquals(BASE, Observability.getDiscoveryBuildInfo(BASE)); + + // noop implementations ignored + Observability.reportMetricsUsage(Meter.NOOP); + Observability.reportTracingUsage(NoopTracer.getInstance()); + Assert.assertEquals(BASE, Observability.getDiscoveryBuildInfo(BASE)); + + Observability.reportMetricsUsage(new Meter() { }); + Assert.assertEquals(BASE + ";ydb-sdk-metrics/0.1.0", Observability.getDiscoveryBuildInfo(BASE)); + + Observability.reportTracingUsage((String spanName, SpanKind spanKind) -> Span.NOOP); + Assert.assertEquals( + BASE + ";ydb-sdk-tracing/0.1.0;ydb-sdk-metrics/0.1.0", + Observability.getDiscoveryBuildInfo(BASE) + ); + + Observability.reportMetricsUsage(Meter.NOOP); + Observability.reportTracingUsage(NoopTracer.getInstance()); + Assert.assertEquals( + BASE + ";ydb-sdk-tracing/0.1.0;ydb-sdk-metrics/0.1.0", + Observability.getDiscoveryBuildInfo(BASE) + ); + } +} \ No newline at end of file diff --git a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java index e79c0fb9a..c3b516401 100644 --- a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java +++ b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java @@ -33,6 +33,7 @@ import tech.ydb.core.operation.OperationBinder; import tech.ydb.core.tracing.NoopTracer; import tech.ydb.core.tracing.Span; +import tech.ydb.core.tracing.SpanKind; import tech.ydb.core.tracing.Tracer; import tech.ydb.core.utils.Version; import tech.ydb.proto.discovery.DiscoveryProtos; @@ -241,6 +242,9 @@ public void customSchedulerTest() { @Test public void buildInfoTest() { + Observability.reset(); + Observability.reportTracingUsage((String spanName, SpanKind spanKind) -> Span.NOOP); + MockedCall.DiscoveryCall discoveryCall = MockedCall.discovery("self", new EndpointRecord("node", 2136)); MockedCall.WhoAmICall whoAmICall = MockedCall.whoAmICall("i am node"); @@ -256,7 +260,8 @@ public void buildInfoTest() { .build(); String sdk = "ydb-java-sdk/" + Version.getVersion().get(); - String customVersion = sdk + ";driver/1.0.0;test-app/1.0.0"; + String custom = sdk + ";driver/1.0.0;test-app/1.0.0"; + String traced = custom + ";ydb-sdk-tracing/0.1.0"; Result call = whoAmI(transport).join(); @@ -264,8 +269,9 @@ public void buildInfoTest() { Assert.assertNotNull(discoveryCall.getLastCallMetadata()); Assert.assertNotNull(whoAmICall.getLastCallMetadata()); - Assert.assertEquals(customVersion, discoveryCall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); - Assert.assertEquals(customVersion, whoAmICall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); + // Only discovery calle use traced build info + Assert.assertEquals(traced, discoveryCall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); + Assert.assertEquals(custom, whoAmICall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); } @Test diff --git a/query/src/main/java/tech/ydb/query/impl/SessionPool.java b/query/src/main/java/tech/ydb/query/impl/SessionPool.java index a7743c079..dc11ac18c 100644 --- a/query/src/main/java/tech/ydb/query/impl/SessionPool.java +++ b/query/src/main/java/tech/ydb/query/impl/SessionPool.java @@ -22,6 +22,7 @@ import tech.ydb.core.StatusCode; import tech.ydb.core.UnexpectedResultException; import tech.ydb.core.grpc.GrpcReadStream; +import tech.ydb.core.impl.Observability; import tech.ydb.core.metrics.Meter; import tech.ydb.core.tracing.Span; import tech.ydb.core.utils.FutureTools; @@ -71,6 +72,7 @@ class SessionPool implements AutoCloseable { this.scheduler = scheduler; this.queue = new WaitingQueue<>(new Handler(rpc), maxSize); this.metrics = new PoolMetrics(meter, "query", poolName, queue, minSize); + Observability.reportMetricsUsage(meter); CleanerTask cleaner = new CleanerTask(idleDuration); this.cleanerFuture = scheduler.scheduleAtFixedRate( diff --git a/table/src/main/java/tech/ydb/table/impl/pool/SessionPool.java b/table/src/main/java/tech/ydb/table/impl/pool/SessionPool.java index cffad0d1b..a825f8c49 100644 --- a/table/src/main/java/tech/ydb/table/impl/pool/SessionPool.java +++ b/table/src/main/java/tech/ydb/table/impl/pool/SessionPool.java @@ -22,6 +22,7 @@ import tech.ydb.core.Status; import tech.ydb.core.StatusCode; import tech.ydb.core.UnexpectedResultException; +import tech.ydb.core.impl.Observability; import tech.ydb.core.metrics.Meter; import tech.ydb.core.utils.FutureTools; import tech.ydb.table.Session; @@ -63,6 +64,7 @@ public SessionPool(Clock clock, TableRpc rpc, boolean keepQueryText, SessionPool this.scheduler = rpc.getScheduler(); this.queue = new WaitingQueue<>(new Handler(rpc, keepQueryText), options.getMaxSize()); this.metrics = new PoolMetrics(meter, "table", poolName, queue, this.minSize); + Observability.reportMetricsUsage(meter); KeepAliveTask keepAlive = new KeepAliveTask(options); this.keepAliveFuture = scheduler.scheduleAtFixedRate( From 804db6df1e8a690e0ce00528268c016ef4bf4f41 Mon Sep 17 00:00:00 2001 From: Alexandr Gorshenin Date: Mon, 20 Jul 2026 11:41:04 +0100 Subject: [PATCH 3/3] Small fixes --- core/src/main/java/tech/ydb/core/impl/Observability.java | 8 ++++++-- .../test/java/tech/ydb/core/impl/ObservabilityTest.java | 8 ++++++-- .../java/tech/ydb/core/impl/YdbTransportImplTest.java | 5 ++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/tech/ydb/core/impl/Observability.java b/core/src/main/java/tech/ydb/core/impl/Observability.java index 8694fcabd..9a1a6f2b7 100644 --- a/core/src/main/java/tech/ydb/core/impl/Observability.java +++ b/core/src/main/java/tech/ydb/core/impl/Observability.java @@ -21,11 +21,15 @@ private Observability() { } public static void reportTracingUsage(Tracer tracer) { - isTracingEnabled = isTracingEnabled || (tracer != NoopTracer.getInstance()); + if (tracer != NoopTracer.getInstance()) { + isTracingEnabled = true; + } } public static void reportMetricsUsage(Meter meter) { - isMetricsEnabled = isMetricsEnabled || (meter != Meter.NOOP); + if (meter != Meter.NOOP) { + isMetricsEnabled = true; + } } static String getDiscoveryBuildInfo(String base) { diff --git a/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java b/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java index 024b1b4e2..0274dc8fa 100644 --- a/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java +++ b/core/src/test/java/tech/ydb/core/impl/ObservabilityTest.java @@ -1,5 +1,6 @@ package tech.ydb.core.impl; +import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -15,10 +16,13 @@ public class ObservabilityTest { private static final String BASE = "ydb-java-sdk/1.2.3"; - @Test - public void baseTest() { + @After + public void reset() { Observability.reset(); + } + @Test + public void baseTest() { Assert.assertEquals(BASE, Observability.getDiscoveryBuildInfo(BASE)); // noop implementations ignored diff --git a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java index c3b516401..d708ab576 100644 --- a/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java +++ b/core/src/test/java/tech/ydb/core/impl/YdbTransportImplTest.java @@ -70,6 +70,7 @@ public void setUp() throws InterruptedException { @After public void shutdown() { testScheduler.shutdown(); + Observability.reset(); } private CompletableFuture> whoAmI(GrpcTransport transport) { @@ -269,9 +270,11 @@ public void buildInfoTest() { Assert.assertNotNull(discoveryCall.getLastCallMetadata()); Assert.assertNotNull(whoAmICall.getLastCallMetadata()); - // Only discovery calle use traced build info + // Only discovery calls use traced build info Assert.assertEquals(traced, discoveryCall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); Assert.assertEquals(custom, whoAmICall.getLastCallMetadata().get(YdbHeaders.BUILD_INFO)); + + transport.close(); } @Test