Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Per tutte le eccezioni che consentono un nuovo tentativo, in genere è preferibile utilizzare una strategia di ripetizione dei tentativi e backoff esponenziale che preveda tempi di attesa progressivamente più lunghi tra un tentativo e l'altro, in modo da gestire al meglio i problemi temporanei, come gli errori ConcurrentModificationException
. Di seguito viene illustrato un esempio di modello di ripetizione dei tentativi e backoff esponenziale:
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);
}
}