Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

loadbalancer: introduce the HealthChecker interface #2800

Conversation

bryce-anderson
Copy link
Contributor

Motivation:

We want to make health checking more module in
DefaultLoadBalancer.

Modifications:

  • Add the HealthChecker interface and the supporting cast.
  • Integrate the HealthChecker into DefaultLoadBalancer.
  • Adjust the LatencyTracker interface and extend it to support HealthChecker.

Result:

A modular way to support health checking.

Motivation:

We want to make health checking more module in
DefaultLoadBalancer.

Modifications:

- Add the HealthChecker interface and the supporting
  cast.
- Integrate the HealthChecker into DefaultLoadBalancer.
- Adjust the LatencyTracker interface and extend it to
  support HealthChecker.

Result:

A modular way to support health checking.
Copy link
Contributor

@tkountis tkountis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On purpose? Is there a replacement that i missed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did delete it on purpose. I suppose we can keep it, but I had hoped that we could use the class by extension instead of by proxy so that we can avoid the questions of which delegate to pick. That is important because if they have different time sources we can have really weird bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I brought it back just in case we want it later. I'm still of the opinion that we need to somehow enforce the same time source for both delegates but we can continue to iterate on it.

Copy link
Contributor

@mgodave mgodave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, it will slot into some of the work I have planned.

@bryce-anderson bryce-anderson merged commit 34db3ac into apple:main Jan 12, 2024
15 checks passed
@bryce-anderson bryce-anderson deleted the bl_anderson/L7OutlierDetector_pt1-interfaces branch January 12, 2024 21:16
Copy link
Member

@idelpivnitskiy idelpivnitskiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, some recommendations:

@@ -52,16 +53,4 @@ interface LatencyTracker extends ScoreSupplier {
* @param beforeStartTimeNs return value from {@link #beforeStart()}.
*/
void observeError(long beforeStartTimeNs);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: consider onSuccess/onError/onCancel instead of observe* to be consistent with other places

/**
* The representation of a health checking system for use with load balancing.
* <p>
* The core
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it have continuation?

* The current time in nanoseconds.
* @return the current time in nanoseconds.
*/
protected abstract long currentTimeNanos();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the DefaultRequestTracker can access Executor from HealthCheckerFactory.newHealthChecker(...), I would recommend using Executor as a TimeSource instead of adding an internal hook here, as a consistent approach we use in other places.

if (context == null) {
context = new DefaultContextMap();
}
context.put(REQUEST_TRACKER_KEY, healthIndicator);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not ideal that we have to rely on context. Let's see how we retrieve it from the context in follow-up PRs, maybe there will be a better way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to suggestions. This is The best both Thomas and I have thought of so far playing within the constraints of ConnectionFactory and the *Requester interfaces.

@Nullable final HealthCheckConfig healthCheckConfig,
@Nullable final LoadBalancerObserver<ResolvedAddress> loadBalancerObserver) {
@Nullable final Supplier<HealthChecker> healthCheckerFactory) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplier<HealthChecker> -> Supplier<HealthChecker<ResolvedAddress>>, we should also add parameters for LoadBalancingPolicy

this.sequentialExecutor = new SequentialExecutor((uncaughtException) ->
LOGGER.error("{}: Uncaught exception in SequentialExecutor", this, uncaughtException));
LOGGER.error("{}: Uncaught exception in " + this.getClass().getSimpleName(), this, uncaughtException));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using logger formatting instead of concatenation

@@ -0,0 +1,36 @@
/*
* Copyright © 2023 Apple Inc. and the ServiceTalk project authors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of my comments addressed in this patch:

Subject: [PATCH] comments
---
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/LoadBalancerBuilder.java	(date 1705108853059)
@@ -89,7 +89,8 @@
      * {@link HealthChecker}.
      * @return {code this}
      */
-    LoadBalancerBuilder<ResolvedAddress, C> healthCheckerFactory(HealthCheckerFactory healthCheckerFactory);
+    LoadBalancerBuilder<ResolvedAddress, C> healthCheckerFactory(
+            HealthCheckerFactory<ResolvedAddress> healthCheckerFactory);
 
     /**
      * This {@link LoadBalancer} may monitor hosts to which connection establishment has failed
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancer.java	(date 1705108572725)
@@ -133,7 +133,7 @@
             final int linearSearchSpace,
             final LoadBalancerObserver<ResolvedAddress> loadBalancerObserver,
             @Nullable final HealthCheckConfig healthCheckConfig,
-            @Nullable final Supplier<HealthChecker> healthCheckerFactory) {
+            @Nullable final Supplier<HealthChecker<ResolvedAddress>> healthCheckerFactory) {
         this.targetResource = requireNonNull(targetResourceName);
         this.lbDescription = makeDescription(id, targetResource);
         this.hostSelector = requireNonNull(hostSelector, "hostSelector");
Index: servicetalk-loadbalancer/src/test/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancerBuilderAdapter.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/test/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancerBuilderAdapter.java b/servicetalk-loadbalancer/src/test/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancerBuilderAdapter.java
--- a/servicetalk-loadbalancer/src/test/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancerBuilderAdapter.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/test/java/io/servicetalk/loadbalancer/RoundRobinLoadBalancerBuilderAdapter.java	(date 1705108853063)
@@ -44,7 +44,7 @@
 
     @Override
     public LoadBalancerBuilder<String, TestLoadBalancedConnection> healthCheckerFactory(
-            HealthCheckerFactory healthCheckerFactory) {
+            HealthCheckerFactory<String> healthCheckerFactory) {
         throw new IllegalStateException("Cannot set a load balancer health checker for old round robin");
     }
 
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthChecker.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthChecker.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthChecker.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthChecker.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthChecker.java	(date 1705107787951)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2023 Apple Inc. and the ServiceTalk project authors
+ * 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.
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthCheckerFactory.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthCheckerFactory.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthCheckerFactory.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthCheckerFactory.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthCheckerFactory.java	(date 1705107831292)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2023 Apple Inc. and the ServiceTalk project authors
+ * 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.
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/RequestTracker.java	(date 1705107576296)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2020 Apple Inc. and the ServiceTalk project authors
+ * Copyright © 2023-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.
@@ -22,7 +22,6 @@
  */
 interface RequestTracker {
 
-    @SuppressWarnings("rawtypes")
     ContextMap.Key<RequestTracker> REQUEST_TRACKER_KEY =
             ContextMap.Key.newKey("request_tracker", RequestTracker.class);
 
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancerBuilder.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancerBuilder.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancerBuilder.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancerBuilder.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/DefaultLoadBalancerBuilder.java	(date 1705108853061)
@@ -51,7 +51,7 @@
     @Nullable
     private LoadBalancerObserver<ResolvedAddress> loadBalancerObserver;
     @Nullable
-    private HealthCheckerFactory healthCheckerFactory;
+    private HealthCheckerFactory<ResolvedAddress> healthCheckerFactory;
     private Duration healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL;
     private Duration healthCheckJitter = DEFAULT_HEALTH_CHECK_JITTER;
     private int healthCheckFailedConnectionsThreshold = DEFAULT_HEALTH_CHECK_FAILED_CONNECTIONS_THRESHOLD;
@@ -83,7 +83,8 @@
     }
 
     @Override
-    public LoadBalancerBuilder<ResolvedAddress, C> healthCheckerFactory(HealthCheckerFactory healthCheckerFactory) {
+    public LoadBalancerBuilder<ResolvedAddress, C> healthCheckerFactory(
+            HealthCheckerFactory<ResolvedAddress> healthCheckerFactory) {
         this.healthCheckerFactory = healthCheckerFactory;
         return this;
     }
@@ -133,7 +134,7 @@
         }
         final LoadBalancerObserver<ResolvedAddress> loadBalancerObserver = this.loadBalancerObserver != null ?
                 this.loadBalancerObserver : NoopLoadBalancerObserver.instance();
-        Supplier<HealthChecker> healthCheckerSupplier;
+        Supplier<HealthChecker<ResolvedAddress>> healthCheckerSupplier;
         if (healthCheckerFactory == null) {
             healthCheckerSupplier = null;
         } else {
@@ -154,14 +155,14 @@
         private final LoadBalancerObserver<ResolvedAddress> loadBalancerObserver;
         private final int linearSearchSpace;
         @Nullable
-        private final Supplier<HealthChecker> healthCheckerFactory;
+        private final Supplier<HealthChecker<ResolvedAddress>> healthCheckerFactory;
         @Nullable
         private final HealthCheckConfig healthCheckConfig;
 
         DefaultLoadBalancerFactory(final String id, final LoadBalancingPolicy loadBalancingPolicy,
-        final int linearSearchSpace, final HealthCheckConfig healthCheckConfig,
+                                   final int linearSearchSpace, final HealthCheckConfig healthCheckConfig,
                                    final LoadBalancerObserver<ResolvedAddress> loadBalancerObserver,
-                                   final Supplier<HealthChecker> healthCheckerFactory) {
+                                   @Nullable final Supplier<HealthChecker<ResolvedAddress>> healthCheckerFactory) {
             this.id = requireNonNull(id, "id");
             this.loadBalancingPolicy = requireNonNull(loadBalancingPolicy, "loadBalancingPolicy");
             this.loadBalancerObserver = requireNonNull(loadBalancerObserver, "loadBalancerObserver");
Index: servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java
--- a/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java	(revision 34db3ac6fde1fd333efc146b37b70506642e51f0)
+++ b/servicetalk-loadbalancer/src/main/java/io/servicetalk/loadbalancer/HealthIndicator.java	(date 1705107858564)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2023 Apple Inc. and the ServiceTalk project authors
+ * 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants