@@ -35,6 +35,8 @@ namespace Grpc.Net.Client.Balancer.Internal
3535{
3636 internal class ConnectionManager : IDisposable , IChannelControlHelper
3737 {
38+ private static readonly ServiceConfig DefaultServiceConfig = new ServiceConfig ( ) ;
39+
3840 private readonly SemaphoreSlim _nextPickerLock ;
3941 private readonly object _lock ;
4042 internal readonly Resolver _resolver ;
@@ -50,6 +52,7 @@ internal class ConnectionManager : IDisposable, IChannelControlHelper
5052 private Task ? _resolveTask ;
5153 private TaskCompletionSource < SubchannelPicker > _nextPickerTcs ;
5254 private int _currentSubchannelId ;
55+ private ServiceConfig ? _previousServiceConfig ;
5356
5457 internal ConnectionManager (
5558 Resolver resolver ,
@@ -148,30 +151,65 @@ private void OnResolverResult(ResolverResult result)
148151 throw new InvalidOperationException ( $ "Load balancer not configured.") ;
149152 }
150153
154+ var channelStatus = result . Status ;
155+
151156 // https://github.com/grpc/proposal/blob/master/A21-service-config-error-handling.md
152- // 1. Only use resolved service config if not disabled.
153- // 2. Only use resolved service config if valid. This includes having a factory for one of the load balancing configs.
157+ // Additionally, only use resolved service config if not disabled.
154158 LoadBalancingConfig ? loadBalancingConfig = null ;
155- if ( result . ServiceConfig != null )
159+ if ( ! DisableResolverServiceConfig )
156160 {
157- if ( ! DisableResolverServiceConfig )
161+ ServiceConfig ? workingServiceConfig = null ;
162+ if ( result . ServiceConfig == null )
158163 {
159- if ( result . ServiceConfig . LoadBalancingConfigs . Count > 0 )
164+ // Step 4 and 5
165+ if ( result . ServiceConfigStatus == null )
166+ {
167+ // Step 5: Use default service config if none is provided.
168+ _previousServiceConfig = DefaultServiceConfig ;
169+ workingServiceConfig = DefaultServiceConfig ;
170+ }
171+ else
160172 {
161- if ( ! ChildHandlerLoadBalancer . TryGetValidServiceConfigFactory ( result . ServiceConfig . LoadBalancingConfigs , LoadBalancerFactories , out loadBalancingConfig , out var _ ) )
173+ // Step 4
174+ if ( _previousServiceConfig == null )
162175 {
163- ConnectionManagerLog . ResolverUnsupportedLoadBalancingConfig ( Logger , result . ServiceConfig . LoadBalancingConfigs ) ;
176+ // Step 4.ii: If no config was provided or set previously, then treat resolution as a failure.
177+ channelStatus = result . ServiceConfigStatus . GetValueOrDefault ( ) ;
178+ }
179+ else
180+ {
181+ // Step 4.i: Continue using previous service config if it was set and a new one is not provided.
182+ workingServiceConfig = _previousServiceConfig ;
183+ ConnectionManagerLog . ResolverServiceConfigFallback ( Logger , result . ServiceConfigStatus . GetValueOrDefault ( ) ) ;
164184 }
165185 }
166186 }
167187 else
188+ {
189+ // Step 3: Use provided service config if it is set.
190+ workingServiceConfig = result . ServiceConfig ;
191+ _previousServiceConfig = result . ServiceConfig ;
192+ }
193+
194+
195+ if ( workingServiceConfig ? . LoadBalancingConfigs . Count > 0 )
196+ {
197+ if ( ! ChildHandlerLoadBalancer . TryGetValidServiceConfigFactory ( workingServiceConfig . LoadBalancingConfigs , LoadBalancerFactories , out loadBalancingConfig , out var _ ) )
198+ {
199+ ConnectionManagerLog . ResolverUnsupportedLoadBalancingConfig ( Logger , workingServiceConfig . LoadBalancingConfigs ) ;
200+ }
201+ }
202+ }
203+ else
204+ {
205+ if ( result . ServiceConfig != null )
168206 {
169207 ConnectionManagerLog . ResolverServiceConfigNotUsed ( Logger ) ;
170208 }
171209 }
172210
173211 var state = new ChannelState (
174- result . Status ,
212+ channelStatus ,
175213 result . Addresses ,
176214 loadBalancingConfig ,
177215 BalancerAttributes . Empty ) ;
@@ -513,6 +551,9 @@ internal static class ConnectionManagerLog
513551 private static readonly Action < ILogger , Exception ? > _pickWaiting =
514552 LoggerMessage . Define ( LogLevel . Trace , new EventId ( 14 , "PickWaiting" ) , "Waiting for a new picker." ) ;
515553
554+ private static readonly Action < ILogger , Status , Exception ? > _resolverServiceConfigFallback =
555+ LoggerMessage . Define < Status > ( LogLevel . Debug , new EventId ( 15 , "ResolverServiceConfigFallback" ) , "Falling back to previously loaded service config. Resolver failure when retreiving or parsing service config with status: {Status}" ) ;
556+
516557 public static void ResolverRefreshRequested ( ILogger logger )
517558 {
518559 _resolverRefreshRequested ( logger , null ) ;
@@ -586,6 +627,11 @@ public static void PickWaiting(ILogger logger)
586627 {
587628 _pickWaiting ( logger , null ) ;
588629 }
630+
631+ public static void ResolverServiceConfigFallback ( ILogger logger , Status status )
632+ {
633+ _resolverServiceConfigFallback ( logger , status , null ) ;
634+ }
589635 }
590636}
591637#endif
0 commit comments