3333import java .util .concurrent .ScheduledExecutorService ;
3434import java .util .concurrent .ScheduledFuture ;
3535import java .util .concurrent .TimeUnit ;
36- import java .util .concurrent .atomic .AtomicBoolean ;
3736import java .util .concurrent .atomic .AtomicLong ;
38- import java .util .concurrent .locks .ReentrantLock ;
3937import java .util .logging .Logger ;
4038import javax .annotation .Nullable ;
4139
@@ -55,11 +53,9 @@ public class GcpFallbackChannel extends ManagedChannel {
5553 private final boolean ownsFallbackState ;
5654 private final GcpFallbackOpenTelemetry openTelemetry ;
5755
58- private final AtomicBoolean localInFallbackMode = new AtomicBoolean (false );
59- private final AtomicLong localGeneration = new AtomicLong (0 );
56+ private final AtomicLong localProbeGeneration = new AtomicLong (0 );
6057 private final AtomicLong localProbeSuccesses = new AtomicLong (0 );
6158 private final AtomicLong localFirstPrimaryProbeSuccessNanos = new AtomicLong (0 );
62- private final ReentrantLock stateLock = new ReentrantLock ();
6359
6460 private final ScheduledExecutorService execService ;
6561 private volatile ScheduledFuture <?> primaryProbeFuture = null ;
@@ -94,9 +90,7 @@ public GcpFallbackChannel(
9490 this .ownsFallbackState = false ;
9591 } else {
9692 this .fallbackState =
97- execService != null
98- ? new GcpFallbackState (execService )
99- : new GcpFallbackState ();
93+ execService != null ? new GcpFallbackState (execService ) : new GcpFallbackState ();
10094 this .ownsFallbackState = true ;
10195 }
10296 this .execService = fallbackState .getOrCreateExecutorService (options );
@@ -166,9 +160,7 @@ public GcpFallbackChannel(
166160 this .ownsFallbackState = false ;
167161 } else {
168162 this .fallbackState =
169- execService != null
170- ? new GcpFallbackState (execService )
171- : new GcpFallbackState ();
163+ execService != null ? new GcpFallbackState (execService ) : new GcpFallbackState ();
172164 this .ownsFallbackState = true ;
173165 }
174166 this .execService = fallbackState .getOrCreateExecutorService (options );
@@ -190,55 +182,32 @@ public GcpFallbackChannel(
190182 init ();
191183 }
192184
193- private void syncFallbackModeState () {
194- if (localGeneration .get () < fallbackState .getGeneration ()) {
195- stateLock .lock ();
196- try {
197- synchronized (fallbackState ) {
198- long poolGen = fallbackState .getGeneration ();
199- if (localGeneration .get () < poolGen ) {
200- localInFallbackMode .set (fallbackState .getTargetFallbackMode ());
201- localProbeSuccesses .set (0 );
202- localFirstPrimaryProbeSuccessNanos .set (0 );
203- localGeneration .set (poolGen );
204- }
205- }
206- } finally {
207- stateLock .unlock ();
208- }
185+ private void resetProbeStatsIfGenerationChanged () {
186+ long currentGen = fallbackState .getGeneration ();
187+ if (localProbeGeneration .get () != currentGen ) {
188+ localProbeSuccesses .set (0 );
189+ localFirstPrimaryProbeSuccessNanos .set (0 );
190+ localProbeGeneration .set (currentGen );
209191 }
210192 }
211193
212194 public boolean isInFallbackMode () {
213- syncFallbackModeState ();
214- return (localInFallbackMode .get () && fallbackChannel != null ) || primaryChannel == null ;
195+ return (fallbackState .isInFallbackMode () && fallbackChannel != null ) || primaryChannel == null ;
215196 }
216197
217198 @ VisibleForTesting
218199 GcpFallbackState getFallbackState () {
219200 return fallbackState ;
220201 }
221202
222- @ VisibleForTesting
223- AtomicBoolean getLocalInFallbackMode () {
224- return localInFallbackMode ;
225- }
226-
227- @ VisibleForTesting
228- AtomicLong getLocalGeneration () {
229- return localGeneration ;
230- }
231-
232203 @ VisibleForTesting
233204 AtomicLong getLocalProbeSuccesses () {
205+ resetProbeStatsIfGenerationChanged ();
234206 return localProbeSuccesses ;
235207 }
236208
237209 private void init () {
238- synchronized (fallbackState ) {
239- localGeneration .set (fallbackState .getGeneration ());
240- localInFallbackMode .set (fallbackState .getTargetFallbackMode ());
241- }
210+ localProbeGeneration .set (fallbackState .getGeneration ());
242211 if (options .getPrimaryProbingFunction () != null ) {
243212 this .primaryProbeFuture =
244213 fallbackState .scheduleTask (
@@ -261,7 +230,7 @@ private void init() {
261230 }
262231
263232 private void processPrimaryStatusCode (Status .Code statusCode ) {
264- if (fallbackChannel != null && !localInFallbackMode . get ()) {
233+ if (fallbackChannel != null && !fallbackState . isInFallbackMode ()) {
265234 if (options .getErroneousStates ().contains (statusCode )) {
266235 fallbackState .getPrimaryFailures ().incrementAndGet ();
267236 } else {
@@ -281,63 +250,41 @@ private void processFallbackStatusCode(Status.Code statusCode) {
281250 }
282251
283252 private void probePrimary () {
284- if (!localInFallbackMode .get () && primaryChannel != null ) {
285- return ;
286- }
287- syncFallbackModeState ();
288- if (!localInFallbackMode .get () && primaryChannel != null ) {
253+ if (!fallbackState .isInFallbackMode () && primaryChannel != null ) {
289254 return ;
290255 }
291- long probeStartGen = localGeneration .get ();
256+ resetProbeStatsIfGenerationChanged ();
257+ long probeStartGen = localProbeGeneration .get ();
292258 String result = "" ;
293259 if (primaryDelegateChannel == null ) {
294260 result = INIT_FAILURE_REASON ;
295261 } else {
296262 result = options .getPrimaryProbingFunction ().apply (primaryDelegateChannel );
297263 }
298- stateLock .lock ();
299- try {
300- syncFallbackModeState ();
301- if (localGeneration .get () == probeStartGen
302- && (localInFallbackMode .get () || primaryChannel == null )) {
303- if ("OK" .equals (result )) {
304- long nowNanos = System .nanoTime ();
305- long firstSuccessNanos =
306- localFirstPrimaryProbeSuccessNanos .updateAndGet (prev -> prev == 0 ? nowNanos : prev );
307- long primaryProbeSuccessCount = localProbeSuccesses .incrementAndGet ();
308-
309- boolean durationSatisfied = true ;
310- if (options .getMinPrimaryProbeSuccessDuration () != null
311- && !options .getMinPrimaryProbeSuccessDuration ().isZero ()
312- && !options .getMinPrimaryProbeSuccessDuration ().isNegative ()) {
313- long elapsedNanos = nowNanos - firstSuccessNanos ;
314- durationSatisfied =
315- elapsedNanos >= options .getMinPrimaryProbeSuccessDuration ().toNanos ();
316- }
317-
318- if (options .isEnableRecovery ()
319- && fallbackChannel != null
320- && primaryProbeSuccessCount >= options .getMinPrimaryProbeSuccessCount ()
321- && durationSatisfied ) {
322- long recoveredGen =
323- fallbackState .recordRecovery (
324- probeStartGen , options .isEnablePerChannelRecovery ());
325- if (recoveredGen != -1 ) {
326- localInFallbackMode .set (false );
327- localProbeSuccesses .set (0 );
328- localFirstPrimaryProbeSuccessNanos .set (0 );
329- localGeneration .set (recoveredGen );
330- } else {
331- syncFallbackModeState ();
332- }
333- }
334- } else {
335- localProbeSuccesses .set (0 );
336- localFirstPrimaryProbeSuccessNanos .set (0 );
264+ if ("" .equals (result ) && fallbackState .getGeneration () == probeStartGen ) {
265+ if (options .isEnableRecovery () && fallbackChannel != null ) {
266+ long nowNanos = System .nanoTime ();
267+ long firstSuccessNanos =
268+ localFirstPrimaryProbeSuccessNanos .updateAndGet (prev -> prev == 0 ? nowNanos : prev );
269+ long primaryProbeSuccessCount = localProbeSuccesses .incrementAndGet ();
270+
271+ boolean durationSatisfied = true ;
272+ if (options .getMinPrimaryProbeSuccessDuration () != null
273+ && !options .getMinPrimaryProbeSuccessDuration ().isZero ()
274+ && !options .getMinPrimaryProbeSuccessDuration ().isNegative ()) {
275+ long elapsedNanos = nowNanos - firstSuccessNanos ;
276+ durationSatisfied =
277+ elapsedNanos >= options .getMinPrimaryProbeSuccessDuration ().toNanos ();
278+ }
279+
280+ if (primaryProbeSuccessCount >= options .getMinPrimaryProbeSuccessCount ()
281+ && durationSatisfied ) {
282+ fallbackState .recordRecovery (probeStartGen );
337283 }
338284 }
339- } finally {
340- stateLock .unlock ();
285+ } else {
286+ localProbeSuccesses .set (0 );
287+ localFirstPrimaryProbeSuccessNanos .set (0 );
341288 }
342289 // Report metric based on result.
343290 openTelemetry .getModule ().reportProbeResult (options .getPrimaryChannelName (), result );
0 commit comments