Skip to content

Make DelegatingServiceInstanceListSupplier implement SelectedInstanceCallback #1221

Closed

Description

@HJK181

I've tried to setup a custom load balancer as described in this post. However, with spring-cloud-starter-loadbalancer:3.1.4 I'm experiencing

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier' available: more than one 'primary' bean found among candidates: [CustomServiceInstanceListSupplier, retryAwareDiscoveryClientServiceInstanceListSupplier]
@Configuration
@LoadBalancerClient(value = "custom", configuration = CustomLoadBalancerConfiguration.class)
public class CustomLoadBalancerClient {
}

public class CustomLoadBalancerConfiguration {

	@Bean
	@Primary
	public ServiceInstanceListSupplier customServiceInstanceListSupplier(ConfigurableApplicationContext context, RedissonClient redissonClient) {
		ServiceInstanceListSupplier delegate = ServiceInstanceListSupplier.builder()
				.withDiscoveryClient()
				.withCaching()
				.build(context);
		return new CustomServiceInstanceListSupplier(delegate, redissonClient);
	}
}

public class CustomServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier implements SelectedInstanceCallback {
...
}

When deploying the application to Kubernetes along spring-cloud-kubernetes-facric8-loadbalancer:2.1.4 and activating spring.cloud.kubernetes.loadbalancer.mode=SERVICE it's even getting worse

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier' available: more than one 'primary' bean found among candidates: [customServiceInstanceListSupplier, kubernetesServicesListSupplier, retryAwareDiscoveryClientServiceInstanceListSupplier]

Am I doing something wrong, or is the example from the post wrong?

Activity

  1. HJK181 commented on Mar 27, 2023

    @HJK181
    ContributorAuthor

    Removing @Primary leads to

    Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier' available: expected single matching bean but found 2: customServiceInstanceListSupplier,kubernetesServicesListSupplier
    
  2. HJK181 commented on Mar 28, 2023

    @HJK181
    ContributorAuthor

    Further debugging revealed a second issue. My CustomServiceInstanceListSupplier implements SelectedInstanceCallback which the RoundRobinLoadBalancer respects by calling SelectedInstanceCallback#selectedServiceInstance

    public class RoundRobinLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    ...
    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
    			List<ServiceInstance> serviceInstances) {
    		Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances);
    		if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
    			((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
    		}
    		return serviceInstanceResponse;
    	}
    }
    

    However, with Spring retry on the classpath my CustomServiceInstanceListSupplier is wrapped in a RetryAwareServiceInstanceListSupplier which is not respecting SelectedInstanceCallback on its delegate.

    @Configuration(proxyBeanMethods = false)
    	@ConditionalOnBlockingDiscoveryEnabled
    	@ConditionalOnClass(RetryTemplate.class)
    	@Conditional(BlockingOnAvoidPreviousInstanceAndRetryEnabledCondition.class)
    	@AutoConfigureAfter(BlockingSupportConfiguration.class)
    	@ConditionalOnBean(ServiceInstanceListSupplier.class)
    	public static class BlockingRetryConfiguration {
    
    		@Bean
    		@ConditionalOnBean(DiscoveryClient.class)
    		@Primary
    		public ServiceInstanceListSupplier retryAwareDiscoveryClientServiceInstanceListSupplier(
    				ServiceInstanceListSupplier delegate) {
    			return new RetryAwareServiceInstanceListSupplier(delegate);
    		}
    
    	}
    

    I suspect that every implementation of ServiceInstanceListSupplier using some sort of "delegate" has the same problem, see RequestBasedStickySessionServiceInstanceListSupplier as an example.

  3. HJK181 commented on Mar 31, 2023

    @HJK181
    ContributorAuthor

    @OlgaMaciaszek any feedback appreciated as I'm stuck here. What I need to achieve is that my own ServiceInstanceListSupplier is used regardless of any auto-configured supplier. Or I need a way to use the auto-configured one as a delegate in my supplier, but that won't work as retryAwareDiscoveryClientServiceInstanceListSupplier is marked @Primary.

  4. HJK181 commented on Apr 12, 2023

    @HJK181
    ContributorAuthor

    @spencergibb Can you please have a look and give me hint if this is considered a bug or if any configuration possibility exists as a workaround? I've tried using @AutoConfigureAfter(LoadBalancerClientConfiguration.ReactiveRetryConfiguration.class) at my class annotated with @LoadBalancerClient without a chance. retryAwareDiscoveryClientServiceInstanceListSupplier is still configured after mine ...

  5. OlgaMaciaszek commented on Apr 12, 2023

    @OlgaMaciaszek
    Collaborator

    Hello, @HJK181. This is happening as for retrying failed requests, RetryAwareServiceInstanceListSupplier is enabled by default. As indicated in the docs, if you do not want it to be instantiated, you should set spring.cloud.loadbalancer.retry.avoidPreviousInstance to false. Does this solve your issue?

  6. HJK181 commented on Apr 12, 2023

    @HJK181
    ContributorAuthor

    I’d like to use the retry functionality, but in conjunction with my custom instance list supplier.

  7. OlgaMaciaszek commented on Apr 12, 2023

    @OlgaMaciaszek
    Collaborator

    That property does not disable retry. It only disables the RetryAwareServiceInstanceListSupplier that handles avoiding retrying on the same instance.

  8. 16 remaining items

  9. HJK181 commented on Apr 13, 2023

    @HJK181
    ContributorAuthor

    I can check tomorrow if I find the time to setup my IDE for contributing to the project. Is there any chance that it will be backported to 3.1.x?

  10. OlgaMaciaszek commented on Apr 13, 2023

    @OlgaMaciaszek
    Collaborator

    Unfortunately, not really. We mostly only backport bugfixes. This would be a new feature.

  11. added a commit that references this issue on Apr 14, 2023
    a44a77f
  12. HJK181 commented on Apr 19, 2023

    @HJK181
    ContributorAuthor
  13. added this to the 4.0.4 milestone on May 30, 2023
  14. moved this to In Progress in 2022.0.4on May 30, 2023
  15. moved this to In Progress in 2023.0.0-M1on May 30, 2023
  16. moved this from In Progress to Done in 2022.0.4on May 31, 2023
  17. moved this from In Progress to Done in 2023.0.0-M1on May 31, 2023
  18. changed the title [-]Custom configuration for the LoadBalancer doesn't work[/-] [+]Make DelegatingServiceInstanceListSupplier implement SelectedInstanceCallback[/+] on Jul 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions