All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.grpc.internal.RetryingNameResolver Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2023 The gRPC 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.grpc.internal;

import com.google.common.annotations.VisibleForTesting;
import io.grpc.Attributes;
import io.grpc.NameResolver;
import io.grpc.Status;
import io.grpc.SynchronizationContext;

/**
 * This wrapper class can add retry capability to any polling {@link NameResolver} implementation
 * that supports calling {@link ResolutionResultListener}s with the outcome of each resolution.
 *
 * 

The {@link NameResolver} used with this */ final class RetryingNameResolver extends ForwardingNameResolver { private final NameResolver retriedNameResolver; private final RetryScheduler retryScheduler; private final SynchronizationContext syncContext; static final Attributes.Key RESOLUTION_RESULT_LISTENER_KEY = Attributes.Key.create( "io.grpc.internal.RetryingNameResolver.RESOLUTION_RESULT_LISTENER_KEY"); /** * Creates a new {@link RetryingNameResolver}. * * @param retriedNameResolver A {@link NameResolver} that will have failed attempt retried. * @param retryScheduler Used to schedule the retry attempts. */ RetryingNameResolver(NameResolver retriedNameResolver, RetryScheduler retryScheduler, SynchronizationContext syncContext) { super(retriedNameResolver); this.retriedNameResolver = retriedNameResolver; this.retryScheduler = retryScheduler; this.syncContext = syncContext; } @Override public void start(Listener2 listener) { super.start(new RetryingListener(listener)); } @Override public void shutdown() { super.shutdown(); retryScheduler.reset(); } /** * Used to get the underlying {@link NameResolver} that is getting its failed attempts retried. */ @VisibleForTesting NameResolver getRetriedNameResolver() { return retriedNameResolver; } @VisibleForTesting class DelayedNameResolverRefresh implements Runnable { @Override public void run() { refresh(); } } private class RetryingListener extends Listener2 { private Listener2 delegateListener; RetryingListener(Listener2 delegateListener) { this.delegateListener = delegateListener; } @Override public void onResult(ResolutionResult resolutionResult) { // If the resolution result listener is already an attribute it indicates that a name resolver // has already been wrapped with this class. This indicates a misconfiguration. if (resolutionResult.getAttributes().get(RESOLUTION_RESULT_LISTENER_KEY) != null) { throw new IllegalStateException( "RetryingNameResolver can only be used once to wrap a NameResolver"); } // To have retry behavior for name resolvers that haven't migrated to onResult2. delegateListener.onResult(resolutionResult.toBuilder().setAttributes( resolutionResult.getAttributes().toBuilder() .set(RESOLUTION_RESULT_LISTENER_KEY, new ResolutionResultListener()).build()) .build()); } @Override public Status onResult2(ResolutionResult resolutionResult) { Status status = delegateListener.onResult2(resolutionResult); if (status.isOk()) { retryScheduler.reset(); } else { retryScheduler.schedule(new DelayedNameResolverRefresh()); } return status; } @Override public void onError(Status error) { delegateListener.onError(error); syncContext.execute(() -> retryScheduler.schedule(new DelayedNameResolverRefresh())); } } /** * Simple callback class to store in {@link ResolutionResult} attributes so that * ManagedChannel can indicate if the resolved addresses were accepted. Temporary until * the Listener2.onResult() API can be changed to return a boolean for this purpose. */ class ResolutionResultListener { public void resolutionAttempted(Status successStatus) { if (successStatus.isOk()) { retryScheduler.reset(); } else { retryScheduler.schedule(new DelayedNameResolverRefresh()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy