package com.vladmihalcea.flexypool.strategy;
import com.vladmihalcea.flexypool.adaptor.PoolAdapter;
import com.vladmihalcea.flexypool.common.ConfigurationProperties;
import com.vladmihalcea.flexypool.connection.ConnectionRequestContext;
import com.vladmihalcea.flexypool.exception.AcquireTimeoutException;
import com.vladmihalcea.flexypool.metric.Histogram;
import com.vladmihalcea.flexypool.metric.Metrics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* <code>RetryConnectionAcquiringStrategy</code> extends the {@link AbstractConnectionAcquiringStrategy}
* and it allows multiple acquiring attempts before giving up by rethrowing the {@link AcquireTimeoutException}
*
* @author Vlad Mihalcea
* @since 1.0
*/
public final class RetryConnectionAcquiringStrategy<T extends DataSource> extends AbstractConnectionAcquiringStrategy {
public static final String RETRY_ATTEMPTS_HISTOGRAM = "retryAttemptsHistogram";
private static final Logger LOGGER = LoggerFactory.getLogger(RetryConnectionAcquiringStrategy.class);
/**
* The {@link com.vladmihalcea.flexypool.strategy.RetryConnectionAcquiringStrategy.Factory} class allows
* creating this strategy for a given {@link ConfigurationProperties}
*/
public static class Factory<T extends DataSource> implements ConnectionAcquiringStrategyFactory<RetryConnectionAcquiringStrategy, T> {
private final int retryAttempts;
public Factory(int retryAttempts) {
this.retryAttempts = retryAttempts;
}
/**
* Creates a {@link com.vladmihalcea.flexypool.strategy.RetryConnectionAcquiringStrategy} for a given
* {@link ConfigurationProperties}
*
* @param configurationProperties configurationProperties
* @return strategy
*/
public RetryConnectionAcquiringStrategy newInstance(ConfigurationProperties<T, Metrics, PoolAdapter<T>> configurationProperties) {
return new RetryConnectionAcquiringStrategy(
configurationProperties, retryAttempts
);
}
}
private final int retryAttempts;
private final Histogram retryAttemptsHistogram;
/**
* Create the strategy for the given configurationProperties and the retryAttempts.
*
* @param configurationProperties configurationProperties
* @param retryAttempts maximum retry attempts
*/
private RetryConnectionAcquiringStrategy(ConfigurationProperties<? extends DataSource, Metrics, PoolAdapter> configurationProperties, int retryAttempts) {
super(configurationProperties);
this.retryAttempts = validateRetryAttempts(retryAttempts);
this.retryAttemptsHistogram = configurationProperties.getMetrics().histogram(RETRY_ATTEMPTS_HISTOGRAM);
}
private int validateRetryAttempts(int retryAttempts) {
if (retryAttempts <= 0) {
throw new IllegalArgumentException("retryAttempts must ge greater than 0!");
}
return retryAttempts;
}
/**
* {@inheritDoc}
*/
@Override
public Connection getConnection(ConnectionRequestContext requestContext) throws SQLException {
int remainingAttempts = retryAttempts;
try {
do {
try {
return getConnectionFactory().getConnection(requestContext);
} catch (AcquireTimeoutException e) {
requestContext.incrementAttempts();
remainingAttempts--;
LOGGER.warn("Can't acquireConnection connection, remaining retry attempts {}", remainingAttempts);
if (remainingAttempts <= 0) {
throw e;
}
}
} while (true);
} finally {
int attemptedRetries = requestContext.getRetryAttempts();
if (attemptedRetries > 0) {
retryAttemptsHistogram.update(attemptedRetries);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "RetryConnectionAcquiringStrategy{" +
"retryAttempts=" + retryAttempts +
'}';
}
}