All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aliyun.tair.retry.JedisRetryCommand Maven / Gradle / Ivy

package com.aliyun.tair.retry;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisException;

public abstract class JedisRetryCommand {
    private static final Logger LOGGER = LoggerFactory.getLogger(JedisRetryCommand.class);

    private final JedisPool jedisPool;
    private final int maxRetries;
    private final Duration maxTotalRetriesDuration;

    public JedisRetryCommand(JedisPool jedisPool, int maxRetries, Duration maxTotalRetriesDuration) {
        this.jedisPool = jedisPool;
        this.maxRetries = maxRetries;
        this.maxTotalRetriesDuration = maxTotalRetriesDuration;
    }

    public abstract T execute(Jedis connection);

    /**
     * This method will retry maxRetries times and check
     * whether the time reaches maxTotalRetriesDuration
     * If any one of the conditions is met, JedisException will be thrown
     */
    public T runWithRetries() {
        Instant deadline = Instant.now().plus(maxTotalRetriesDuration);
        Exception lastException = null;

        for (int retriesLeft = maxRetries; retriesLeft > 0; retriesLeft--) {
            Jedis connection = null;
            try {
                connection = jedisPool.getResource();
                return execute(connection);
            } catch (Exception jce) {
                lastException = jce;
                LOGGER.warn("Redis throw an exception: {}", connection, jce);
                sleep(getBackoffSleepMillis(retriesLeft, deadline));
            } finally {
                releaseConnection(connection);
            }
            if (Instant.now().isAfter(deadline)) {
                throw new JedisException("Command retry deadline exceeded.");
            }
        }

        JedisException maxRetriesException
            = new JedisException("No more command retries left.");
        maxRetriesException.addSuppressed(lastException);
        throw maxRetriesException;
    }

    private static long getBackoffSleepMillis(int retriesLeft, Instant deadline) {
        if (retriesLeft <= 0) {
            return 0;
        }

        long millisLeft = Duration.between(Instant.now(), deadline).toMillis();
        if (millisLeft < 0) {
            throw new JedisException("Command retry deadline exceeded.");
        }

        /**
         * Theoretical data, the actual TIMEOUT should be considered
         * At time 0.0, 5 attempts left, sleep (10s/(5*5)) = 0.4s
         * At time 0.4, 4 attempts left, sleep (9.6s/(4*4)) = 0.6s
         * At time 1.0, 3 attempts left, sleep (9s/(3*3)) = 1.0s
         * At time 2.0, 2 attempts left, sleep (8/(2*2)) = 2.0s
         * At time 6.0, 1 attempts left, sleep (4/(1*1)) = 4.0s
         */
        return millisLeft / ((long)retriesLeft * (retriesLeft + 1));
    }

    private void sleep(long sleepMillis) {
        try {
            TimeUnit.MILLISECONDS.sleep(sleepMillis);
        } catch (InterruptedException e) {
            throw new JedisException(e);
        }
    }

    private void releaseConnection(Jedis connection) {
        if (connection != null) {
            connection.close();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy