Skip to content
Vlad Mihalcea edited this page Oct 17, 2024 · 65 revisions

Java 17 or later

This is the latest FlexyPool release version:

<flexy-pool.version>3.0.0</flexy-pool.version>

Make sure you add this to your pom.xml.

Micrometer Metrics

If you want to use the Micrometer framework for metrics instead of Dropwizard Metrics, then you need to add the flexy-micrometer-metrics dependency and exclude the flexy-dropwizard-metrics dependency from the connection pool-specific FlexyPool dependency (e.g. flexy-hikaricp):

<dependency>
	<groupId>com.vladmihalcea.flexy-pool</groupId>
	<artifactId>flexy-micrometer-metrics</artifactId>
	<version>${flexy-pool.version}</version>
</dependency>

<dependency>
	<groupId>com.vladmihalcea.flexy-pool</groupId>
	<artifactId>flexy-hikaricp</artifactId>
	<version>${flexy-pool.version}</version>
	<exclusions>
		<exclusion>
			<groupId>com.vladmihalcea.flexy-pool</groupId>
			<artifactId>flexy-dropwizard-metrics</artifactId>
		</exclusion>
	</exclusions>
</dependency>

Stand-alone environments

For Spring Boot, check out this flexy-pool-spring-boot-starter written by Arthur Gavlyukovskiy.

Connection Pool Settings

Depending on your current project connection pool choice you need to follow one of these guides first:

FlexyPool Settings

Configuration

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:

@Configuration
public class FlexyPoolBeanConfiguration {

    public static class ConnectionAcquisitionTimeThresholdExceededEventListener
            extends EventListener<ConnectionAcquisitionTimeThresholdExceededEvent> {

        public static final Logger LOGGER = LoggerFactory.getLogger(
                ConnectionAcquisitionTimeThresholdExceededEventListener.class);

        public ConnectionAcquisitionTimeThresholdExceededEventListener() {
            super(ConnectionAcquisitionTimeThresholdExceededEvent.class);
        }

        @Override
        public void on(ConnectionAcquisitionTimeThresholdExceededEvent 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 ConnectionAcquisitionTimeoutEventListener
            extends EventListener<ConnectionAcquisitionTimeoutEvent> {

        public static final Logger LOGGER = LoggerFactory.getLogger(
                ConnectionAcquisitionTimeoutEventListener.class);

        public ConnectionAcquisitionTimeoutEventListener() {
            super(ConnectionAcquisitionTimeoutEvent.class);
        }

        @Override
        public void on(ConnectionAcquisitionTimeoutEvent event) {
            LOGGER.info("Caught event {}", event);
        }
    }

    @Autowired
    private HikariDataSource poolingDataSource;

    @Value("${flexy.pool.uniqueId}")
    private String uniqueId;

    @Bean
    public FlexyPoolConfiguration<HikariDataSource> configuration() {
        return new FlexyPoolConfiguration.Builder<HikariDataSource>(
                uniqueId,
                poolingDataSource,
                HikariCPPoolAdapter.FACTORY
        )
        .setMetricsFactory(MetricsFactoryResolver.INSTANCE.resolve())
        .setConnectionProxyFactory(ConnectionDecoratorFactoryResolver.INSTANCE.resolve())
        .setMetricLogReporterMillis(TimeUnit.SECONDS.toMillis(5))
        .setMetricNamingUniqueName(UniqueNamingStrategy.INSTANCE)
        .setJmxEnabled(true)
        .setJmxAutoStart(true)
        .setConnectionAcquisitionTimeThresholdMillis(50L)
        .setConnectionLeaseTimeThresholdMillis(250L)
        .setEventListenerResolver(() -> Arrays.<EventListener<? extends Event>>asList(
				new ConnectionAcquisitionTimeoutEventListener(),
				new ConnectionAcquisitionTimeThresholdExceededEventListener(),
				new ConnectionLeaseTimeThresholdExceededEventListener()
		) )
        .build();
    }

    @Bean(initMethod = "start", destroyMethod = "stop")
    public FlexyPoolDataSource dataSource() {
        FlexyPoolConfiguration<HikariDataSource> configuration = configuration();
        return new FlexyPoolDataSource<HikariDataSource>(configuration,
                new IncrementPoolOnTimeoutConnectionAcquisitionStrategy.Factory(5),
                new RetryConnectionAcquisitionStrategy.Factory(2)
        );
    }
}
Parameter name Parameter type Optional Default value Description

uniqueName

Supplied through FlexyPoolConfiguration.Builder constructor

false

N/A

Each FlexyPool instance requires a unique name so that JMX domains won’t clash

targetDataSource

Supplied through FlexyPoolConfiguration.Builder constructor

false

N/A

The target DataSource we are monitoring

poolAdapterFactory

Supplied through FlexyPoolConfiguration.Builder constructor

false

N/A

The specific pool adaptor factory associated with the target DataSource

metricsFactory

Supplied through FlexyPoolConfiguration.Builder setMetricsFactory

true

DropwizardMetrics.FACTORY

The metrics factory allows customizing the metrics implementation. A Dropwizard Metrics implementation is being supplied

connectionProxyFactory

Supplied through FlexyPoolConfiguration.Builder setConnectionProxyFactory

true

ConnectionDecoratorFactory.INSTANCE

The connection proxy provider. You can choose between decorating Connections and a JDK Dynamic Proxies implementation

jmxEnabled

Supplied through FlexyPoolConfiguration.Builder setJmxEnabled

true

true

Specifies if the JMX service is enabled

metricLogReporterMillis

Supplied through FlexyPoolConfiguration.Builder setMetricLogReporterMillis

true

TimeUnit.MINUTES.toMillis(5)

Specifies the metrics log reported interval

metricNamingUniqueName

Supplied through FlexyPoolConfiguration.Builder setMetricNamingUniqueName

true

com.vladmihalcea.flexypool.strategy.DefaultNamingStrategy

Specifies a class name that implements the MetricNamingStrategy interface

eventListenerResolver

Supplied through FlexyPoolConfiguration.Builder setEventListenerResolver

true

N/A

Specifies a class name responsible for supplying the EventListener implementations

connectionAcquisitionTimeThresholdMillis

Supplied through FlexyPoolConfiguration.Builder setConnectionAcquisitionTimeThresholdMillis

true

Long.MAX_VALUE

Specifies a time threshold for the connection acquisition request. When the time limit is exceeded a log entry will be generated and a ConnectionAcquisitionTimeThresholdExceededEvent will be published.

connectionLeaseTimeThresholdMillis

Supplied through FlexyPoolConfiguration.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.

Data Source

@Bean(initMethod = "start", destroyMethod = "stop")
public FlexyPoolDataSource dataSource() {
	Configuration<HikariDataSource> configuration = configuration();
	return new FlexyPoolDataSource<HikariDataSource>(configuration,
			new IncrementPoolOnTimeoutConnectionAcquisitionStrategy.Factory(5),
			new RetryConnectionAcquisitionStrategy.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

Strategies

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

IncrementPoolOnTimeoutConnectionAcquisitionStrategy

This strategy will increment the target connection pool maximum size on connection acquisition 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 acquisition timeout is detected, the current request won’t fail if the pool hasn’t grown to it’s maxOverflowPoolSize.

OverFlowPoolSize

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>

RetryConnectionAcquisitionStrategy

This strategy is useful for those connection pools lacking a connection acquiring retry mechanism

Parameter name Optional Description

retryAttempts

false

This is the maximum number of retries attempts before giving up

Event Handling

You can register your own event handlers for the following events:

  • ConnectionAcquisitionTimeoutEvent (generated when a connection acquisition timeout exception is thrown)

  • ConnectionAcquisitionTimeThresholdExceededEvent (generated when the connection acquisition threshold given by the connectionAcquisitionTimeThresholdMillis 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.

Customized Metrics

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:

FlexyPoolConfiguration<DataSource> configuration = new FlexyPoolConfiguration.Builder<>(
		"unique",
		dataSource,
		poolAdapterFactory
	)
	.setConnectionProxyFactory(connectionProxyFactory)
	.setJmxAutoStart(true)
	.setJmxEnabled(true)
	.setMetricLogReporterMillis(120)
	.setMetricsFactory(
		configurationProperties -> new DropwizardMetrics(
			configurationProperties,
			metricRegistry,
			(metricClass, metricName) -> new ExponentiallyDecayingReservoir()
		)
	)
	.build();