Example #1
0
        GetNextPickerAsync(CancellationToken cancellationToken)
        {
            ConnectionManagerLog.PickWaiting(Logger);

            Debug.Assert(Monitor.IsEntered(_lock));

            var nextPickerTcs = _nextPickerTcs;

            await _nextPickerLock.WaitAsync(cancellationToken).ConfigureAwait(false);

            try
            {
                using (cancellationToken.Register(s => ((TaskCompletionSource <SubchannelPicker?>)s !).TrySetCanceled(), nextPickerTcs))
                {
                    var nextPicker = await nextPickerTcs.Task.ConfigureAwait(false);

                    lock (_lock)
                    {
                        _nextPickerTcs = new TaskCompletionSource <SubchannelPicker>(TaskCreationOptions.RunContinuationsAsynchronously);
                    }

                    return(nextPicker);
                }
            }
            finally
            {
                _nextPickerLock.Release();
            }
        }
Example #2
0
        public void UpdateState(BalancerState state)
        {
            lock (_lock)
            {
                if (State != state.ConnectivityState)
                {
                    ConnectionManagerLog.ChannelStateUpdated(Logger, state.ConnectivityState);
                    State = state.ConnectivityState;

                    // Iterate in reverse to reduce shifting items in the list as watchers are removed.
                    for (var i = _stateWatchers.Count - 1; i >= 0; i--)
                    {
                        var stateWatcher = _stateWatchers[i];

                        if (stateWatcher.WaitForState == null || stateWatcher.WaitForState == State)
                        {
                            stateWatcher.Tcs.SetResult(null);
                            _stateWatchers.RemoveAt(i);
                        }
                    }
                }

                if (!Equals(_picker, state.Picker))
                {
                    ConnectionManagerLog.ChannelPickerUpdated(Logger);
                    _picker = state.Picker;
                    _nextPickerTcs.SetResult(state.Picker);
                    _nextPickerTcs = new TaskCompletionSource <SubchannelPicker>(TaskCreationOptions.RunContinuationsAsynchronously);
                }
            }
        }
Example #3
0
        private Task <SubchannelPicker> GetPickerAsync(SubchannelPicker?currentPicker, CancellationToken cancellationToken)
        {
            lock (_lock)
            {
                if (_picker != null && _picker != currentPicker)
                {
                    Debug.Assert(_pickerTask != null);
                    return(_pickerTask);
                }
                else
                {
                    ConnectionManagerLog.PickWaiting(Logger);

                    return(_nextPickerTcs.Task.WaitAsync(cancellationToken));
                }
            }
        }
Example #4
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}");
                }
            }
        }
Example #5
0
        private void OnResolverResult(ResolverResult result)
        {
            if (_balancer == null)
            {
                throw new InvalidOperationException($"Load balancer not configured.");
            }

            var channelStatus = result.Status;

            // https://github.com/grpc/proposal/blob/master/A21-service-config-error-handling.md
            // Additionally, only use resolved service config if not disabled.
            LoadBalancingConfig?loadBalancingConfig = null;

            if (!DisableResolverServiceConfig)
            {
                ServiceConfig?workingServiceConfig = null;
                if (result.ServiceConfig == null)
                {
                    // Step 4 and 5
                    if (result.ServiceConfigStatus == null)
                    {
                        // Step 5: Use default service config if none is provided.
                        _previousServiceConfig = DefaultServiceConfig;
                        workingServiceConfig   = DefaultServiceConfig;
                    }
                    else
                    {
                        // Step 4
                        if (_previousServiceConfig == null)
                        {
                            // Step 4.ii: If no config was provided or set previously, then treat resolution as a failure.
                            channelStatus = result.ServiceConfigStatus.GetValueOrDefault();
                        }
                        else
                        {
                            // Step 4.i: Continue using previous service config if it was set and a new one is not provided.
                            workingServiceConfig = _previousServiceConfig;
                            ConnectionManagerLog.ResolverServiceConfigFallback(Logger, result.ServiceConfigStatus.GetValueOrDefault());
                        }
                    }
                }
                else
                {
                    // Step 3: Use provided service config if it is set.
                    workingServiceConfig   = result.ServiceConfig;
                    _previousServiceConfig = result.ServiceConfig;
                }


                if (workingServiceConfig?.LoadBalancingConfigs.Count > 0)
                {
                    if (!ChildHandlerLoadBalancer.TryGetValidServiceConfigFactory(workingServiceConfig.LoadBalancingConfigs, LoadBalancerFactories, out loadBalancingConfig, out var _))
                    {
                        ConnectionManagerLog.ResolverUnsupportedLoadBalancingConfig(Logger, workingServiceConfig.LoadBalancingConfigs);
                    }
                }
            }
            else
            {
                if (result.ServiceConfig != null)
                {
                    ConnectionManagerLog.ResolverServiceConfigNotUsed(Logger);
                }
            }

            var state = new ChannelState(
                channelStatus,
                result.Addresses,
                loadBalancingConfig,
                BalancerAttributes.Empty);

            lock (_lock)
            {
                _balancer.UpdateChannelState(state);
                _resolverStartedTcs.TrySetResult(null);
            }
        }