Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Pour toutes les exceptions qui autorisent une nouvelle tentative, il est généralement préférable d'utiliser une stratégie de backoff exponentiel et de nouvelle tentative qui allonge progressivement les temps d'attente entre les tentatives afin de mieux gérer les problèmes transitoires tels que les erreurs ConcurrentModificationException
. Voici un exemple de modèle de backoff exponentiel et de nouvelle tentative :
public static void main() {
try (Driver driver = getDriver(HOST_BOLT, getDefaultConfig())) {
retriableOperation(driver, "CREATE (n {prop:'1'})")
.withRetries(5)
.withExponentialBackoff(true)
.maxWaitTimeInMilliSec(500)
.call();
}
}
protected RetryableWrapper retriableOperation(final Driver driver, final String query){
return new RetryableWrapper<Void>() {
@Override
public Void submit() {
log.info("Performing graph Operation in a retry manner......");
try (Session session = driver.session(writeSessionConfig)) {
try (Transaction trx = session.beginTransaction()) {
trx.run(query).consume();
trx.commit();
}
}
return null;
}
@Override
public boolean isRetryable(Exception e) {
if (isCME(e)) {
log.debug("Retrying on exception.... {}", e);
return true;
}
return false;
}
private boolean isCME(Exception ex) {
return ex.getMessage().contains("Operation failed due to conflicting concurrent operations");
}
};
}
/**
* Wrapper which can retry on certain condition. Client can retry operation using this class.
*/
@Log4j2
@Getter
public abstract class RetryableWrapper<T> {
private long retries = 5;
private long maxWaitTimeInSec = 1;
private boolean exponentialBackoff = true;
/**
* Override the method with custom implementation, which will be called in retryable block.
*/
public abstract T submit() throws Exception;
/**
* Override with custom logic, on which exception to retry with.
*/
public abstract boolean isRetryable(final Exception e);
/**
* Define the number of retries.
*
* @param retries -no of retries.
*/
public RetryableWrapper<T> withRetries(final long retries) {
this.retries = retries;
return this;
}
/**
* Max wait time before making the next call.
*
* @param time - max polling interval.
*/
public RetryableWrapper<T> maxWaitTimeInMilliSec(final long time) {
this.maxWaitTimeInSec = time;
return this;
}
/**
* ExponentialBackoff coefficient.
*/
public RetryableWrapper<T> withExponentialBackoff(final boolean expo) {
this.exponentialBackoff = expo;
return this;
}
/**
* Call client method which is wrapped in submit method.
*/
public T call() throws Exception {
int count = 0;
Exception exceptionForMitigationPurpose = null;
do {
final long waitTime = exponentialBackoff ? Math.min(getWaitTimeExp(retries), maxWaitTimeInSec) : 0;
try {
return submit();
} catch (Exception e) {
exceptionForMitigationPurpose = e;
if (isRetryable(e) && count < retries) {
Thread.sleep(waitTime);
log.debug("Retrying on exception attempt - {} on exception cause - {}", count, e.getMessage());
} else if (!isRetryable(e)) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
}
} while (++count < retries);
throw new IOException(String.format(
"Retry was unsuccessful.... attempts %d. Hence throwing exception " + "back to the caller...", count),
exceptionForMitigationPurpose);
}
/*
* Returns the next wait interval, in milliseconds, using an exponential backoff
* algorithm.
*/
private long getWaitTimeExp(final long retryCount) {
if (0 == retryCount) {
return 0;
}
return ((long) Math.pow(2, retryCount) * 100L);
}
}