Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RedisCache.putIfAbsent does not set the TTL on the element [DATAREDIS-542] #1119

spring-projects-issues opened this issue Jul 28, 2016 · 4 comments
type: bug type: task


Copy link

@spring-projects-issues spring-projects-issues commented Jul 28, 2016

Steven Urquhart opened DATAREDIS-542 and commented

RedisCache.putIfAbsent does not set the TTL on the element in redis. This appears to be because the RedisCachePutIfAbsentCallback performs the wrong check to decide whether it should set the TTL.

The check at

Looks like it should check for null being returned from the set rather than checking equality with the supplied value

Affects: 1.7.1 (Hopper SR1), 1.7.2 (Hopper SR2), 1.6.6 (Gosling SR6)

Referenced from: pull request #224

Backported to: 1.7.5 (Hopper SR5), 1.6.7 (Gosling SR7)

Copy link

@spring-projects-issues spring-projects-issues commented Aug 30, 2016

Mark Paluch commented

Hi Steven Urquhart,
could you provide us with a test case to reproduce that issue? We weren't able to reproduce that issue

Copy link

@spring-projects-issues spring-projects-issues commented Aug 30, 2016

Steven Urquhart commented

Hi Mark,
I dont have a specific test case but if you try to putIfAbsent an element that has a time to live specfied on it it wont get set. The processKeyExpiration is never called for it as the check at the line I mentioned should be against null, rather than the value passed. This is when there is no existing data in the cache for that key

Copy link

@spring-projects-issues spring-projects-issues commented Aug 30, 2016

Mark Paluch commented

That's what we did. You can find the test code and Redis MONITOR output below:

    public void putIfAbsent() {

        RedisCache rc = new RedisCache("foo", null, this.template, 60L);

        rc.putIfAbsent("key-1", "value-1");


Redis output:

1472556473.395019 [0] "DEL" "testCache~lock"
1472556473.396916 [0] "EXISTS" "foo~lock"
1472556473.397042 [0] "EXISTS" "foo~lock"
1472556473.397185 [0] "SETNX" "key-1" "value-1"
1472556473.397335 [0] "GET" "key-1"
1472556473.397519 [0] "EXPIRE" "key-1" "60"
1472556473.397690 [0] "ZADD" "foo~keys" "0.0" "key-1"
1472556473.397764 [0] "EXPIRE" "foo~keys" "60"

In a highly concurrent use it is possible that two clients call SETNX at a time, that's why we read after write. In that case, only one writer sets the timeout and performs key maintenance which is not necessarily the last caller

Copy link

@spring-projects-issues spring-projects-issues commented Sep 15, 2016

Durgalal Patel commented

Hi Mark Paluch,

We are also facing the same issue.
After invoking putIfAbsent api the key/value is set in Redis, however the ttl of key is NOT set as per the default expiration and hence key is not removed from redis even after the expected default expiration time has elapsed.
When we check ttl value using REDIS CLI command, it returns -1 instead of expected default value we have set.

// cache object is spring autowired instance
cache.putIfAbsent( "my_key", "my_value" );

REDIS CLI Output :

ttl "\xac\xed\x00\x05t\x00\x0cmy_key"
(integer) -1

Below is spring configuration code to generate the autowired cache object

public JedisConnectionFactory jedisConnectionFactory() {
    JedisConnectionFactory factory = new JedisConnectionFactory();
    factory.setHostName( redisHostName );
    factory.setPort( redisPort );
    factory.setTimeout( timeout );
    factory.setPassword( redisPassword );
    factory.setDatabase( databaseIndex );
    factory.setUsePool( true );
    return factory;

public RedisTemplate< Object, Object > redisTemplate() {
    RedisTemplate< Object, Object > redisTemplate = new RedisTemplate< Object, Object >();
    redisTemplate.setConnectionFactory( jedisConnectionFactory() );
    return redisTemplate;

@Bean(name = "cache")
public CacheManager cache() {
	RedisCacheManager redisCacheManager = new RedisCacheManager( redisTemplate() );
	return redisCacheManager;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
type: bug type: task
None yet

No branches or pull requests

2 participants