Example #1
0
        public async ValueTask <(Subchannel Subchannel, BalancerAddress Address, Action <CompletionContext> OnComplete)> PickAsync(PickContext context, bool waitForReady, CancellationToken cancellationToken)
        {
            SubchannelPicker?previousPicker = null;

            // Wait for a valid picker. When the client state changes a new picker will be returned.
            // Cancellation will break out of the loop. Typically cancellation will come from a
            // deadline specified for a call being exceeded.
            while (true)
            {
                var currentPicker = await GetPickerAsync(previousPicker, cancellationToken).ConfigureAwait(false);

                ConnectionManagerLog.PickStarted(Logger);
                var result = currentPicker.Pick(context);

                switch (result.Type)
                {
                case PickResultType.Complete:
                    var subchannel = result.Subchannel !;
                    var address    = subchannel.CurrentAddress;

                    if (address != null)
                    {
                        ConnectionManagerLog.PickResultSuccessful(Logger, subchannel.Id, address);
                        return(subchannel, address, result.Complete);
                    }
                    else
                    {
                        ConnectionManagerLog.PickResultSubchannelNoCurrentAddress(Logger, subchannel.Id);
                        previousPicker = currentPicker;
                    }
                    break;

                case PickResultType.Queue:
                    ConnectionManagerLog.PickResultQueued(Logger);
                    previousPicker = currentPicker;
                    break;

                case PickResultType.Fail:
                    if (waitForReady)
                    {
                        ConnectionManagerLog.PickResultFailureWithWaitForReady(Logger, result.Status);
                        previousPicker = currentPicker;
                    }
                    else
                    {
                        ConnectionManagerLog.PickResultFailure(Logger, result.Status);
                        throw new RpcException(result.Status);
                    }
                    break;

                case PickResultType.Drop:
                    // Use metadata on the exception to signal the request was dropped.
                    // Metadata is checked by retry. If request was dropped then it isn't retried.
                    var metadata = new Metadata {
                        new Metadata.Entry(GrpcProtocolConstants.DropRequestTrailer, bool.TrueString)
                    };
                    throw new RpcException(result.Status, metadata);

                default:
                    throw new InvalidOperationException($"Unexpected pick result type: {result.Type}");
                }
            }
        }