/*
* Copyright 2016 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 org.apache.ratis.thirdparty.io.grpc;
import static org.apache.ratis.thirdparty.com.google.common.base.Preconditions.checkArgument;
import static org.apache.ratis.thirdparty.com.google.common.base.Preconditions.checkNotNull;
import org.apache.ratis.thirdparty.com.google.common.base.MoreObjects;
import org.apache.ratis.thirdparty.com.google.common.base.Objects;
import org.apache.ratis.thirdparty.com.google.common.base.Preconditions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.annotation.concurrent.ThreadSafe;
/**
* A pluggable component that receives resolved addresses from {@link NameResolver} and provides the
* channel a usable subchannel when asked.
*
* Overview
*
* A LoadBalancer typically implements three interfaces:
*
* - {@link LoadBalancer} is the main interface. All methods on it are invoked sequentially
* in the same synchronization context (see next section) as returned by
* {@link org.apache.ratis.thirdparty.io.grpc.LoadBalancer.Helper#getSynchronizationContext}. It receives the results
* from the {@link NameResolver}, updates of subchannels' connectivity states, and the
* channel's request for the LoadBalancer to shutdown.
* - {@link SubchannelPicker SubchannelPicker} does the actual load-balancing work. It selects
* a {@link Subchannel Subchannel} for each new RPC.
* - {@link Factory Factory} creates a new {@link LoadBalancer} instance.
*
*
* {@link Helper Helper} is implemented by gRPC library and provided to {@link Factory
* Factory}. It provides functionalities that a {@code LoadBalancer} implementation would typically
* need.
*
*
The Synchronization Context
*
* All methods on the {@link LoadBalancer} interface are called from a Synchronization Context,
* meaning they are serialized, thus the balancer implementation doesn't need to worry about
* synchronization among them. {@link org.apache.ratis.thirdparty.io.grpc.LoadBalancer.Helper#getSynchronizationContext}
* allows implementations to schedule tasks to be run in the same Synchronization Context, with or
* without a delay, thus those tasks don't need to worry about synchronizing with the balancer
* methods.
*
*
However, the actual running thread may be the network thread, thus the following rules must be
* followed to prevent blocking or even dead-locking in a network:
*
*
*
* - Never block in the Synchronization Context. The callback methods must
* return quickly. Examples or work that must be avoided: CPU-intensive calculation, waiting on
* synchronization primitives, blocking I/O, blocking RPCs, etc.
*
* - Avoid calling into other components with lock held. The Synchronization
* Context may be under a lock, e.g., the transport lock of OkHttp. If your LoadBalancer holds a
* lock in a callback method (e.g., {@link #handleResolvedAddresses handleResolvedAddresses()})
* while calling into another method that also involves locks, be cautious of deadlock. Generally
* you wouldn't need any locking in the LoadBalancer if you follow the canonical implementation
* pattern below.
*
*
*
* The canonical implementation pattern
*
* A {@link LoadBalancer} keeps states like the latest addresses from NameResolver, the
* Subchannel(s) and their latest connectivity states. These states are mutated within the
* Synchronization Context,
*
*
A typical {@link SubchannelPicker SubchannelPicker} holds a snapshot of these states. It may
* have its own states, e.g., a picker from a round-robin load-balancer may keep a pointer to the
* next Subchannel, which are typically mutated by multiple threads. The picker should only mutate
* its own state, and should not mutate or re-acquire the states of the LoadBalancer. This way the
* picker only needs to synchronize its own states, which is typically trivial to implement.
*
*
When the LoadBalancer states changes, e.g., Subchannels has become or stopped being READY, and
* we want subsequent RPCs to use the latest list of READY Subchannels, LoadBalancer would create a
* new picker, which holds a snapshot of the latest Subchannel list. Refer to the javadoc of {@link
* org.apache.ratis.thirdparty.io.grpc.LoadBalancer.SubchannelStateListener#onSubchannelState onSubchannelState()} how to do
* this properly.
*
*
No synchronization should be necessary between LoadBalancer and its pickers if you follow
* the pattern above. It may be possible to implement in a different way, but that would usually
* result in more complicated threading.
*
* @since 1.2.0
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1771")
@NotThreadSafe
public abstract class LoadBalancer {
@Internal
@NameResolver.ResolutionResultAttr
public static final Attributes.Key