1, scene

Redis version:

org.springframework.boot spring-boot-starter-data-redis

Redis framework:

Org. Redisson redisson – spring – the boot – starter 3.11.2

Redis operation tools class:

    public boolean setIfAbsent(String key, Object value, long time) {
        key = append(key);
        boolean flag = true;
        try {
            if (time > 0) {
                flag = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
            } else {
                flag = setIfAbsent(key, value);
            }
        }  catch (Exception e) {
            throw new DispatchCenterException(REDIS_SET_IF_ABSENT_ERR, "key=" + key, e);
        }
        return flag;
    }
Copy the code

In the actual scenario, redission is used as the framework for operating Redis, and the corresponding tool class is encapsulated. When we use setIfAbset, the console always has the problem of null pointer. The screenshot of the problem is as follows:

The exception stack shows that there is a problem with Redis, but the code does not show the problem, so only the debug process can trace the exception message. The final exception message was found during tracing.

2, the reason

public boolean setIfAbsent(String key, Object value, long time) { key = append(key); Boolean flag = true; // When the setIfAbsent response value is obtained, the encapsulated utility class uses Boolean instead of Boolean. try { if (time > 0) { flag = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS); } else { flag = setIfAbsent(key, value); } } catch (Exception e) { throw new DispatchCenterException(REDIS_SET_IF_ABSENT_ERR, "key=" + key, e); } return flag; Public Boolean setIfAbsent(K key, V value, long timeout,) {setIfAbsent(K key, V value, long timeout,)} TimeUnit unit) { byte[] rawKey = this.rawKey(key); byte[] rawValue = this.rawValue(value); Expiration expiration = Expiration.from(timeout, unit); return (Boolean)this.execute((connection) -> { return connection.set(rawKey, rawValue, expiration, SetOption.ifAbsent()); }, true); } . . . public T getNow() { return this.promise.getNow(); } // Return null;} // Return null;} // Return null; public V getNow() { Object result = this.result; return ! (result instanceof DefaultPromise.CauseHolder) && result ! = SUCCESS && result ! = UNCANCELLABLE ? result : null; }Copy the code

The scene of a

For idempotent normal redis requests, you can see that result returns 1 after the final redis execution, and the logical judgment of the final return value can see that result is returned directly.

Scenario 2

In the idempotent scenario, result is an Object and an unviewable Object. As you can see from the second figure, SUCESS’s Object points to the same memory address as result points to the same memory address, and the final result of all logical judgments is false. If the receiver is set to Boolean instead of Boolean, a null pointer will appear when unpacking after returning null. Because the normal logic is setIfAbsent if the key already exists in redis, redission should return false rather than null.

3. Solutions

Change the Boolean in the wrapped Redis utility class to the wrapped Boolean class.

    public Boolean setIfAbsent(String key, Object value, long time) {
        key = append(key);
        Boolean flag = true;
        try {
            if (time > 0) {
                flag = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
            } else {
                flag = setIfAbsent(key, value);
            }
        }  catch (Exception e) {
            throw new DispatchCenterException(REDIS_SET_IF_ABSENT_ERR, "key=" + key, e);
        }
        return Boolean.TRUE.equals(flag);
    }
Copy the code

4, summarize

SetIfAbsent returns false or 0 if a key is present in redis. Put an issue on GitHub and wait for a reply.