From 13615f042642ec1b4cd740fd5913bc8bc535c37a Mon Sep 17 00:00:00 2001 From: Utsab Date: Sat, 29 Mar 2025 16:28:09 +0530 Subject: [PATCH] added weighted node selction --- pom.xml | 2 +- ranger-client/pom.xml | 2 +- ranger-core/pom.xml | 2 +- .../core/finder/BaseServiceFinderBuilder.java | 11 ++++-- .../WeightedRandomServiceNodeSelector.java | 36 +++++++++++++++++++ .../ranger/core/model/ServiceNode.java | 14 ++++++++ .../core/serviceprovider/ServiceProvider.java | 14 ++++++++ .../core/finderhub/ServiceFinderHubTest.java | 3 +- ranger-discovery-bundle/pom.xml | 2 +- ranger-drove-client/pom.xml | 2 +- .../drove/AbstractRangerDroveHubClient.java | 4 +-- ranger-drove/pom.xml | 2 +- .../serde/DroveResponseDataDeserializer.java | 3 ++ ranger-http-client/pom.xml | 2 +- .../http/AbstractRangerHttpHubClient.java | 4 +-- ranger-http-model/pom.xml | 2 +- ranger-http/pom.xml | 2 +- ranger-hub-server-bundle/pom.xml | 2 +- ranger-server-bundle/pom.xml | 2 +- ranger-server-common/pom.xml | 2 +- ranger-server/pom.xml | 2 +- ranger-zk-client/pom.xml | 2 +- .../client/zk/ShardedRangerZKHubClient.java | 4 +-- .../client/zk/UnshardedRangerZKHubClient.java | 4 +-- ranger-zookeeper/pom.xml | 2 +- 25 files changed, 100 insertions(+), 27 deletions(-) create mode 100644 ranger-core/src/main/java/io/appform/ranger/core/finder/nodeselector/WeightedRandomServiceNodeSelector.java diff --git a/pom.xml b/pom.xml index 11d1d18d..f148fbd2 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ io.appform.ranger ranger pom - 1.1-RC5 + 1.2-RC5 Ranger https://github.com/appform-io/ranger Service Discovery for Java diff --git a/ranger-client/pom.xml b/ranger-client/pom.xml index 064f474b..1eeb8203 100644 --- a/ranger-client/pom.xml +++ b/ranger-client/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-core/pom.xml b/ranger-core/pom.xml index f4605de4..901fc29f 100644 --- a/ranger-core/pom.xml +++ b/ranger-core/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-core/src/main/java/io/appform/ranger/core/finder/BaseServiceFinderBuilder.java b/ranger-core/src/main/java/io/appform/ranger/core/finder/BaseServiceFinderBuilder.java index 23859a41..de9a9746 100644 --- a/ranger-core/src/main/java/io/appform/ranger/core/finder/BaseServiceFinderBuilder.java +++ b/ranger-core/src/main/java/io/appform/ranger/core/finder/BaseServiceFinderBuilder.java @@ -18,10 +18,15 @@ import com.google.common.base.Preconditions; import com.google.common.collect.Lists; -import io.appform.ranger.core.finder.nodeselector.RandomServiceNodeSelector; +import io.appform.ranger.core.finder.nodeselector.WeightedRandomServiceNodeSelector; import io.appform.ranger.core.finder.serviceregistry.ServiceRegistryUpdater; import io.appform.ranger.core.finder.serviceregistry.signal.ScheduledRegistryUpdateSignal; -import io.appform.ranger.core.model.*; +import io.appform.ranger.core.model.Deserializer; +import io.appform.ranger.core.model.NodeDataSource; +import io.appform.ranger.core.model.Service; +import io.appform.ranger.core.model.ServiceNodeSelector; +import io.appform.ranger.core.model.ServiceRegistry; +import io.appform.ranger.core.model.ShardSelector; import io.appform.ranger.core.signals.Signal; import lombok.extern.slf4j.Slf4j; import lombok.val; @@ -48,7 +53,7 @@ public abstract class BaseServiceFinderBuilder protected boolean disablePushUpdaters; protected D deserializer; protected ShardSelector shardSelector; - protected ServiceNodeSelector nodeSelector = new RandomServiceNodeSelector<>(); + protected ServiceNodeSelector nodeSelector = new WeightedRandomServiceNodeSelector<>(); protected final List> additionalRefreshSignals = new ArrayList<>(); protected final List> startSignalHandlers = Lists.newArrayList(); protected final List> stopSignalHandlers = Lists.newArrayList(); diff --git a/ranger-core/src/main/java/io/appform/ranger/core/finder/nodeselector/WeightedRandomServiceNodeSelector.java b/ranger-core/src/main/java/io/appform/ranger/core/finder/nodeselector/WeightedRandomServiceNodeSelector.java new file mode 100644 index 00000000..48a48566 --- /dev/null +++ b/ranger-core/src/main/java/io/appform/ranger/core/finder/nodeselector/WeightedRandomServiceNodeSelector.java @@ -0,0 +1,36 @@ +package io.appform.ranger.core.finder.nodeselector; + +import io.appform.ranger.core.model.ServiceNode; +import io.appform.ranger.core.model.ServiceNodeSelector; + +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +public class WeightedRandomServiceNodeSelector implements ServiceNodeSelector { + + @Override + public ServiceNode select(List> serviceNodes) { + if (serviceNodes == null || serviceNodes.isEmpty()) { + throw new IllegalArgumentException("Service nodes list cannot be empty"); + } + + double totalWeight = 0.0; + double[] cumulativeWeights = new double[serviceNodes.size()]; + + for (int i = 0; i < serviceNodes.size(); i++) { + totalWeight += serviceNodes.get(i).getWeight(); + cumulativeWeights[i] = totalWeight; + } + + double randomValue = ThreadLocalRandom.current().nextDouble(totalWeight); + + + int index = Arrays.binarySearch(cumulativeWeights, randomValue); + if (index < 0) { + index = -index - 1; + } + + return serviceNodes.get(index); + } +} diff --git a/ranger-core/src/main/java/io/appform/ranger/core/model/ServiceNode.java b/ranger-core/src/main/java/io/appform/ranger/core/model/ServiceNode.java index 05a41df1..7707488f 100644 --- a/ranger-core/src/main/java/io/appform/ranger/core/model/ServiceNode.java +++ b/ranger-core/src/main/java/io/appform/ranger/core/model/ServiceNode.java @@ -28,6 +28,8 @@ public class ServiceNode { private String host; private int port; + @Builder.Default + private double weight = 1.0; private T nodeData; @Builder.Default private HealthcheckStatus healthcheckStatus = HealthcheckStatus.healthy; @@ -37,6 +39,18 @@ public class ServiceNode { @Builder.Default private String portScheme = PortSchemes.HTTP; + public ServiceNode(final String host, final int port, final T nodeData, final HealthcheckStatus healthcheckStatus, + final long lastUpdatedTimeStamp, + final String portScheme) { + this.host = host; + this.port = port; + this.weight = 1; + this.nodeData = nodeData; + this.healthcheckStatus = healthcheckStatus; + this.lastUpdatedTimeStamp = lastUpdatedTimeStamp; + this.portScheme = portScheme; + } + public String representation() { return String.format("%s:%d", host, port); } diff --git a/ranger-core/src/main/java/io/appform/ranger/core/serviceprovider/ServiceProvider.java b/ranger-core/src/main/java/io/appform/ranger/core/serviceprovider/ServiceProvider.java index 08f7db50..8985625c 100644 --- a/ranger-core/src/main/java/io/appform/ranger/core/serviceprovider/ServiceProvider.java +++ b/ranger-core/src/main/java/io/appform/ranger/core/serviceprovider/ServiceProvider.java @@ -26,6 +26,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.lang.management.ManagementFactory; import java.util.Collections; import java.util.List; @@ -69,10 +70,23 @@ private void handleHealthUpdate(HealthcheckResult result) { log.debug("No update to health state of node. Skipping data source update."); return; } + + serviceNode.setWeight(computeWeight());// to be updated serviceNode.setHealthcheckStatus(result.getStatus()); serviceNode.setLastUpdatedTimeStamp(result.getUpdatedTime()); dataSink.updateState(serializer, serviceNode); log.debug("Updated node with health check result: {}", result); } + private double computeWeight() { + double T = 60000; // Midpoint at 5 minutes + double S = 30000; // Scaling factor + + return 1.0 / (1.0 + Math.exp(-( (getInstanceUptime() - T) / S ))); + } + + private long getInstanceUptime() { + return ManagementFactory.getRuntimeMXBean().getUptime(); + } + } diff --git a/ranger-core/src/test/java/io/appform/ranger/core/finderhub/ServiceFinderHubTest.java b/ranger-core/src/test/java/io/appform/ranger/core/finderhub/ServiceFinderHubTest.java index 3d1d1d36..caaa9965 100644 --- a/ranger-core/src/test/java/io/appform/ranger/core/finderhub/ServiceFinderHubTest.java +++ b/ranger-core/src/test/java/io/appform/ranger/core/finderhub/ServiceFinderHubTest.java @@ -186,7 +186,8 @@ private static class TestNodeDataSource implements NodeDataSource>> refresh(Deserializer deserializer) { val list = new ArrayList>(); - list.add(new ServiceNode<>("HOST", 0, TestNodeData.builder().shardId(1).build(), HealthcheckStatus.healthy, Long.MAX_VALUE, "HTTP")); + list.add(new ServiceNode<>("HOST", 0, 1f, TestNodeData.builder().shardId(1).build(), + HealthcheckStatus.healthy, Long.MAX_VALUE, "HTTP")); return Optional.of(list); } diff --git a/ranger-discovery-bundle/pom.xml b/ranger-discovery-bundle/pom.xml index 58bf1bcd..e7e8d82c 100644 --- a/ranger-discovery-bundle/pom.xml +++ b/ranger-discovery-bundle/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-drove-client/pom.xml b/ranger-drove-client/pom.xml index 155cbbd4..0a70ca39 100644 --- a/ranger-drove-client/pom.xml +++ b/ranger-drove-client/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-drove-client/src/main/java/io/appform/ranger/client/drove/AbstractRangerDroveHubClient.java b/ranger-drove-client/src/main/java/io/appform/ranger/client/drove/AbstractRangerDroveHubClient.java index b205f9d8..80c0c753 100644 --- a/ranger-drove-client/src/main/java/io/appform/ranger/client/drove/AbstractRangerDroveHubClient.java +++ b/ranger-drove-client/src/main/java/io/appform/ranger/client/drove/AbstractRangerDroveHubClient.java @@ -16,7 +16,7 @@ package io.appform.ranger.client.drove; import io.appform.ranger.client.AbstractRangerHubClient; -import io.appform.ranger.core.finder.nodeselector.RandomServiceNodeSelector; +import io.appform.ranger.core.finder.nodeselector.WeightedRandomServiceNodeSelector; import io.appform.ranger.core.finderhub.ServiceDataSource; import io.appform.ranger.core.finderhub.ServiceFinderHub; import io.appform.ranger.core.model.ServiceNodeSelector; @@ -42,7 +42,7 @@ public abstract class AbstractRangerDroveHubClient nodeSelector = new RandomServiceNodeSelector<>(); + private final ServiceNodeSelector nodeSelector = new WeightedRandomServiceNodeSelector<>(); @Override protected ServiceDataSource getDefaultDataSource() { diff --git a/ranger-drove/pom.xml b/ranger-drove/pom.xml index de64fb73..7a3f0350 100644 --- a/ranger-drove/pom.xml +++ b/ranger-drove/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 1.30 diff --git a/ranger-drove/src/main/java/io/appform/ranger/drove/serde/DroveResponseDataDeserializer.java b/ranger-drove/src/main/java/io/appform/ranger/drove/serde/DroveResponseDataDeserializer.java index 53441f4a..b586d75d 100644 --- a/ranger-drove/src/main/java/io/appform/ranger/drove/serde/DroveResponseDataDeserializer.java +++ b/ranger-drove/src/main/java/io/appform/ranger/drove/serde/DroveResponseDataDeserializer.java @@ -22,7 +22,9 @@ import lombok.extern.slf4j.Slf4j; import lombok.val; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -44,6 +46,7 @@ public final List> deserialize(List appInfo) { } return new ServiceNode<>(endpoint.getHost(), endpoint.getPort(), + 1, info, HealthcheckStatus.healthy, currTime, diff --git a/ranger-http-client/pom.xml b/ranger-http-client/pom.xml index 048a55f5..849a848d 100644 --- a/ranger-http-client/pom.xml +++ b/ranger-http-client/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-http-client/src/main/java/io/appform/ranger/client/http/AbstractRangerHttpHubClient.java b/ranger-http-client/src/main/java/io/appform/ranger/client/http/AbstractRangerHttpHubClient.java index 5dd6910b..63466445 100644 --- a/ranger-http-client/src/main/java/io/appform/ranger/client/http/AbstractRangerHttpHubClient.java +++ b/ranger-http-client/src/main/java/io/appform/ranger/client/http/AbstractRangerHttpHubClient.java @@ -16,7 +16,7 @@ package io.appform.ranger.client.http; import io.appform.ranger.client.AbstractRangerHubClient; -import io.appform.ranger.core.finder.nodeselector.RandomServiceNodeSelector; +import io.appform.ranger.core.finder.nodeselector.WeightedRandomServiceNodeSelector; import io.appform.ranger.core.finderhub.ServiceDataSource; import io.appform.ranger.core.finderhub.ServiceFinderHub; import io.appform.ranger.core.model.ServiceNodeSelector; @@ -46,7 +46,7 @@ public abstract class AbstractRangerHttpHubClient httpClient; @Builder.Default - private final ServiceNodeSelector nodeSelector = new RandomServiceNodeSelector<>(); + private final ServiceNodeSelector nodeSelector = new WeightedRandomServiceNodeSelector<>(); @Override protected ServiceDataSource getDefaultDataSource() { diff --git a/ranger-http-model/pom.xml b/ranger-http-model/pom.xml index 091dc08c..436d9ae3 100644 --- a/ranger-http-model/pom.xml +++ b/ranger-http-model/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-http/pom.xml b/ranger-http/pom.xml index 54a6eb2d..69243068 100644 --- a/ranger-http/pom.xml +++ b/ranger-http/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-hub-server-bundle/pom.xml b/ranger-hub-server-bundle/pom.xml index 6ec8bbcd..a90ae07a 100644 --- a/ranger-hub-server-bundle/pom.xml +++ b/ranger-hub-server-bundle/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 diff --git a/ranger-server-bundle/pom.xml b/ranger-server-bundle/pom.xml index a50ddef8..0712ea58 100644 --- a/ranger-server-bundle/pom.xml +++ b/ranger-server-bundle/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-server-common/pom.xml b/ranger-server-common/pom.xml index ceeefd82..b0c51bef 100644 --- a/ranger-server-common/pom.xml +++ b/ranger-server-common/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-server/pom.xml b/ranger-server/pom.xml index d71666ab..7b4cf7f4 100644 --- a/ranger-server/pom.xml +++ b/ranger-server/pom.xml @@ -22,7 +22,7 @@ io.appform.ranger ranger - 1.1-RC5 + 1.2-RC5 ranger-server diff --git a/ranger-zk-client/pom.xml b/ranger-zk-client/pom.xml index 907a8343..cd14b549 100644 --- a/ranger-zk-client/pom.xml +++ b/ranger-zk-client/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0 diff --git a/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/ShardedRangerZKHubClient.java b/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/ShardedRangerZKHubClient.java index 55af8445..7a8fd57c 100644 --- a/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/ShardedRangerZKHubClient.java +++ b/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/ShardedRangerZKHubClient.java @@ -15,7 +15,7 @@ */ package io.appform.ranger.client.zk; -import io.appform.ranger.core.finder.nodeselector.RandomServiceNodeSelector; +import io.appform.ranger.core.finder.nodeselector.WeightedRandomServiceNodeSelector; import io.appform.ranger.core.finder.serviceregistry.MapBasedServiceRegistry; import io.appform.ranger.core.finder.shardselector.MatchingShardSelector; import io.appform.ranger.core.finderhub.ServiceFinderFactory; @@ -36,7 +36,7 @@ public class ShardedRangerZKHubClient private final ShardSelector> shardSelector = new MatchingShardSelector<>(); @Builder.Default - private final ServiceNodeSelector nodeSelector = new RandomServiceNodeSelector<>(); + private final ServiceNodeSelector nodeSelector = new WeightedRandomServiceNodeSelector<>(); @Override protected ServiceFinderFactory> getFinderFactory() { diff --git a/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/UnshardedRangerZKHubClient.java b/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/UnshardedRangerZKHubClient.java index 2b3eea83..59b8a6f9 100644 --- a/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/UnshardedRangerZKHubClient.java +++ b/ranger-zk-client/src/main/java/io/appform/ranger/client/zk/UnshardedRangerZKHubClient.java @@ -15,7 +15,7 @@ */ package io.appform.ranger.client.zk; -import io.appform.ranger.core.finder.nodeselector.RandomServiceNodeSelector; +import io.appform.ranger.core.finder.nodeselector.WeightedRandomServiceNodeSelector; import io.appform.ranger.core.finder.serviceregistry.ListBasedServiceRegistry; import io.appform.ranger.core.finder.shardselector.ListShardSelector; import io.appform.ranger.core.finderhub.ServiceFinderFactory; @@ -36,7 +36,7 @@ public class UnshardedRangerZKHubClient private final ShardSelector> shardSelector = new ListShardSelector<>(); @Builder.Default - private final ServiceNodeSelector nodeSelector = new RandomServiceNodeSelector<>(); + private final ServiceNodeSelector nodeSelector = new WeightedRandomServiceNodeSelector<>(); @Override protected ServiceFinderFactory> getFinderFactory() { diff --git a/ranger-zookeeper/pom.xml b/ranger-zookeeper/pom.xml index d1e9ad24..d8d01720 100644 --- a/ranger-zookeeper/pom.xml +++ b/ranger-zookeeper/pom.xml @@ -21,7 +21,7 @@ ranger io.appform.ranger - 1.1-RC5 + 1.2-RC5 4.0.0