This is an automated email from the ASF dual-hosted git repository.
gian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 6f0271a043e Improve connection count server selection strategy for
tie-breaking cases (#17764)
6f0271a043e is described below
commit 6f0271a043e3fd100d67793d2a7aa4e98296c18d
Author: Michal Ciesielczyk <[email protected]>
AuthorDate: Wed Apr 2 18:08:02 2025 +0200
Improve connection count server selection strategy for tie-breaking cases
(#17764)
Previously, when multiple servers had the same number of active
connections, the balancer would always select the same server
deterministically. This led to uneven distribution in certain cases.
This commit introduces a simple tie-breaking mechanism to ensure a more
balanced distribution.
---
.../ConnectionCountServerSelectorStrategy.java | 6 +-
.../ConnectionCountServerSelectorStrategyTest.java | 115 +++++++++++++++++++++
2 files changed, 120 insertions(+), 1 deletion(-)
diff --git
a/server/src/main/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategy.java
b/server/src/main/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategy.java
index 0df1c5984a1..262b23701df 100644
---
a/server/src/main/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategy.java
+++
b/server/src/main/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategy.java
@@ -30,11 +30,15 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
public class ConnectionCountServerSelectorStrategy implements
ServerSelectorStrategy
{
private static final Comparator<QueryableDruidServer> COMPARATOR =
- Comparator.comparingInt(s -> ((DirectDruidClient)
s.getQueryRunner()).getNumOpenConnections());
+ Comparator.comparingDouble(s ->
+ ((DirectDruidClient)
s.getQueryRunner()).getNumOpenConnections()
+ + ThreadLocalRandom.current().nextDouble()
+ );
@Nullable
@Override
diff --git
a/server/src/test/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategyTest.java
b/server/src/test/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategyTest.java
new file mode 100644
index 00000000000..d914e9e5943
--- /dev/null
+++
b/server/src/test/java/org/apache/druid/client/selector/ConnectionCountServerSelectorStrategyTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.druid.client.selector;
+
+import org.apache.druid.client.DirectDruidClient;
+import org.apache.druid.client.DruidServer;
+import org.apache.druid.client.QueryableDruidServer;
+import org.apache.druid.java.util.common.DateTimes;
+import org.apache.druid.java.util.common.Intervals;
+import org.apache.druid.server.coordination.ServerType;
+import org.apache.druid.timeline.DataSegment;
+import org.apache.druid.timeline.partition.NumberedShardSpec;
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class ConnectionCountServerSelectorStrategyTest
+{
+ @Test
+ public void testDifferentConnectionCount()
+ {
+ QueryableDruidServer s1 = mockServer("test1", 2);
+ QueryableDruidServer s2 = mockServer("test2", 1);
+ QueryableDruidServer s3 = mockServer("test3", 4);
+ ServerSelector serverSelector = initSelector(s1, s2, s3);
+
+ for (int i = 0; i < 100; ++i) {
+ Assert.assertEquals(s2, serverSelector.pick(null));
+ }
+ }
+
+ @Test
+ public void testBalancerTieBreaking()
+ {
+ QueryableDruidServer s1 = mockServer("test1", 100);
+ QueryableDruidServer s2 = mockServer("test2", 100);
+ ServerSelector serverSelector = initSelector(s1, s2);
+
+ Set<String> pickedServers = new HashSet<>();
+ for (int i = 0; i < 100; ++i) {
+ pickedServers.add(serverSelector.pick(null).getServer().getName());
+ }
+ Assert.assertTrue(
+ "Multiple servers should be selected when the number of connections is
equal.",
+ pickedServers.size() > 1
+ );
+ }
+
+ private QueryableDruidServer mockServer(String name, int openConnections)
+ {
+ DirectDruidClient client = EasyMock.createMock(DirectDruidClient.class);
+
EasyMock.expect(client.getNumOpenConnections()).andReturn(openConnections).anyTimes();
+ EasyMock.replay(client);
+ return new QueryableDruidServer(
+ new DruidServer(
+ name,
+ "localhost",
+ null,
+ 0,
+ ServerType.HISTORICAL,
+ DruidServer.DEFAULT_TIER,
+ 0
+ ), client
+ );
+ }
+
+ private ServerSelector initSelector(QueryableDruidServer... servers)
+ {
+ TierSelectorStrategy strategy = new
HighestPriorityTierSelectorStrategy(new
ConnectionCountServerSelectorStrategy());
+ ServerSelector selector = new ServerSelector(
+ new DataSegment(
+ "test",
+ Intervals.of("2025-01-01/2025-01-02"),
+ DateTimes.of("2025-01-01").toString(),
+ new HashMap<>(),
+ new ArrayList<>(),
+ new ArrayList<>(),
+ new NumberedShardSpec(0, 0),
+ 0,
+ 0L
+ ), strategy
+ );
+ List<QueryableDruidServer> serverList = new
ArrayList<>(Arrays.asList(servers));
+ Collections.shuffle(serverList);
+ for (QueryableDruidServer server : serverList) {
+ selector.addServerAndUpdateSegment(server, selector.getSegment());
+ }
+ return selector;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]