From a0b3288fa93e8add36933b689a9d76fd2a1fb289 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Mon, 22 Apr 2024 15:14:10 -0600 Subject: [PATCH 1/6] WIP --- .../client/api/DefaultMetadata.java | 43 +++++++++++++++ .../io/servicetalk/client/api/Metadata.java | 52 +++++++++++++++++++ .../client/api/ServiceDiscovererEvent.java | 14 +++++ 3 files changed, 109 insertions(+) create mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java create mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java new file mode 100644 index 0000000000..b7357d702c --- /dev/null +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java @@ -0,0 +1,43 @@ +package io.servicetalk.client.api; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +final class DefaultMetadata implements Metadata { + + private static Logger LOGGER = LoggerFactory.getLogger(DefaultMetadata.class); + + static final Metadata EMPTY_METADATA = new EmptyMetadata(); + + private final Map values; + + DefaultMetadata(final Map values) { + this.values = requireNonNull(values, "values"); + } + + @Override + public T get(Key key) { + Object value = values.get(key.name()); + if (value == null) { + return key.defaultValue(); + } else if (!key.clazz().isInstance(value)) { + LOGGER.info("Metadata entry with name {} was found but didn't contain the expected type. Found: {}, " + + "expected: {}", key.name(), value.getClass(), key.clazz()); + return key.defaultValue(); + } else { + return key.clazz().cast(value); + } + } + + // A simple implementation of the always-empty Metadata. + private static final class EmptyMetadata implements Metadata { + @Override + public T get(Key key) { + return key.defaultValue(); + } + } +} diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java new file mode 100644 index 0000000000..fde1e6c706 --- /dev/null +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java @@ -0,0 +1,52 @@ +package io.servicetalk.client.api; + +import static java.util.Objects.requireNonNull; + +public interface Metadata { + + final class Key { + + private final Class clazz; + private final String name; + private final T defaultValue; + + public Key(final Class clazz, final String name, final T defaultValue) { + this.clazz = requireNonNull(clazz, "clazz"); + this.name = requireNonNull(name, "name"); + this.defaultValue = requireNonNull(defaultValue, "defaultValue"); + } + + String name() { + return name; + } + + Class clazz() { + return clazz; + } + + T defaultValue() { + return defaultValue; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != Key.class) { + return false; + } + Key other = (Key) obj; + return other.clazz.equals(clazz) && other.name.equals(name); + } + + @Override + public String toString() { + return "Key<" + clazz.getName() + ">(" + name + ", " + defaultValue + ")"; + } + } + + T get(Key key); +} diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java index c08ecbbf6d..2a5dfd2077 100644 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java @@ -39,6 +39,20 @@ public interface ServiceDiscovererEvent { */ Status status(); + /** + * Meta-data associated with the specified address. + *

+ * Metadata is data that is not strictly necessary for load balancing purposes but can be useful for making more + * intelligent decisions. As described in {@link #address()}, updates to an addresses meta-data can be accomplished + * by sending another {@link ServiceDiscovererEvent} with the same address and {@link Status} but with different + * metadata. This also means that updates to status must also propagate the desired meta-data state or the empty + * meta-data state is assumed. + * @return + */ + default Metadata metadata() { + return DefaultMetadata.EMPTY_METADATA; + } + /** * Status provided by the {@link ServiceDiscoverer} system that guides the actions of {@link LoadBalancer} upon the * bound {@link ServiceDiscovererEvent#address()} (via {@link ServiceDiscovererEvent}). From 6d216ea3e6e06dc136738a30e5a3a63bdced95e9 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Tue, 23 Apr 2024 10:54:25 -0600 Subject: [PATCH 2/6] Demonstrate the use of weight --- .../java/io/servicetalk/client/api/MetadataKeys.java | 12 ++++++++++++ .../io/servicetalk/loadbalancer/DefaultHost.java | 8 ++++++++ .../loadbalancer/DefaultLoadBalancer.java | 10 ++++++---- .../main/java/io/servicetalk/loadbalancer/Host.java | 6 ++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java new file mode 100644 index 0000000000..958e719fe6 --- /dev/null +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java @@ -0,0 +1,12 @@ +package io.servicetalk.client.api; + +public final class MetadataKeys { + + /** + * Metadata that describes the relative weight of an endpoint. + */ + public static final Metadata.Key WEIGHT = new Metadata.Key<>(Double.class, "endpoint.weight", 1.0); + private MetadataKeys() { + // no instances. + } +} diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java index e10a81be09..61738443a4 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultHost.java @@ -79,6 +79,7 @@ private enum State { private final String lbDescription; private final Addr address; + private final double weight; @Nullable private final HealthCheckConfig healthCheckConfig; @Nullable @@ -91,12 +92,14 @@ private enum State { private volatile ConnState connState = new ConnState(emptyList(), State.ACTIVE, 0, null); DefaultHost(final String lbDescription, final Addr address, + final double weight, final ConnectionPoolStrategy connectionPoolStrategy, final ConnectionFactory connectionFactory, final HostObserver hostObserver, final @Nullable HealthCheckConfig healthCheckConfig, final @Nullable HealthIndicator healthIndicator) { this.lbDescription = requireNonNull(lbDescription, "lbDescription"); this.address = requireNonNull(address, "address"); + this.weight = weight; this.healthIndicator = healthIndicator; this.connectionPoolStrategy = requireNonNull(connectionPoolStrategy, "connectionPoolStrategy"); requireNonNull(connectionFactory, "connectionFactory"); @@ -113,6 +116,11 @@ public Addr address() { return address; } + @Override + public double weight() { + return weight; + } + @Override public boolean markActiveIfNotClosed() { final ConnState oldState = connStateUpdater.getAndUpdate(this, oldConnState -> { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java index b389b9414c..3b9897d5b2 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java @@ -17,6 +17,7 @@ import io.servicetalk.client.api.ConnectionFactory; import io.servicetalk.client.api.LoadBalancedConnection; +import io.servicetalk.client.api.MetadataKeys; import io.servicetalk.client.api.NoActiveHostException; import io.servicetalk.client.api.NoAvailableHostException; import io.servicetalk.client.api.ServiceDiscovererEvent; @@ -52,6 +53,7 @@ import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_NOT_READY_EVENT; import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_READY_EVENT; +import static io.servicetalk.client.api.MetadataKeys.WEIGHT; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE; @@ -309,7 +311,7 @@ private void sequentialOnNext(Collection createHost(ResolvedAddress addr) { + private Host createHost(ResolvedAddress addr, double weight) { final LoadBalancerObserver.HostObserver hostObserver = loadBalancerObserver.hostObserver(addr); // All hosts will share the health check config of the parent load balancer. final HealthIndicator indicator = outlierDetector.newHealthIndicator(addr, hostObserver); @@ -388,7 +390,7 @@ private Host createHost(ResolvedAddress addr) { // failed connect threshold is negative, meaning disabled. final HealthCheckConfig hostHealthCheckConfig = healthCheckConfig == null || healthCheckConfig.failedThreshold < 0 ? null : healthCheckConfig; - final Host host = new DefaultHost<>(lbDescription, addr, connectionPoolStrategy, + final Host host = new DefaultHost<>(lbDescription, addr, weight, connectionPoolStrategy, connectionFactory, hostObserver, hostHealthCheckConfig, indicator); if (indicator != null) { indicator.setHost(host); diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java index a94c1f3c1b..15ece6e60b 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java @@ -51,6 +51,12 @@ interface Host extends Listen */ ResolvedAddress address(); + /** + * The relative weight of the endpoint. + * @return the relative weight of the endpoint. + */ + double weight(); + /** * Determine the health status of this host. * @return whether the host considers itself healthy enough to serve traffic. This is best effort and does not From 409e176d525d83d38e91f34aae5c2e6051f06555 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Tue, 23 Apr 2024 12:02:13 -0600 Subject: [PATCH 3/6] Add way to modify Metadata --- .../client/api/DefaultMetadata.java | 32 ++++++++++++++++++- .../io/servicetalk/client/api/Metadata.java | 3 ++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java index b7357d702c..cdaae96b82 100644 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java @@ -3,6 +3,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.HashMap; import java.util.Map; import static java.util.Objects.requireNonNull; @@ -15,7 +16,7 @@ final class DefaultMetadata implements Metadata { private final Map values; - DefaultMetadata(final Map values) { + private DefaultMetadata(final Map values) { this.values = requireNonNull(values, "values"); } @@ -33,11 +34,40 @@ public T get(Key key) { } } + @Override + public Metadata put(Key key, T value) { + Map next = new HashMap<>(values); + next.put(key.name(), value); + return new DefaultMetadata(next); + } + + @Override + public Metadata remove(Key key) { + if (!values.containsKey(key.name())) { + return this; + } + Map next = new HashMap<>(values); + next.remove(key.name()); + return new DefaultMetadata(next); + } + // A simple implementation of the always-empty Metadata. private static final class EmptyMetadata implements Metadata { @Override public T get(Key key) { return key.defaultValue(); } + + @Override + public Metadata put(Key key, T value) { + Map next = new HashMap<>(); + next.put(key.name(), value); + return new DefaultMetadata(next); + } + + @Override + public Metadata remove(Key key) { + return this; + } } } diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java index fde1e6c706..fbace0a6e9 100644 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java @@ -49,4 +49,7 @@ public String toString() { } T get(Key key); + + Metadata put(Key key, T value); + Metadata remove(Key key); } From 656ed7337bde694e2c6ba96b3354ad37bf8e8ca6 Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Tue, 30 Apr 2024 15:38:17 -0600 Subject: [PATCH 4/6] This feels a bit better --- .../client/api/DefaultMetadata.java | 73 --------------- .../io/servicetalk/client/api/Metadata.java | 55 ----------- .../servicetalk/client/api/MetadataKeys.java | 12 --- .../client/api/ServiceDiscovererEvent.java | 17 ++-- .../client/api/ServiceDiscovererMetadata.java | 91 +++++++++++++++++++ .../loadbalancer/DefaultLoadBalancer.java | 7 +- 6 files changed, 101 insertions(+), 154 deletions(-) delete mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java delete mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java delete mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java create mode 100644 servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java deleted file mode 100644 index cdaae96b82..0000000000 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/DefaultMetadata.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.servicetalk.client.api; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -import static java.util.Objects.requireNonNull; - -final class DefaultMetadata implements Metadata { - - private static Logger LOGGER = LoggerFactory.getLogger(DefaultMetadata.class); - - static final Metadata EMPTY_METADATA = new EmptyMetadata(); - - private final Map values; - - private DefaultMetadata(final Map values) { - this.values = requireNonNull(values, "values"); - } - - @Override - public T get(Key key) { - Object value = values.get(key.name()); - if (value == null) { - return key.defaultValue(); - } else if (!key.clazz().isInstance(value)) { - LOGGER.info("Metadata entry with name {} was found but didn't contain the expected type. Found: {}, " + - "expected: {}", key.name(), value.getClass(), key.clazz()); - return key.defaultValue(); - } else { - return key.clazz().cast(value); - } - } - - @Override - public Metadata put(Key key, T value) { - Map next = new HashMap<>(values); - next.put(key.name(), value); - return new DefaultMetadata(next); - } - - @Override - public Metadata remove(Key key) { - if (!values.containsKey(key.name())) { - return this; - } - Map next = new HashMap<>(values); - next.remove(key.name()); - return new DefaultMetadata(next); - } - - // A simple implementation of the always-empty Metadata. - private static final class EmptyMetadata implements Metadata { - @Override - public T get(Key key) { - return key.defaultValue(); - } - - @Override - public Metadata put(Key key, T value) { - Map next = new HashMap<>(); - next.put(key.name(), value); - return new DefaultMetadata(next); - } - - @Override - public Metadata remove(Key key) { - return this; - } - } -} diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java deleted file mode 100644 index fbace0a6e9..0000000000 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/Metadata.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.servicetalk.client.api; - -import static java.util.Objects.requireNonNull; - -public interface Metadata { - - final class Key { - - private final Class clazz; - private final String name; - private final T defaultValue; - - public Key(final Class clazz, final String name, final T defaultValue) { - this.clazz = requireNonNull(clazz, "clazz"); - this.name = requireNonNull(name, "name"); - this.defaultValue = requireNonNull(defaultValue, "defaultValue"); - } - - String name() { - return name; - } - - Class clazz() { - return clazz; - } - - T defaultValue() { - return defaultValue; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null || obj.getClass() != Key.class) { - return false; - } - Key other = (Key) obj; - return other.clazz.equals(clazz) && other.name.equals(name); - } - - @Override - public String toString() { - return "Key<" + clazz.getName() + ">(" + name + ", " + defaultValue + ")"; - } - } - - T get(Key key); - - Metadata put(Key key, T value); - Metadata remove(Key key); -} diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java deleted file mode 100644 index 958e719fe6..0000000000 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/MetadataKeys.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.servicetalk.client.api; - -public final class MetadataKeys { - - /** - * Metadata that describes the relative weight of an endpoint. - */ - public static final Metadata.Key WEIGHT = new Metadata.Key<>(Double.class, "endpoint.weight", 1.0); - private MetadataKeys() { - // no instances. - } -} diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java index 2a5dfd2077..042d53c193 100644 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererEvent.java @@ -16,12 +16,14 @@ package io.servicetalk.client.api; import java.util.Locale; +import java.util.Map; /** * Notification from the Service Discovery system that availability for an address has changed. * @param the type of address after resolution. */ public interface ServiceDiscovererEvent { + /** * Get the resolved address which is the subject of this event. *

@@ -40,17 +42,12 @@ public interface ServiceDiscovererEvent { Status status(); /** - * Meta-data associated with the specified address. - *

- * Metadata is data that is not strictly necessary for load balancing purposes but can be useful for making more - * intelligent decisions. As described in {@link #address()}, updates to an addresses meta-data can be accomplished - * by sending another {@link ServiceDiscovererEvent} with the same address and {@link Status} but with different - * metadata. This also means that updates to status must also propagate the desired meta-data state or the empty - * meta-data state is assumed. - * @return + * The raw meta-data associated with this ServiceDiscovererEvent. + * Note: the result will be an unmodifiable collection. + * @return the raw meta-data associated with this ServiceDiscovererEvent. */ - default Metadata metadata() { - return DefaultMetadata.EMPTY_METADATA; + default Map metadata() { + return ServiceDiscovererMetadata.EMPTY_MAP; } /** diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java new file mode 100644 index 0000000000..d273dc7b73 --- /dev/null +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java @@ -0,0 +1,91 @@ +package io.servicetalk.client.api; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static java.util.Objects.requireNonNull; + +/** + * Utilities helpful for extracting meta-data from {@link ServiceDiscovererEvent}s. + */ +public final class ServiceDiscovererMetadata { + + public static final Map EMPTY_MAP = Collections.unmodifiableMap(new HashMap<>(0)); + + /** + * Metadata that describes the relative weight of an endpoint. + */ + public static final Key WEIGHT = new ServiceDiscovererMetadata.Key<>(Double.class, "endpoint.weight", 1.0); + + /** + * Metadata describing the priority class of an endpoint. + */ + public static final Key PRIORITY = new ServiceDiscovererMetadata.Key<>( + Integer.class, "endpoint.priority", 0); + + /** + * An extractor of meta-data to user with {@link ServiceDiscovererEvent} instances. + * + * A {@link ServiceDiscovererEvent} can carry additional metadata, but this data is not type safe. The key type + * exists to provide a uniform way to define meta-data extractors that can properly extract and cast meta-data + * while also providing a default. + * @param the expected type of the meta-data. + */ + public static final class Key { + + private final Class clazz; + private final String name; + private final T defaultValue; + + public Key(final Class clazz, final String name, final T defaultValue) { + this.clazz = requireNonNull(clazz, "clazz"); + this.name = requireNonNull(name, "name"); + this.defaultValue = requireNonNull(defaultValue, "defaultValue"); + } + + public String name() { + return name; + } + + public Class clazz() { + return clazz; + } + + public boolean exists(ServiceDiscovererEvent event) { + return event.metadata().containsKey(name); + } + + public T getValue(ServiceDiscovererEvent event) { + Object result = event.metadata().get(name); + if (clazz.isInstance(result)) { + return (T) result; + } else { + return defaultValue; + } + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != Key.class) { + return false; + } + Key other = (Key) obj; + return other.clazz.equals(clazz) && other.name.equals(name); + } + + @Override + public String toString() { + return "Key<" + clazz.getName() + ">(" + name + ", " + defaultValue + ")"; + } + } + + private ServiceDiscovererMetadata() { + // no instances. + } +} diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java index 3b9897d5b2..f7c7f85a46 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java @@ -17,7 +17,6 @@ import io.servicetalk.client.api.ConnectionFactory; import io.servicetalk.client.api.LoadBalancedConnection; -import io.servicetalk.client.api.MetadataKeys; import io.servicetalk.client.api.NoActiveHostException; import io.servicetalk.client.api.NoAvailableHostException; import io.servicetalk.client.api.ServiceDiscovererEvent; @@ -53,7 +52,7 @@ import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_NOT_READY_EVENT; import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_READY_EVENT; -import static io.servicetalk.client.api.MetadataKeys.WEIGHT; +import static io.servicetalk.client.api.ServiceDiscovererMetadata.WEIGHT; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE; @@ -311,7 +310,7 @@ private void sequentialOnNext(Collection Date: Tue, 30 Apr 2024 17:20:14 -0600 Subject: [PATCH 5/6] More cleanup --- .../client/api/ServiceDiscovererMetadata.java | 39 +++++++++++++++++-- .../loadbalancer/DefaultLoadBalancer.java | 2 +- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java index d273dc7b73..7f395131d3 100644 --- a/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java +++ b/servicetalk-client-api/src/main/java/io/servicetalk/client/api/ServiceDiscovererMetadata.java @@ -1,3 +1,18 @@ +/* + * Copyright © 2024 Apple Inc. and the ServiceTalk project authors + * + * Licensed 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 io.servicetalk.client.api; import java.util.Collections; @@ -16,7 +31,7 @@ public final class ServiceDiscovererMetadata { /** * Metadata that describes the relative weight of an endpoint. */ - public static final Key WEIGHT = new ServiceDiscovererMetadata.Key<>(Double.class, "endpoint.weight", 1.0); + public static final Key WEIGHT = new ServiceDiscovererMetadata.Key<>(Double.class, "endpoint.weight", 1d); /** * Metadata describing the priority class of an endpoint. @@ -44,18 +59,36 @@ public Key(final Class clazz, final String name, final T defaultValue) { this.defaultValue = requireNonNull(defaultValue, "defaultValue"); } + /** + * Get the name associated with the meta-data. + * @return the name associated with the meta-data. + */ public String name() { return name; } + /** + * The java class that is expected to be associated with the meta-data. + * @return the java class that is expected to be associated with the meta-data. + */ public Class clazz() { return clazz; } - public boolean exists(ServiceDiscovererEvent event) { - return event.metadata().containsKey(name); + /** + * Determine whether the meta-data both contains an entry with the keys name and that entry is the correct type. + * @param event the {@link ServiceDiscovererEvent} for which to check if the meta-data exists. + * @return true if the meta-data contains an entry with the keys name and the value is the correct type. + */ + public boolean contains(ServiceDiscovererEvent event) { + return clazz.isInstance(event.metadata().get(name)); } + /** + * Extract the meta-data from a {@link ServiceDiscovererEvent}, or get the default. + * @param event the {@link ServiceDiscovererEvent} from which to extract the meta-data. + * @return the value contained in the meta-data, or the default if it doesn't exist or has the wrong type. + */ public T getValue(ServiceDiscovererEvent event) { Object result = event.metadata().get(name); if (clazz.isInstance(result)) { diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java index f7c7f85a46..ac138b1c46 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java @@ -52,10 +52,10 @@ import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_NOT_READY_EVENT; import static io.servicetalk.client.api.LoadBalancerReadyEvent.LOAD_BALANCER_READY_EVENT; -import static io.servicetalk.client.api.ServiceDiscovererMetadata.WEIGHT; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.AVAILABLE; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.EXPIRED; import static io.servicetalk.client.api.ServiceDiscovererEvent.Status.UNAVAILABLE; +import static io.servicetalk.client.api.ServiceDiscovererMetadata.WEIGHT; import static io.servicetalk.concurrent.api.AsyncCloseables.newCompositeCloseable; import static io.servicetalk.concurrent.api.AsyncCloseables.toAsyncCloseable; import static io.servicetalk.concurrent.api.Processors.newPublisherProcessorDropHeadOnOverflow; From 93db35a585859037e57b21e5c7b588a08875d37f Mon Sep 17 00:00:00 2001 From: Bryce Anderson Date: Wed, 1 May 2024 09:34:14 -0600 Subject: [PATCH 6/6] Fix build --- .../src/main/java/io/servicetalk/loadbalancer/Host.java | 8 -------- .../java/io/servicetalk/loadbalancer/DefaultHostTest.java | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java index 6fd1bbcf96..15ece6e60b 100644 --- a/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java +++ b/servicetalk-loadbalancer-experimental/src/main/java/io/servicetalk/loadbalancer/Host.java @@ -84,12 +84,4 @@ interface Host extends Listen * @return true if the host is now in the closed state, false otherwise. */ boolean markExpired(); - - /** - * The weight of the host, relative to the weights of associated hosts. - * @return the relative weight of the host. - */ - default double weight() { - return 1.0; - } } diff --git a/servicetalk-loadbalancer-experimental/src/test/java/io/servicetalk/loadbalancer/DefaultHostTest.java b/servicetalk-loadbalancer-experimental/src/test/java/io/servicetalk/loadbalancer/DefaultHostTest.java index 1d3c8c083f..3a0c03c805 100644 --- a/servicetalk-loadbalancer-experimental/src/test/java/io/servicetalk/loadbalancer/DefaultHostTest.java +++ b/servicetalk-loadbalancer-experimental/src/test/java/io/servicetalk/loadbalancer/DefaultHostTest.java @@ -48,6 +48,7 @@ class DefaultHostTest { private static final String DEFAULT_ADDRESS = "address"; + private static final double DEFAULT_WEIGHT = 1.0; @RegisterExtension final ExecutorExtension executor = ExecutorExtension.withTestExecutor(); @@ -81,7 +82,7 @@ void cleanup() { } private void buildHost(@Nullable HealthIndicator healthIndicator) { - host = new DefaultHost<>("lbDescription", DEFAULT_ADDRESS, + host = new DefaultHost<>("lbDescription", DEFAULT_ADDRESS, DEFAULT_WEIGHT, LinearSearchConnectionPoolStrategy.factory(DEFAULT_LINEAR_SEARCH_SPACE) .buildStrategy("resource"), connectionFactory, mockHostObserver, healthCheckConfig, healthIndicator);