util: OutlierDetection should use Ticker, not TimeProvider (#12110)

TimeProvider provides wall time. That can move forward and backward as time is adjusted. OutlierDetection is measuring durations, so it should use a monotonic clock.

Fixes #11622
This commit is contained in:
vimanikag 2025-06-16 14:47:36 +00:00 committed by GitHub
parent d5b4fb51c2
commit 1c43098990
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 10 additions and 10 deletions

View File

@ -22,6 +22,7 @@ import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Ticker;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@ -39,7 +40,6 @@ import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.SynchronizationContext;
import io.grpc.SynchronizationContext.ScheduledHandle;
import io.grpc.internal.TimeProvider;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collection;
@ -82,7 +82,7 @@ public final class OutlierDetectionLoadBalancer extends LoadBalancer {
private final SynchronizationContext syncContext;
private final Helper childHelper;
private final GracefulSwitchLoadBalancer switchLb;
private TimeProvider timeProvider;
private Ticker ticker;
private final ScheduledExecutorService timeService;
private ScheduledHandle detectionTimerHandle;
private Long detectionTimerStartNanos;
@ -95,14 +95,14 @@ public final class OutlierDetectionLoadBalancer extends LoadBalancer {
/**
* Creates a new instance of {@link OutlierDetectionLoadBalancer}.
*/
public OutlierDetectionLoadBalancer(Helper helper, TimeProvider timeProvider) {
public OutlierDetectionLoadBalancer(Helper helper, Ticker ticker) {
logger = helper.getChannelLogger();
childHelper = new ChildHelper(checkNotNull(helper, "helper"));
switchLb = new GracefulSwitchLoadBalancer(childHelper);
endpointTrackerMap = new EndpointTrackerMap();
this.syncContext = checkNotNull(helper.getSynchronizationContext(), "syncContext");
this.timeService = checkNotNull(helper.getScheduledExecutorService(), "timeService");
this.timeProvider = timeProvider;
this.ticker = ticker;
logger.log(ChannelLogLevel.DEBUG, "OutlierDetection lb created.");
}
@ -151,7 +151,7 @@ public final class OutlierDetectionLoadBalancer extends LoadBalancer {
// If a timer has started earlier we cancel it and use the difference between the start
// time and now as the interval.
initialDelayNanos = Math.max(0L,
config.intervalNanos - (timeProvider.currentTimeNanos() - detectionTimerStartNanos));
config.intervalNanos - (ticker.read() - detectionTimerStartNanos));
}
// If a timer has been previously created we need to cancel it and reset all the call counters
@ -201,7 +201,7 @@ public final class OutlierDetectionLoadBalancer extends LoadBalancer {
@Override
public void run() {
detectionTimerStartNanos = timeProvider.currentTimeNanos();
detectionTimerStartNanos = ticker.read();
endpointTrackerMap.swapCounters();
@ -638,7 +638,7 @@ public final class OutlierDetectionLoadBalancer extends LoadBalancer {
config.baseEjectionTimeNanos * ejectionTimeMultiplier,
maxEjectionDurationSecs);
return currentTimeNanos > maxEjectionTimeNanos;
return currentTimeNanos - maxEjectionTimeNanos > 0;
}
/** Tracks both successful and failed call counts. */

View File

@ -16,6 +16,7 @@
package io.grpc.util;
import com.google.common.base.Ticker;
import io.grpc.Internal;
import io.grpc.LoadBalancer;
import io.grpc.LoadBalancer.Helper;
@ -23,7 +24,6 @@ import io.grpc.LoadBalancerProvider;
import io.grpc.NameResolver.ConfigOrError;
import io.grpc.Status;
import io.grpc.internal.JsonUtil;
import io.grpc.internal.TimeProvider;
import io.grpc.util.OutlierDetectionLoadBalancer.OutlierDetectionLoadBalancerConfig;
import io.grpc.util.OutlierDetectionLoadBalancer.OutlierDetectionLoadBalancerConfig.FailurePercentageEjection;
import io.grpc.util.OutlierDetectionLoadBalancer.OutlierDetectionLoadBalancerConfig.SuccessRateEjection;
@ -34,7 +34,7 @@ public final class OutlierDetectionLoadBalancerProvider extends LoadBalancerProv
@Override
public LoadBalancer newLoadBalancer(Helper helper) {
return new OutlierDetectionLoadBalancer(helper, TimeProvider.SYSTEM_TIME_PROVIDER);
return new OutlierDetectionLoadBalancer(helper, Ticker.systemTicker());
}
@Override

View File

@ -227,7 +227,7 @@ public class OutlierDetectionLoadBalancerTest {
when(mockStreamTracerFactory.newClientStreamTracer(any(),
any())).thenReturn(mockStreamTracer);
loadBalancer = new OutlierDetectionLoadBalancer(mockHelper, fakeClock.getTimeProvider());
loadBalancer = new OutlierDetectionLoadBalancer(mockHelper, fakeClock.getTicker());
}
@Test