-
Notifications
You must be signed in to change notification settings - Fork 120
Installation Guide
Make sure you add this to your pom.xml
<flexy-pool.version>1.2.3</flexy-pool.version>
If you are using Java 1.7 or above you might want to add the following dependency:
<dependency>
<groupId>com.vladmihalcea.flexy-pool</groupId>
<artifactId>flexy-pool-core-java7</artifactId>
<version>${flexy-pool.version}</version>
</dependency>
In a managed environment (e.g. Java EE), we need to use a declarative configuration model:
Depending on your current project connection pool choice you need to follow one of these guides first:
-
JDBC Connection Pools
-
JTA Transaction Managers
Each FlexyPool instance requires a data source specific Configuration, which is supplied prior to constructing a FlexyPoolDataSource instance. You’ll have to rename your target Data Source (e.g from dataSource to poolingDataSource), as the FlexyPool Data Source will impersonate the actual Data Source.
A full Configuration would look like this:
@org.springframework.context.annotation.Configuration
public class FlexyPoolConfiguration {
public static class ConnectionAcquireTimeThresholdExceededEventListener
extends EventListener<ConnectionAcquireTimeThresholdExceededEvent> {
public static final Logger LOGGER = LoggerFactory.getLogger(
ConnectionAcquireTimeThresholdExceededEventListener.class);
public ConnectionAcquireTimeThresholdExceededEventListener() {
super(ConnectionAcquireTimeThresholdExceededEvent.class);
}
@Override
public void on(ConnectionAcquireTimeThresholdExceededEvent event) {
LOGGER.info("Caught event {}", event);
}
}
public static class ConnectionLeaseTimeThresholdExceededEventListener
extends EventListener<ConnectionLeaseTimeThresholdExceededEvent> {
public static final Logger LOGGER = LoggerFactory.getLogger(
ConnectionLeaseTimeThresholdExceededEventListener.class);
public ConnectionLeaseTimeThresholdExceededEventListener() {
super(ConnectionLeaseTimeThresholdExceededEvent.class);
}
@Override
public void on(ConnectionLeaseTimeThresholdExceededEvent event) {
LOGGER.info("Caught event {}", event);
}
}
public static class ConnectionAcquireTimeoutEventListener
extends EventListener<ConnectionAcquireTimeoutEvent> {
public static final Logger LOGGER = LoggerFactory.getLogger(
ConnectionAcquireTimeoutEventListener.class);
public ConnectionAcquireTimeoutEventListener() {
super(ConnectionAcquireTimeoutEvent.class);
}
@Override
public void on(ConnectionAcquireTimeoutEvent event) {
LOGGER.info("Caught event {}", event);
}
}
@Autowired
private PoolingDataSource poolingDataSource;
@Value("${flexy.pool.uniqueId}")
private String uniqueId;
@Bean
public Configuration<PoolingDataSource> configuration() {
return new Configuration.Builder<PoolingDataSource>(
uniqueId,
poolingDataSource,
BitronixPoolAdapter.FACTORY
)
.setMetricsFactory(MetricsFactoryResolver.INSTANCE.resolve())
.setConnectionProxyFactory(ConnectionDecoratorFactoryResolver.INSTANCE.resolve())
.setMetricLogReporterMillis(TimeUnit.SECONDS.toMillis(5))
.setJmxEnabled(true)
.setJmxAutoStart(true)
.setConnectionAcquireTimeThresholdMillis(50L)
.setConnectionLeaseTimeThresholdMillis(250L)
.setEventListenerResolver(new EventListenerResolver() {
@Override
public List<EventListener<? extends Event>> resolveListeners() {
return Arrays.<EventListener<? extends Event>>asList(
new ConnectionAcquireTimeoutEventListener(),
new ConnectionAcquireTimeThresholdExceededEventListener(),
new ConnectionAcquireTimeoutEventListener()
);
}
})
.build();
}
@Bean(initMethod = "start", destroyMethod = "stop")
public FlexyPoolDataSource dataSource() {
Configuration<PoolingDataSource> configuration = configuration();
return new FlexyPoolDataSource<PoolingDataSource>(configuration,
new IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory(5),
new RetryConnectionAcquiringStrategy.Factory(2)
);
}
}
Parameter name | Parameter type | Optional | Default value | Description |
---|---|---|---|---|
uniqueName |
Supplied through Configuration.Builder constructor |
false |
N/A |
Each FlexyPool instance requires a unique name so that JMX domains won’t clash |
targetDataSource |
Supplied through Configuration.Builder constructor |
false |
N/A |
The target DataSource we are monitoring |
poolAdapterFactory |
Supplied through Configuration.Builder constructor |
false |
N/A |
The specific pool adaptor factory associated to the target DataSource |
metricsFactory |
Supplied through Configuration.Builder setMetricsFactory |
true |
DropwizardMetrics.FACTORY or CodahaleMetrics.FACTORY |
The metrics factory allows to customize the metrics implementation. A Dropwizard/CodaHale Metrics implementation is being supplied |
connectionProxyFactory |
Supplied through Configuration.Builder setConnectionProxyFactory |
true |
ConnectionDecoratorFactory.INSTANCE |
The connection proxy provider. You can choose between decorating Connections and a JDK Dynamic Proxies implementation |
jmxEnabled |
Supplied through Configuration.Builder setJmxEnabled |
true |
true |
Specifies if the jmx service is enabled |
metricLogReporterMillis |
Supplied through Configuration.Builder setMetricLogReporterMillis |
true |
TimeUnit.MINUTES.toMillis(5) |
Specifies the metrics log reported interval |
eventListenerResolver |
Supplied through Configuration.Builder setEventListenerResolver |
true |
N/A |
Specifies a class name responsible for supplying the EventListener implementations |
connectionAcquireTimeThresholdMillis |
Supplied through Configuration.Builder setConnectionAcquireTimeThresholdMillis |
true |
Long.MAX_VALUE |
Specifies a time threshold for the connection acquire request. When the time limit is exceeded a log entry will be generated and a ConnectionAcquireTimeThresholdExceededEvent will be published. |
connectionLeaseTimeThresholdMillis |
Supplied through Configuration.Builder setConnectionLeaseTimeThresholdMillis |
true |
Long.MAX_VALUE |
Specifies a time threshold for the connection lease. When the time limit is exceeded a log entry will be generated and a ConnectionLeaseTimeThresholdExceededEvent will be published. |
@Bean(initMethod = "start", destroyMethod = "stop")
public FlexyPoolDataSource dataSource() {
Configuration<HikariDataSource> configuration = configuration();
return new FlexyPoolDataSource<HikariDataSource>(configuration,
new IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory(5),
new RetryConnectionAcquiringStrategy.Factory(2)
);
}
Parameter name | Optional | Description |
---|---|---|
configuration |
false |
Each FlexyPool instance requires it’s own configuration |
strategies |
true |
The strategies will be applied when fetching a connection from the target connection pool. You can set up any number of strategies and they will be applied in the same order they were supplied |
A Strategy is a connection acquiring safety mechanisms, a resort that’s called when a connection is not successfully fetched from the target Connection Pool. You can define your own Strategies, since this is a trivial job to do.
FlexyPool comes with the following default strategies
This strategy will increment the target connection pool maximum size on connection acquire timeout.
Parameter name | Optional | Description |
---|---|---|
maxOverflowPoolSize |
false |
This is the maximum limit a target connection pool can stretch to |
timeoutMillis |
true |
If the connection acquiring time takes more than this value a pool size increment is attempted. If this value is not supplied, the target pool timeout will be used instead, so that when the target pool throws a timeout exception the pool size increment is attempted. |
The connection pool has a minimum size and on demand it can grow up to its maximum size. The overflow is a buffer of extra connections allowing the connection pool to grow beyond its initial maximum size. Whenever a connection acquire timeout is detected, the current request won’t fail if the pool hasn’t grown to it’s maxOverflowPoolSize.
![OverFlowPoolSize](https://raw.githubusercontent.com/wiki/vladmihalcea/flexy-pool/image/architecture/OverFlowPoolSize.png)
It’s safe to set the target connection pool acquiring timeout interval to a value that’s appropriate for your application wait expectations. You might also set the connection expiring interval to free up unused connections, releasing them when they are no longer required.
A DBCP example setting the initial maximum size and the acquiring timeout interval.
<bean id="poolingDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="3"/>
<property name="maxWait" value="100"/>
</bean>
An enterprise system must have use an integrated monitoring report tool, such as Ganglia or Graphite and it’s easy to instruct FlexyPool to use a different reporting mechanism than the default ones.
You may wish to customize different Reservoirs for specific Metrics or to use a CSV reporter and this is how you can do it:
@org.springframework.context.annotation.Configuration
public class DataSourceConfiguration {
@Value("${data.source.type}")
private String dataSourceType;
@Value("${data.source.className}")
private Class<?> dataSourceClassName;
@Value("${data.source.uniqueName}")
private String dataSourceUniqueName;
@Value("${data.source.minPoolSize}")
private int dataSourceMinPoolSize;
@Value("${data.source.maxPoolSize}")
private int dataSourceMaxPoolSize;
@Value("${data.source.maxIdleTime}")
private int dataSourceMaxIdleTime;
@Value("${data.source.acquisitionTimeout}")
private int dataSourceAcquisitionTimeout;
@Value("${data.source.connectionAcquireTimeoutMillis}")
private int dataSourceConnectionAcquireTimeoutMillis;
@Value("${data.source.shareTransactionConnections}")
private boolean shareTransactionConnections;
@Value("${data.source.maxOverflow}")
private int dataSourceMaxOverflow;
@Value("${data.source.retryAttempts}")
private int dataSourceRetryAttempts;
@Value("${jdbc.url}")
private String dataSourceUrl;
@Value("${jdbc.username}")
private String dataSourceUsername;
@Value("${jdbc.password}")
private String dataSourcePassword;
@Value("${app.home}")
private String applicationHome;
private Map<String, Properties> dataSourceDriverPropertiesMap;
public DataSourceConfiguration(Map<String, Properties> dataSourceDriverPropertiesMap) {
this.dataSourceDriverPropertiesMap = dataSourceDriverPropertiesMap;
}
@Bean(initMethod = "init", destroyMethod = "close")
public PoolingDataSource poolingDataSource() {
PoolingDataSource poolingDataSource = new PoolingDataSource();
poolingDataSource.setClassName(dataSourceClassName.getName());
poolingDataSource.setUniqueName(dataSourceUniqueName);
poolingDataSource.setMinPoolSize(dataSourceMinPoolSize);
poolingDataSource.setMaxPoolSize(dataSourceMaxPoolSize);
poolingDataSource.setMaxIdleTime(dataSourceMaxIdleTime);
poolingDataSource.setAcquisitionTimeout(dataSourceAcquisitionTimeout);
poolingDataSource.setShareTransactionConnections(shareTransactionConnections);
poolingDataSource.setDriverProperties(dataSourceDriverPropertiesMap.get(dataSourceType));
return poolingDataSource;
}
@Bean
public Configuration<PoolingDataSource> configuration() {
return new Configuration.Builder<PoolingDataSource>(
DataSourceConfiguration.class.getSimpleName(),
poolingDataSource(),
BitronixPoolAdapter.FACTORY
)
.setMetricsFactory(new MetricsFactory() {
@Override
public Metrics newInstance(ConfigurationProperties configurationProperties) {
return new CodahaleMetrics(configurationProperties, new ReservoirFactory() {
@Override
public Reservoir newInstance(Class<? extends Metric> metricClass, String metricName) {
if(Timer.class.isAssignableFrom(metricClass)) {
return new ExponentiallyDecayingReservoir(1028 * 8, 0.015);
} else if(FlexyPoolDataSource.CONCURRENT_CONNECTIONS_HISTOGRAM.equals(metricName) ||
FlexyPoolDataSource.CONCURRENT_CONNECTION_REQUESTS_HISTOGRAM.equals(metricName)) {
return new ExponentiallyDecayingReservoir(1028 * 16, 0.015);
}
return new ExponentiallyDecayingReservoir();
}
}, new MetricsLifeCycleCallback() {
private CsvReporter csvReporter;
@Override
public MetricsLifeCycleCallback init(ConfigurationProperties configurationProperties, MetricRegistry metricRegistry) {
File metricsFolder = new File(applicationHome, "metrics");
metricsFolder.mkdirs();
csvReporter = CsvReporter.forRegistry(metricRegistry)
.build(metricsFolder);
return this;
}
@Override
public void start() {
csvReporter.start(15, TimeUnit.SECONDS);
}
@Override
public void stop() {
csvReporter.stop();
}
});
}
})
.build();
}
@Bean(initMethod = "start", destroyMethod = "stop")
public FlexyPoolDataSource dtfDataSource() {
Configuration<PoolingDataSource> configuration = configuration();
return new FlexyPoolDataSource<PoolingDataSource>(configuration,
new IncrementPoolOnTimeoutConnectionAcquiringStrategy.Factory(dataSourceMaxOverflow,
dataSourceConnectionAcquireTimeoutMillis),
new RetryConnectionAcquiringStrategy.Factory(dataSourceRetryAttempts)
);
}
}