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(); } }
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); } } }
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)); } } }
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}"); } } }
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); } }