From 3a8a21e1592b013fb77312abd917a6861f5d3674 Mon Sep 17 00:00:00 2001 From: Igor Melnichenko Date: Wed, 5 Aug 2026 01:32:12 +0300 Subject: [PATCH] Tolerate endpoints without a location when detecting the local DC detectLocalDC grouped endpoints with Collectors.groupingBy on EndpointRecord::getLocation, which throws NullPointerException for a null key. The endpoint that a transport bootstraps with has no location: it is built by getDiscoveryEndpoint from the connection string. So YdbTransportImpl.start seeded the pool with that endpoint and blew up before reaching the "fewer than two datacenters" guard, making BalancingSettings.detectLocalDs unusable in fallback mode. Filter out endpoints with no location - they say nothing about datacenters - and treat a null endpoint list as "no local DC". --- .../tech/ydb/core/impl/pool/PriorityPicker.java | 4 ++++ .../ydb/core/impl/pool/PriorityPickerTest.java | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/core/src/main/java/tech/ydb/core/impl/pool/PriorityPicker.java b/core/src/main/java/tech/ydb/core/impl/pool/PriorityPicker.java index 2aed6ca49..a401851b2 100644 --- a/core/src/main/java/tech/ydb/core/impl/pool/PriorityPicker.java +++ b/core/src/main/java/tech/ydb/core/impl/pool/PriorityPicker.java @@ -67,8 +67,12 @@ static String getLocationFromConfig(String preferable, String selfLocation) { @VisibleForTesting static String detectLocalDC(List endpoints, Ticker ticker) { + // Endpoints without a known location carry no information about datacenters. They must be + // filtered out because groupingBy rejects a null key - the endpoint the transport bootstraps + // with has no location until the first discovery completes. Map> dcLocationToNodes = endpoints .stream() + .filter(endpoint -> endpoint.getLocation() != null && !endpoint.getLocation().isEmpty()) .collect(Collectors.groupingBy(EndpointRecord::getLocation)); if (dcLocationToNodes.size() < 2) { diff --git a/core/src/test/java/tech/ydb/core/impl/pool/PriorityPickerTest.java b/core/src/test/java/tech/ydb/core/impl/pool/PriorityPickerTest.java index b1da65ad4..037056f3d 100644 --- a/core/src/test/java/tech/ydb/core/impl/pool/PriorityPickerTest.java +++ b/core/src/test/java/tech/ydb/core/impl/pool/PriorityPickerTest.java @@ -9,6 +9,7 @@ import javax.net.ServerSocketFactory; +import com.google.common.base.Ticker; import org.junit.Assert; import org.junit.Test; @@ -56,6 +57,19 @@ public void detectLocalDCFallbackTest() { Assert.assertEquals(0, ignoreSelfLocation.getEndpointPriority("DC3")); } + @Test + public void detectLocalDCWithoutLocationsTest() { + // the endpoint a transport bootstraps with has no location until the first discovery completes + List bootstrap = Collections.singletonList(new EndpointRecord("localhost", 2136)); + PriorityPicker picker = PriorityPicker.from(BalancingSettings.detectLocalDs(), null, bootstrap); + + Assert.assertEquals(0, picker.getEndpointPriority("DC1")); + Assert.assertEquals(0, picker.getEndpointPriority(null)); + + Assert.assertNull(PriorityPicker.detectLocalDC(bootstrap, Ticker.systemTicker())); + Assert.assertNull(PriorityPicker.detectLocalDC(Collections.emptyList(), Ticker.systemTicker())); + } + @Test public void detectLocalDCTest() { TestTicker testTicker = new TestTicker(