redis.clients.jedis.executors.RetryableCommandExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jedis Show documentation
Show all versions of jedis Show documentation
Jedis is a blazingly small and sane Redis java client.
package redis.clients.jedis.executors;
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.CommandObject;
import redis.clients.jedis.Connection;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.util.IOUtils;
import redis.clients.jedis.providers.ConnectionProvider;
public class RetryableCommandExecutor implements CommandExecutor {
private final Logger log = LoggerFactory.getLogger(getClass());
protected final ConnectionProvider provider;
protected final int maxAttempts;
protected final Duration maxTotalRetriesDuration;
public RetryableCommandExecutor(ConnectionProvider provider, int maxAttempts,
Duration maxTotalRetriesDuration) {
this.provider = provider;
this.maxAttempts = maxAttempts;
this.maxTotalRetriesDuration = maxTotalRetriesDuration;
}
@Override
public void close() {
IOUtils.closeQuietly(this.provider);
}
@Override
public final T executeCommand(CommandObject commandObject) {
Instant deadline = Instant.now().plus(maxTotalRetriesDuration);
int consecutiveConnectionFailures = 0;
JedisException lastException = null;
for (int attemptsLeft = this.maxAttempts; attemptsLeft > 0; attemptsLeft--) {
Connection connection = null;
try {
connection = provider.getConnection(commandObject.getArguments());
return execute(connection, commandObject);
} catch (JedisConnectionException jce) {
lastException = jce;
++consecutiveConnectionFailures;
log.debug("Failed connecting to Redis: {}", connection, jce);
// "- 1" because we just did one, but the attemptsLeft counter hasn't been decremented yet
boolean reset = handleConnectionProblem(attemptsLeft - 1, consecutiveConnectionFailures, deadline);
if (reset) {
consecutiveConnectionFailures = 0;
}
} finally {
if (connection != null) {
connection.close();
}
}
if (Instant.now().isAfter(deadline)) {
throw new JedisException("Retry deadline exceeded.");
}
}
JedisException maxAttemptsException = new JedisException("No more attempts left.");
maxAttemptsException.addSuppressed(lastException);
throw maxAttemptsException;
}
/**
* WARNING: This method is accessible for the purpose of testing.
* This should not be used or overriden.
*/
protected T execute(Connection connection, CommandObject commandObject) {
return connection.executeCommand(commandObject);
}
/**
* Related values should be reset if TRUE
is returned.
*
* @param attemptsLeft
* @param consecutiveConnectionFailures
* @param doneDeadline
* @return true - if some actions are taken
*
false - if no actions are taken
*/
private boolean handleConnectionProblem(int attemptsLeft, int consecutiveConnectionFailures, Instant doneDeadline) {
if (consecutiveConnectionFailures < 2) {
return false;
}
sleep(getBackoffSleepMillis(attemptsLeft, doneDeadline));
return true;
}
private static long getBackoffSleepMillis(int attemptsLeft, Instant deadline) {
if (attemptsLeft <= 0) {
return 0;
}
long millisLeft = Duration.between(Instant.now(), deadline).toMillis();
if (millisLeft < 0) {
throw new JedisException("Retry deadline exceeded.");
}
return millisLeft / (attemptsLeft * (attemptsLeft + 1));
}
/**
* WARNING: This method is accessible for the purpose of testing.
* This should not be used or overriden.
*/
protected void sleep(long sleepMillis) {
try {
TimeUnit.MILLISECONDS.sleep(sleepMillis);
} catch (InterruptedException e) {
throw new JedisException(e);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy