spring-bootredisspring-datajedisspring-data-redis

How are cache misses handled by spring-data-redis multiGet?


I am using a Redis cache (via the Jedis client), and I would like to use ValueOperations#multiGet, which takes a Collection of keys, and returns a List of objects from the cache, in the same order. My question is, what happens when some of the keys are in the cache, but others are not? I am aware that underneath, Redis MGET is used, which will return nil for any elements that are not in the cache.

I cannot find any documentation of how ValueOperations will interpret this response. I assume they will be null, and can certainly test it, but it would be dangerous to build a system around undocumented behavior.

For completeness, here is how the cache client is configured:

@Bean
public RedisConnectionFactory redisConnectionFactory() {
    JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();

    redisConnectionFactory.setHostName(address);
    redisConnectionFactory.setPort(port);

    redisConnectionFactory.afterPropertiesSet();
    return redisConnectionFactory;
}

@Bean
public ValueOperations<String, Object> someRedisCache(RedisConnectionFactory cf) {
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(cf);
    redisTemplate.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
    redisTemplate.afterPropertiesSet();
    return redisTemplate.opsForValue();
}

I am using spring-data-redis:2.1.4

So, is there any documentation around this, or some reliable source of truth?


Solution

  • After some poking around, it looks like the answer has something to do with the serializer used - in this case GenericJackson2JsonRedisSerializer. Not wanting to dig too much, I simply wrote a test validating that any (nil) values returned by Redis are convereted to null:

    @Autowired
    ValueOperations<String, SomeObject> valueOperations
    
    @Test
    void multiGet() {
        //Given
        SomeObject someObject = SomeObject
                .builder()
                .contentId("key1")
                .build()
        valueOperations.set("key1", someObject)
    
        //When
        List<SomeObject> someObjects = valueOperations.multiGet(Arrays.asList("key1", "nonexisting"))
    
        //Then
        assertEquals(2, someObjects.size())
        assertEquals(someObject, someObjects.get(0))
        assertEquals(null, someObjects.get(1))
    }
    

    So, in Redis, this:

    127.0.0.1:6379> MGET "\"key1\"" "\"nonexisting\""
    1) "{\"@class\":\"some.package.SomeObject\",\"contentId\":\"key1\"}"
    2) (nil)
    

    will result in a List of {SomeObject, null}

    With the above test in place, if the behavior ever changes due to a version update, someone changing the serializer used, etc. we will know before pushing to prod.