-
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.4</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 ConnectionLeaseTimeThresholdExceededEventListener()
);
}
})
.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>
You can register your own event handlers for the following events:
-
ConnectionAcquireTimeoutEvent (generated when a connection acquire timeout exception is thrown)
-
ConnectionAcquireTimeThresholdExceededEvent (generated when the connection acquire threshold given by the connectionAcquireTimeThresholdMillis configuration property is exceeded)
-
ConnectionLeaseTimeThresholdExceededEvent (generated when the connection lease threshold given by the connectionLeaseTimeThresholdMillis configuration property is exceeded)
To supply the custom event listeners you should use the eventListenerResolver configuration property.
You should be aware that these events are published synchronously so you should avoid executing some time-consuming event handling logic because that would add an extra overhead to the current transaction and connection holding. If your event handling logic is time-consuming, you should consider submitting the event to an ExecutorService and execute it in one of its own worker threads.
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)
);
}
}