1515 */
1616package com .google .cloud .bigtable .gaxx .grpc ;
1717
18- import com .google .api .core .SettableApiFuture ;
18+ import com .google .api .core .ApiFuture ;
1919import com .google .auto .value .AutoValue ;
2020import com .google .bigtable .v2 .PingAndWarmResponse ;
2121import com .google .cloud .bigtable .data .v2 .stub .BigtableChannelPrimer ;
2222import com .google .cloud .bigtable .gaxx .grpc .BigtableChannelPool .Entry ;
2323import com .google .common .annotations .VisibleForTesting ;
2424import com .google .common .collect .ImmutableList ;
25+ import com .google .common .util .concurrent .MoreExecutors ;
2526import java .time .Clock ;
2627import java .time .Duration ;
2728import java .time .Instant ;
2829import java .util .Comparator ;
2930import java .util .List ;
3031import java .util .concurrent .ScheduledExecutorService ;
32+ import java .util .concurrent .ScheduledFuture ;
3133import java .util .concurrent .ThreadLocalRandom ;
3234import java .util .concurrent .TimeUnit ;
3335import java .util .function .Supplier ;
36+ import java .util .logging .Level ;
37+ import java .util .logging .Logger ;
3438import java .util .stream .Collectors ;
3539import javax .annotation .Nullable ;
3640
3741/** Class that manages the health checking in the BigtableChannelPool */
3842class ChannelPoolHealthChecker {
3943
44+ private static final Logger logger = Logger .getLogger (ChannelPoolHealthChecker .class .getName ());
45+
4046 // Configuration constants
4147 // Window_Duration is the duration over which we keep probe results
4248 private static final Duration WINDOW_DURATION = Duration .ofMinutes (5 );
@@ -69,9 +75,12 @@ static ProbeResult create(Instant startTime, boolean success) {
6975
7076 private final Supplier <ImmutableList <Entry >> entrySupplier ;
7177 private volatile Instant lastEviction ;
72- private ScheduledExecutorService executor ;
78+ private final ScheduledExecutorService executor ;
79+
80+ private final ChannelPrimer channelPrimer ;
7381
74- private ChannelPrimer channelPrimer ;
82+ private ScheduledFuture <?> probeTaskScheduledFuture ;
83+ private ScheduledFuture <?> detectAndRemoveTaskScheduledFuture ;
7584
7685 private final Clock clock ;
7786
@@ -89,54 +98,66 @@ public ChannelPoolHealthChecker(
8998 }
9099
91100 void start () {
92- Duration initialDelayProbe =
93- Duration .ofMillis (ThreadLocalRandom .current ().nextLong (PROBE_INTERVAL .toMillis ()));
94- executor .scheduleAtFixedRate (
95- this ::runProbes ,
96- initialDelayProbe .toMillis (),
97- PROBE_INTERVAL .toMillis (),
98- TimeUnit .MILLISECONDS );
99- Duration initialDelayDetect =
100- Duration .ofMillis (ThreadLocalRandom .current ().nextLong (PROBE_INTERVAL .toMillis ()));
101- executor .scheduleAtFixedRate (
102- this ::detectAndRemoveOutlierEntries ,
103- initialDelayDetect .toMillis (),
104- PROBE_INTERVAL .toMillis (),
105- TimeUnit .MILLISECONDS );
101+ if (channelPrimer instanceof BigtableChannelPrimer ) {
102+ Duration initialDelayProbe =
103+ Duration .ofMillis (ThreadLocalRandom .current ().nextLong (PROBE_INTERVAL .toMillis ()));
104+ this .probeTaskScheduledFuture =
105+ executor .scheduleAtFixedRate (
106+ this ::runProbes ,
107+ initialDelayProbe .toMillis (),
108+ PROBE_INTERVAL .toMillis (),
109+ TimeUnit .MILLISECONDS );
110+ Duration initialDelayDetect =
111+ Duration .ofMillis (ThreadLocalRandom .current ().nextLong (PROBE_INTERVAL .toMillis ()));
112+ this .detectAndRemoveTaskScheduledFuture =
113+ executor .scheduleAtFixedRate (
114+ this ::detectAndRemoveOutlierEntries ,
115+ initialDelayDetect .toMillis (),
116+ PROBE_INTERVAL .toMillis (),
117+ TimeUnit .MILLISECONDS );
118+ } else {
119+ logger .log (Level .WARNING , "NoOpChannelPrimer was provided, not checking channel health." );
120+ }
106121 }
107122
108123 /** Stop running health checking */
109124 public void stop () {
110- executor .shutdownNow ();
125+ if (probeTaskScheduledFuture != null ) {
126+ probeTaskScheduledFuture .cancel (true );
127+ }
128+ if (detectAndRemoveTaskScheduledFuture != null ) {
129+ detectAndRemoveTaskScheduledFuture .cancel (true );
130+ }
111131 }
112132
113133 /** Runs probes on all the channels in the pool. */
114134 @ VisibleForTesting
115135 void runProbes () {
116136 for (Entry entry : this .entrySupplier .get ()) {
117137 final Instant startTime = clock .instant ();
118- final SettableApiFuture <PingAndWarmResponse > probeFuture ;
138+ final ApiFuture <PingAndWarmResponse > probeFuture ;
119139
120140 if (channelPrimer instanceof BigtableChannelPrimer ) {
121141 BigtableChannelPrimer primer = (BigtableChannelPrimer ) channelPrimer ;
122142 probeFuture = primer .sendPrimeRequestsAsync (entry .getManagedChannel ());
123143 } else {
124144 continue ;
125145 }
126- probeFuture .addListener (() -> onComplete (entry , startTime , probeFuture ), executor );
146+ probeFuture .addListener (
147+ () -> onComplete (entry , startTime , probeFuture ), MoreExecutors .directExecutor ());
127148 }
128149 }
129150
130151 /** Callback that will update Entry data on probe complete. */
131152 @ VisibleForTesting
132- void onComplete (
133- Entry entry , Instant startTime , SettableApiFuture <PingAndWarmResponse > probeFuture ) {
153+ void onComplete (Entry entry , Instant startTime , ApiFuture <PingAndWarmResponse > probeFuture ) {
134154 boolean success ;
135155 try {
136156 probeFuture .get (PROBE_DEADLINE .toMillis (), TimeUnit .MILLISECONDS );
137157 success = true ;
138158 } catch (Exception e ) {
139159 success = false ;
160+ logger .log (Level .WARNING , "Probe failed" );
140161 }
141162 addProbeResult (entry , ProbeResult .create (startTime , success ));
142163 }
@@ -219,6 +240,9 @@ void detectAndRemoveOutlierEntries() {
219240 Entry outlier = findOutlierEntry ();
220241 if (outlier != null ) {
221242 this .lastEviction = clock .instant ();
243+ outlier .failedProbesInWindow .set (0 );
244+ outlier .successfulProbesInWindow .set (0 );
245+ outlier .probeHistory .clear ();
222246 outlier .getManagedChannel ().enterIdle ();
223247 }
224248 }
0 commit comments