I have implemented a dynamic Redis data source, I find a interface named LettuceClientConfigurationBuilderCustomizer that can help me build LettuceConnectionFactory using custom configuration.

However, there is an obvious disadvantage of this interface. When I try to use the method of setClientResource(), I cannot get the context information of the LettuceConnectionFactory.

This prevents me from creating Tracing(TracingContext needs tags such as hostname, database... ) in ClientResources based on the current context (such as RedisProperties).

I can submit a pull request that contains the builder interface that has been modified to add the redisProperties parameter

Comment From: philwebb

Closing in favor of PR #21123

Comment From: philwebb

PR #21123 was closed because it breaks back compatibility. The suggested solution was:

@Bean
public JedisClientConfigurationBuilderCustomizer myCustomizer(RedisProperties redisProperties) {
    return (builder) -> {
        // use builder and redisProperties here
    }
}

The response to that was:

When there is only one redis data source configuration in the system, there is no problem. If there are multiple configurations, it is not known which properties the current Builder is associated with.

@zmapleshine Can you please provide a small sample application that shows what you are trying to do? I especially interested in why you have multiple RedisProperties beans.

Comment From: zmapleshine

@philwebb

This is a code segment in BeanDefinitionRegistryPostProcessor

   @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        DynamicRedisProperties dynamicRedisProperties = binder.bind("zmaple.redis", DynamicRedisProperties.class).get();
        dynamicRedisProperties.getProperties().forEach((poolName, redisProperties) -> {
            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                    .genericBeanDefinition(DynamicConnectionFactoryBean.class)
                    .addConstructorArgValue(redisProperties)
                    .getRawBeanDefinition();
            registry.registerBeanDefinition(PREFIX + "_" + poolName, beanDefinition);
        });
    }

The logic of initialization is basically the same as LettuceConnectionConfiguration.

The following is a code example of custom configuration. Although it is not correct to inject LettuceConnectionFactory, it is used to illustrate that I need context information to help me better set up ClientResource,but the current interface cannot reach me Requirements.

i want set Tracing in ClientResource ,in Tracing interface , I need to know the current redis configuration in order to get the hostname etc. as a trace tag。

see initialTraceContextProvider()

@Bean
    public List<LettuceClientConfigurationBuilderCustomizer> lettuceClientConfigurationBuilderCustomizer(ObjectProvider<LettuceConnectionFactory> lettuceConnectionFactories) {
        List<LettuceClientConfigurationBuilderCustomizer> lettuceClientConfigurationBuilderCustomizers = new ArrayList<>();
        lettuceConnectionFactories.orderedStream().forEach(lettuceConnectionFactory -> {
            lettuceClientConfigurationBuilderCustomizers.add(clientConfigurationBuilder -> {
                clientConfigurationBuilder.clientResources(ClientResources.builder()
                        .tracing(new Tracing() {
                            @Override
                            public TracerProvider getTracerProvider() {
                                return () -> new Tracer() {
                                    @Override
                                    public Span nextSpan() {
                                        return null;
                                    }

                                    @Override
                                    public Span nextSpan(TraceContext traceContext) {
                                        return null;
                                    }
                                };
                            }

                            @Override
                            public TraceContextProvider initialTraceContextProvider() {
                                return () -> {
                                    Span activeSpan = GlobalTracer.get().activeSpan();
                                    io.opentracing.Tracer.SpanBuilder builder = GlobalTracer.get()
                                            .buildSpan(lettuceConnectionFactory.getClientName())
                                            .withTag("host",lettuceConnectionFactory.getHostName())
                                            .withTag("database", lettuceConnectionFactory.getDatabase());
                                    if (Objects.nonNull(activeSpan)) {
                                        builder.asChildOf(activeSpan.context());
                                    }
                                    return new TraceContext() {
                                    };
                                };
                            }

                            @Override
                            public boolean isEnabled() {
                                return true;
                            }

                            @Override
                            public boolean includeCommandArgsInSpanTags() {
                                return false;
                            }

                            @Override
                            public Endpoint createEndpoint(SocketAddress socketAddress) {
                                return new Endpoint() {
                                };
                            }
                        })
                        .build());
            });
        });
        return lettuceClientConfigurationBuilderCustomizers;
    }

Comment From: zmapleshine

Supplementary code snippets:

DynamicConnectionFactory

    protected RedisConnectionFactory chooseDataSource() {
        String poolName = DynamicRedisDatabaseHolder.peek();
        if (StringUtils.isEmpty(poolName)) {
            return primaryConnectionFactory;
        }
        return connectionFactoryMap.get(PREFIX + "_" + poolName);
    }

Comment From: philwebb

I don't think we want to support such a complex setup directly in Spring Boot. I suspect that your lettuceClientConfigurationBuilderCustomizer @Bean method will also cause problems since it's returning a List.

If you're wanting to dynamically configure multiple RedisConnectionFactory beans, I think you're best approach would be to duplicate the code you need from LettuceConnectionConfiguration and apply any customizations that you need directly at that point.

Comment From: zmapleshine

@philwebb in LettuceConnectionConfiguration ,method getLettuceClientConfiguration do a lot of configuration. And I just want to pass the context information of the currently constructed lettuceConnectionFactory when applying the custom configuration,now I cannot get the context when applying a custom configuration. If I want to achieve my goal, I have to copy the entire configuration. Is this a bit inappropriate? Is it possible to add context information to the custom interface?

Comment From: philwebb

I'm afraid not. We intentionally keep customizers as simple as possible. We're pretty consistent with this design and we don't think it's a good idea to tie a customizer to a properties object. Doing so would mean that a LettuceClientConfigurationBuilder is (and always will be) tied to a RedisProperties. That potentially paints us into a corner and makes it hard to break such a link in the future. We also prefer to keep *Properties objects as a somewhat internal concept and we don't want them to be part of the more public *Customizer APIs.