Skip to content

Redis Keyspace configuration not working as expected #3109

@felixwiemuth

Description

@felixwiemuth

I am trying to set up a Redis repository with programmatic keyspaces configuration according to https://docs.spring.io/spring-data/redis/reference/redis/redis-repositories/keyspaces.html

I would like to load the timeToLive from a configuration properties.

Problem

Here is the attempt:

@Configuration
@EnableRedisRepositories // NOT PROVIDED because using RedisMappingContext: (keyspaceConfiguration = KeyspaceConfig::class)
class RedisRepositoryConfig(
    val myConfigurationProperties: MyConfigurationProperties
) {
    @Bean
    fun redisTemplate(redisConnectionFactory: RedisConnectionFactory?): RedisTemplate<*, *> {
        val template = RedisTemplate<ByteArray?, ByteArray?>()
        template.connectionFactory = redisConnectionFactory
        return template
    }

    @Bean
    fun keyValueMappingContext() =
        RedisMappingContext(
            MappingConfiguration(IndexConfiguration(), KeyspaceConfig(
                ttl = myConfigurationProperties.redisTTLSeconds.also { println("Creating KeyspaceConfig with TTL=$it") }
            ))
        )
}

// It does not make a difference whether this class is defined within the RedisRepositoryConfig or not
class KeyspaceConfig(
    val ttl: Long,
) : KeyspaceConfiguration() {
    init {
        println("KeyspaceConfig constructor called with TTL=$ttl")
    }
    override fun initialConfiguration(): Iterable<KeyspaceSettings> =
        listOf(
            KeyspaceSettings(MyData::class.java, "MyHash").apply {
                timeToLive = ttl.also { println("initialConfiguration(): TTL property is $ttl") }
            }
        )
}

@RedisHash // This annotation seems still needed
data class MyData(
    @Id
    val resourceId: String,
    val data: String
)

As opposed to the documentation, the KeyspaceSettings are not static, so we have to pass or access the configuration somehow.

  • I tried the timeToLivePropertyName = "my.config.redis-ttl-seconds" field in KeyspaceSettings, but it did not have any effect.
  • I tried injecting the MyConfigurationProperties in the KeyspaceConfig constructor (and using @EnableRedisRepositories(keyspaceConfiguration = KeyspaceConfig::class)) , but that doesn't work
  • So I tried instead passing the value in the constructor (as shown above), in the keyValueMappingContext() bean, but it doesn't work as expected, see below

Running the application results in the following output:

Creating KeyspaceConfig with TTL=5
initialConfiguration(): TTL property is 0
KeyspaceConfig constructor called with TTL=5

This shows that KeyspaceConfig is instantiated from elsewhere, and that instance is used to call initialConfiguration() on, instead of the one that is constructed in keyValueMappingContext(), which I would expect.

Workaround

If not declaring a separate KeyspaceConfig class, but instead implement a KeyspaceConfiguration on-the-fly, it works:

    @Bean
    fun keyValueMappingContext() =
        RedisMappingContext(
            MappingConfiguration(IndexConfiguration(), object : KeyspaceConfiguration() {
                override fun initialConfiguration(): Iterable<KeyspaceSettings> =
                    listOf(
                        KeyspaceSettings(MyData::class.java, "MyHash").apply {
                            timeToLive = myConfigurationProperties.redisTTLSeconds.also { println("initialConfiguration(): TTL is $it") }
                        }
                    )
            }
        ))

Output:

initialConfiguration(): TTL is 5

Issues

  • Why does it behave in the way it does, and is this correct?
  • Can the documentation be updated to show how to properly use KeyspaceConfig when one needs to load properties dynamically or from configuration properties?

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions