Skip to content
Commits on Source (8)
......@@ -6,3 +6,4 @@ Database : PostgreSQL|MySQL|...
Driver version : x.x.x
```
-----------------------------------------------------------------------------------------
*Have you searched the CLOSED issues already? How about checking [stackoverflow](https://stackoverflow.com/search?q=HikariCP)?*
......@@ -6,7 +6,6 @@ dist:
before_script:
- if [[ "x$JDK" == *'x9'* ]]; then export MAVEN_SKIP_RC=true; fi
- if [[ "x$JDK" != *'x9'* ]]; then export COVERALLS=coveralls:report; fi
script:
- export JDK8_HOME=$(jdk_switcher home oraclejdk8)
......@@ -32,5 +31,7 @@ matrix:
install: /bin/true
script:
- mvn package -Dskip.unit.tests=true -Dmaven.javadoc.skip=true -V -B
- mvn test $COVERALLS -V -P coverage
- mvn package -Dmaven.javadoc.skip=true -V -B
after_success:
- bash <(curl -s https://codecov.io/bash)
HikariCP Changes
Changes in 2.7.7
* fixed issue whereby configuration through the HikariConfigMXBean could not be altered due
to the sealed configuration change introduced in 2.7.5.
Changes in 2.7.6
* issue 1064 fixed regression where HikariConfig.copyStateTo() propagated the "sealed" status
of the source configuration to the target configuration -- preventing further changes.
Changes in 2.7.5
* issue 1061/pull 1062 fixed compatibility issue with requery.io caused by HikariCP's
proxied Statement class returning a driver-native ResultSet instance from getGeneratedKeys()
instead of returning a HikariCP proxied ResultSet.
* pull 1058 enable quantiles on Prometheus metrics.
* pull 1055 fixed incorrect JavaDoc for HikariConfigMXBean.getMinimumIdle() method.
* issue 1045/pull 1047 added Automatic-Module-Name to jar manifest to ensure that the Java 8
library plays well with the Java 9 module system.
* introduced the concept of a "sealed" configuration. Once a pool is started, attempts to
alter its configuration outside of the HikariConfigMXBean will result in an IllegalStateException.
Changes in 2.7.4
* pull 1026 added support for SQL Server's specific isolation level (SNAPSHOT).
* issue 926/pull 1022 HikariJNDIFactory should not throw a NamingException or else
cascading to other object factories cannot occur.
Changes in 2.7.3
* issue 1003 added PostgreSQL SQL State 0A000 to list of unrecoverable states calling
for eviction.
* pull 1002 updated micrometer support due to API changes in their release candidate.
Changes in 2.7.2
* issue 983 fix logic that determines how many idle connections can be removed, without
violating the minimumIdle contract.
* pull 987 add thread name to leak detection messages.
* issue 982 fix classloader order, try the ThreadContext classloader before other
classloaders.
* pull 977 log better messages when connection is evicted.
* fallback to four digit random pool suffix when SecurityManager prevents writing
to system properties for the purpose of JVM-wide unique pool identifiers.
Changes in 2.7.1
* issue 968 Wrong label order in MicrometerMetricsTracker for the connection usage
......@@ -9,6 +64,14 @@ Changes in 2.7.1
intended to cause parkNanos() to be called every 256 iterations. Thanks to @ztkmkoo
for finding this.
Changes in 2.4.13
* backport more efficient contention handling in ConcurrentBag.requite from 2.6.x
branch.
* issue 955 fix possible race condition when Statements are closed on different
threads from which they were created.
Changes in 2.7.0
* added support for micrometer metrics (currently Alpha-level support).
......
......@@ -2,7 +2,6 @@
[![][Build Status img]][Build Status]
[![][Coverage Status img]][Coverage Status]
[![][Dependency Status img]][Dependency Status]
[![][license img]][license]
[![][Maven Central img]][Maven Central]
[![][Javadocs img]][Javadocs]
......@@ -14,20 +13,12 @@ Fast, simple, reliable. HikariCP is a "zero-overhead" production ready JDBC con
----------------------------------------------------
_Java 8 maven artifact:_
_Java 8/9 maven artifact:_
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.0</version>
</dependency>
```
_Java 9 Early Access maven artifact:_
```xml
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java9ea</artifactId>
<version>2.6.1</version>
<version>2.7.7</version>
</dependency>
```
_Java 7 maven artifact (*maintenance mode*):_
......@@ -35,7 +26,7 @@ _Java 7 maven artifact (*maintenance mode*):_
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP-java7</artifactId>
<version>2.4.12</version>
<version>2.4.13</version>
</dependency>
```
_Java 6 maven artifact (*maintenance mode*):_
......@@ -108,7 +99,7 @@ HikariCP comes with *sane* defaults that perform well in most deployments withou
<sup>&#128206;</sup>&nbsp;*HikariCP uses milliseconds for all time values.*
&#128680;&nbsp;HikariCP relies on accurate timers for both performance and reliability. It is *imperative* that your server is synchronized with a time-source such as an NTP server. *Especially* if your server is running within a virtual machine. Why? [Read more here](https://dba.stackexchange.com/questions/171002/choice-of-connection-pooling-library-for-vm-deploys/171020). **Do not rely on hypervisor settings to "synchronize" the clock of the virtual machine. Configure time-source synchronization inside the virtual machine.** If you come asking for support on an issue that turns out to be caused by lack time synchronization, you will be taunted publicly on Twitter.
&#128680;&nbsp;HikariCP relies on accurate timers for both performance and reliability. It is *imperative* that your server is synchronized with a time-source such as an NTP server. *Especially* if your server is running within a virtual machine. Why? [Read more here](https://dba.stackexchange.com/a/171020). **Do not rely on hypervisor settings to "synchronize" the clock of the virtual machine. Configure time-source synchronization inside the virtual machine.** If you come asking for support on an issue that turns out to be caused by lack time synchronization, you will be taunted publicly on Twitter.
##### Essentials
......@@ -171,14 +162,16 @@ This property controls the maximum amount of time that a connection is allowed t
pool. **This setting only applies when ``minimumIdle`` is defined to be less than ``maximumPoolSize``.**
Whether a connection is retired as idle or not is subject to a maximum variation of +30
seconds, and average variation of +15 seconds. A connection will never be retired as idle *before*
this timeout. A value of 0 means that idle connections are never removed from the pool. The minimum
this timeout. Once the pool reaches ``minimumIdle`` connections, connections will no longer be
retired, even if idle. A value of 0 means that idle connections are never removed from the pool. The minimum
allowed value is 10000ms (10 seconds).
*Default: 600000 (10 minutes)*
&#8986;``maxLifetime``<br/>
This property controls the maximum lifetime of a connection in the pool. An in-use connection will
never be retired, only when it is closed will it then be removed. **We strongly recommend setting
this value, and it should be at least 30 seconds less than any database or infrastructure imposed
never be retired, only when it is closed will it then be removed. On a connection-by-connection
basis, minor negative attenuation is applied to avoid mass-extinction in the pool. **We strongly recommend
setting this value, and it should be at least 30 seconds less than any database or infrastructure imposed
connection time limit.** A value of 0 indicates no maximum lifetime (infinite lifetime), subject of
course to the ``idleTimeout`` setting.
*Default: 1800000 (30 minutes)*
......@@ -311,6 +304,11 @@ frameworks. When this property is specified, the ``dataSourceClassName`` propert
DataSource-specific properties will be ignored.
*Default: none*
&#128288;``schema``<br/>
This property sets the default *schema* for databases that support the concept of schemas.
If this property is not specified, the default schema defined by the JDBC driver is used.
*Default: driver default*
&#10145;``threadFactory``<br/>
This property is only available via programmatic configuration or IoC container. This property
allows you to set the instance of the ``java.util.concurrent.ThreadFactory`` that will be used
......@@ -357,8 +355,9 @@ and will negatively impact your application performance compared to driver-provi
Like Statement caching, most major database vendors support statement logging through
properties of their own driver. This includes Oracle, MySQL, Derby, MSSQL, and others. Some
even support slow query logging. For those few databases that do not support it, [log4jdbc](https://github.com/arthurblake/log4jdbc) or [jdbcdslog-exp](https://code.google.com/p/jdbcdslog-exp/) are
good options.
even support slow query logging. For those few databases that do not support it, several options are available.
We have received [a report that p6spy works well](https://github.com/brettwooldridge/HikariCP/issues/57#issuecomment-354647631),
and also note the availability of [log4jdbc](https://github.com/arthurblake/log4jdbc) and [jdbcdslog-exp](https://code.google.com/p/jdbcdslog-exp/).
----------------------------------------------------
......@@ -419,7 +418,7 @@ location of a properties file. If you intend to use this option, construct a ``
instance using the default constructor and the properties file will be loaded.
### Performance Tips
[MySQL Performnace Tips](https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration)
[MySQL Performance Tips](https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration)
### Popular DataSource Class Names
......@@ -434,14 +433,14 @@ Here is a list of JDBC *DataSource* classes for popular databases:
| Database | Driver | *DataSource* class |
|:---------------- |:------------ |:-------------------|
| Apache Derby | Derby | org.apache.derby.jdbc.ClientDataSource |
| Firebird | Jaybird | org.firebirdsql.pool.FBSimpleDataSource |
| Firebird | Jaybird | org.firebirdsql.ds.FBSimpleDataSource |
| H2 | H2 | org.h2.jdbcx.JdbcDataSource |
| HSQLDB | HSQLDB | org.hsqldb.jdbc.JDBCDataSource |
| IBM DB2 | IBM JCC | com.ibm.db2.jcc.DB2SimpleDataSource |
| IBM Informix | IBM Informix | com.informix.jdbcx.IfxDataSource |
| MS SQL Server | Microsoft | com.microsoft.sqlserver.jdbc.SQLServerDataSource |
| ~~MySQL~~ | Connector/J | ~~com.mysql.jdbc.jdbc2.optional.MysqlDataSource~~ |
| MySQL/MariaDB | MariaDB | org.mariadb.jdbc.MySQLDataSource |
| MariaDB | MariaDB | org.mariadb.jdbc.MariaDbDataSource |
| Oracle | Oracle | oracle.jdbc.pool.OracleDataSource |
| OrientDB | OrientDB | com.orientechnologies.orient.jdbc.OrientDataSource |
| PostgreSQL | pgjdbc-ng | com.impossibl.postgres.jdbc.PGDataSource |
......@@ -486,10 +485,14 @@ Don't forget the [Wiki](https://github.com/brettwooldridge/HikariCP/wiki) for ad
&#8658; slf4j library<br/>
### Sponsors
High-performance projects can never have too many tools! We would like to thank the following companies:
Thanks to [ej-technologies](https://www.ej-technologies.com) for their excellent all-in-one profiler, [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html).
YourKit supports open source projects with its full-featured Java Profiler. Click the YourKit logo below to learn more.<br/>
[![](https://github.com/brettwooldridge/HikariCP/wiki/yklogo.png)](http://www.yourkit.com/java/profiler/index.jsp)<br/>
### Contributions
Please perform changes and submit pull requests from the ``dev`` branch instead of ``master``. Please set your editor to use spaces instead of tabs, and adhere to the apparent style of the code you are editing. The ``dev`` branch is always more "current" than the ``master`` if you are looking to live life on the edge.
......@@ -497,11 +500,8 @@ Please perform changes and submit pull requests from the ``dev`` branch instead
[Build Status]:https://travis-ci.org/brettwooldridge/HikariCP
[Build Status img]:https://travis-ci.org/brettwooldridge/HikariCP.svg?branch=dev
[Coverage Status]:https://coveralls.io/r/brettwooldridge/HikariCP?branch=dev
[Coverage Status img]:https://coveralls.io/repos/brettwooldridge/HikariCP/badge.svg?branch=dev
[Dependency Status]:https://www.versioneye.com/user/projects/551ce51c3661f1bee50004e0
[Dependency Status img]:https://www.versioneye.com/user/projects/551ce51c3661f1bee50004e0/badge.svg?style=flat
[Coverage Status]:https://codecov.io/gh/brettwooldridge/HikariCP
[Coverage Status img]:https://codecov.io/gh/brettwooldridge/HikariCP/branch/dev/graph/badge.svg
[license]:LICENSE
[license img]:https://img.shields.io/badge/license-Apache%202-blue.svg
......
codecov:
notify:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "50..80"
status:
project: yes
patch: yes
changes: no
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "reach, diff, flags, files, footer"
behavior: default
require_changes: no
......@@ -65,8 +65,10 @@ Where ``150`` is the connection establishment time, ``<pool>`` is one of [*hikar
<sup>* Note that the times provided in the raw data is the number of microseconds (μs) since the start of the test. For graphing purposes, raw data for each pool was trimmed such that the first entry has 0 requests enqueued, and the last entry has all connections completed. </sup>
--------------------
#### Apache DBCP vs HikariCP
In case you missed the *time-scale* in the graphs above, here is a properly scaled comparable; Apache DBCP on top, HikariCP on the bottom.
### Apache DBCP vs HikariCP
:point_right: In case you missed the *time-scale* in the graphs above, here is a properly scaled comparable.
Apache DBCP on *top*, HikariCP on the *bottom*.
[![](https://github.com/brettwooldridge/HikariCP/wiki/Spike-Compare.png)](https://github.com/brettwooldridge/HikariCP/wiki/Spike-Compare.png)
......@@ -85,7 +87,7 @@ Consider this hypothetical scenario:
There is a pool with five connections in-use, and zero idle (available) connections. Then, a new thread
comes in requesting a connection.
```
"How the the prime directive apply in this case?" We'll answer with a question of our own:
"How does the prime directive apply in this case?" We'll answer with a question of our own:
> If the thread is directed to create a new connection, and that connection takes 150ms to establish, what happens if one of the five in-use connections is returned to the pool?
......
......@@ -8,27 +8,28 @@
<artifact.classifier />
<felix.bundle.plugin.version>3.3.0</felix.bundle.plugin.version>
<felix.version>5.6.2</felix.version>
<felix.version>5.6.8</felix.version>
<hibernate.version>5.2.10.Final</hibernate.version>
<javassist.version>3.22.0-CR1</javassist.version>
<jndi.version>0.11.4.1</jndi.version>
<maven.release.version>2.5.3</maven.release.version>
<metrics.version>3.2.2</metrics.version>
<micrometer.version>0.10.0.RELEASE</micrometer.version>
<simpleclient.version>0.0.22</simpleclient.version>
<mockito.version>2.8.9</mockito.version>
<metrics.version>3.2.4</metrics.version>
<micrometer.version>1.0.0</micrometer.version>
<simpleclient.version>0.0.26</simpleclient.version>
<mockito.version>2.10.0</mockito.version>
<pax.exam.version>4.11.0</pax.exam.version>
<pax.url.version>2.5.2</pax.url.version>
<postgresql.version>42.1.4</postgresql.version>
<slf4j.version>1.7.25</slf4j.version>
<log4j.version>2.8.2</log4j.version>
<commons.csv.version>1.4</commons.csv.version>
<h2.version>1.4.195</h2.version>
<log4j.version>2.9.0</log4j.version>
<commons.csv.version>1.5</commons.csv.version>
<h2.version>1.4.196</h2.version>
<junit.version>4.12</junit.version>
</properties>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>2.7.1</version>
<version>2.7.9</version>
<packaging>bundle</packaging>
<name>HikariCP</name>
......@@ -44,7 +45,7 @@
<connection>scm:git:git@github.com:brettwooldridge/HikariCP.git</connection>
<developerConnection>scm:git:git@github.com:brettwooldridge/HikariCP.git</developerConnection>
<url>git@github.com:brettwooldridge/HikariCP.git</url>
<tag>HikariCP-2.7.1</tag>
<tag>HikariCP-2.7.9</tag>
</scm>
<licenses>
......@@ -135,7 +136,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.1.4.jre7</version>
<version>${postgresql.version}</version>
<scope>test</scope>
</dependency>
<dependency>
......@@ -268,7 +269,6 @@
<executions>
<!-- Prepares the property pointing to the JaCoCo runtime agent which is passed as VM argument when Maven the Surefire plugin is executed. -->
<execution>
<id>pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
......@@ -286,7 +286,7 @@
</execution>
<!-- Ensures that the code coverage report for unit tests is created after unit tests have been run. -->
<execution>
<id>post-unit-test</id>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
......@@ -294,8 +294,6 @@
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>${project.build.directory}/coverage-reports/jacoco.exec</dataFile>
<!-- Sets the output directory for the code coverage report. -->
<outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
<excludes>
<exclude>**/com/zaxxer/hikari/pool/HikariProxy*</exclude>
<exclude>**/com/zaxxer/hikari/metrics/**</exclude>
......@@ -313,6 +311,7 @@
<configuration>
<classifier>${artifact.classifier}</classifier>
<instructions>
<Automatic-Module-Name>com.zaxxer.hikari</Automatic-Module-Name>
<Bundle-Name>HikariCP</Bundle-Name>
<Export-Package>
com.zaxxer.hikari,
......@@ -362,11 +361,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<version>3.7.0</version>
<extensions>true</extensions>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>-Xlint</compilerArgs>
</configuration>
</plugin>
......@@ -454,29 +454,6 @@
</properties>
</profile>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>4.3.0</version>
<executions>
<execution>
<id>coveralls</id>
<phase>verify</phase>
<goals>
<goal>jacoco</goal>
</goals>
<inherited>false</inherited>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release-sign-artifacts</id>
<activation>
......
......@@ -112,12 +112,9 @@ public interface HikariConfigMXBean
void setMaxLifetime(long maxLifetimeMs);
/**
* The property controls the maximum size that the pool is allowed to reach, including both idle and in-use
* connections. Basically this value will determine the maximum number of actual connections to the database
* backend.
* <p>
* When the pool reaches this size, and no idle connections are available, calls to getConnection() will
* block for up to connectionTimeout milliseconds before timing out.
* The property controls the minimum number of idle connections that HikariCP tries to maintain in the pool,
* including both idle and in-use connections. If the idle connections dip below this value, HikariCP will
* make a best effort to restore them quickly and efficiently.
*
* @return the minimum number of connections in the pool
*/
......@@ -177,4 +174,4 @@ public interface HikariConfigMXBean
* @return the name of the connection pool
*/
String getPoolName();
}
\ No newline at end of file
}
......@@ -16,6 +16,13 @@
package com.zaxxer.hikari;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sql.DataSource;
import java.io.Closeable;
import java.io.PrintWriter;
import java.sql.Connection;
......@@ -23,14 +30,7 @@ import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.pool.HikariPool;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import static com.zaxxer.hikari.pool.HikariPool.POOL_SHUTDOWN;
/**
* The HikariCP pooled DataSource.
......@@ -47,10 +47,14 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
private volatile HikariPool pool;
/**
* Default constructor. Setters be used to configure the pool. Using
* Default constructor. Setters are used to configure the pool. Using
* this constructor vs. {@link #HikariDataSource(HikariConfig)} will
* result in {@link #getConnection()} performance that is slightly lower
* due to lazy initialization checks.
*
* The first call to {@link #getConnection()} starts the pool. Once the pool
* is started, the configuration is "sealed" and no further configuration
* changes are possible -- except via {@link HikariConfigMXBean} methods.
*/
public HikariDataSource()
{
......@@ -59,20 +63,31 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
}
/**
* Construct a HikariDataSource with the specified configuration.
* Construct a HikariDataSource with the specified configuration. The
* {@link HikariConfig} is copied and the pool is started by invoking this
* constructor.
*
* The {@link HikariConfig} can be modified without affecting the HikariDataSource
* and used to initialize another HikariDataSource instance.
*
* @param configuration a HikariConfig instance
*/
public HikariDataSource(HikariConfig configuration)
{
configuration.validate();
configuration.copyState(this);
configuration.copyStateTo(this);
LOGGER.info("{} - Starting...", configuration.getPoolName());
pool = fastPathPool = new HikariPool(this);
LOGGER.info("{} - Start completed.", configuration.getPoolName());
this.seal();
}
// ***********************************************************************
// DataSource methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public Connection getConnection() throws SQLException
......@@ -95,6 +110,7 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
LOGGER.info("{} - Starting...", getPoolName());
try {
pool = result = new HikariPool(this);
this.seal();
}
catch (PoolInitializationException pie) {
if (pie.getCause() instanceof SQLException) {
......@@ -209,6 +225,10 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
return false;
}
// ***********************************************************************
// HikariConfigMXBean methods
// ***********************************************************************
/** {@inheritDoc} */
@Override
public void setMetricRegistry(Object metricRegistry)
......@@ -263,6 +283,20 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
}
}
// ***********************************************************************
// HikariCP-specific methods
// ***********************************************************************
/**
* Returns {@code true} if the pool as been started and is not suspended or shutdown.
*
* @return {@code true} if the pool as been started and is not suspended or shutdown.
*/
public boolean isRunning()
{
return pool != null && pool.poolState != POOL_SHUTDOWN;
}
/**
* Get the {@code HikariPoolMXBean} for this HikariDataSource instance. If this method is called on
* a {@code HikariDataSource} that has been constructed without a {@code HikariConfig} instance,
......@@ -277,7 +311,7 @@ public class HikariDataSource extends HikariConfig implements DataSource, Closea
/**
* Get the {@code HikariConfigMXBean} for this HikariDataSource instance.
*
*
* @return the {@code HikariConfigMXBean} instance.
*/
public HikariConfigMXBean getHikariConfigMXBean()
......
......@@ -43,28 +43,22 @@ public class HikariJNDIFactory implements ObjectFactory
synchronized public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception
{
// We only know how to deal with <code>javax.naming.Reference</code> that specify a class name of "javax.sql.DataSource"
if (!(obj instanceof Reference)) {
return null;
}
Reference ref = (Reference) obj;
if (!"javax.sql.DataSource".equals(ref.getClassName())) {
throw new NamingException(ref.getClassName() + " is not a valid class name/type for this JNDI factory.");
}
if (obj instanceof Reference && "javax.sql.DataSource".equals(((Reference) obj).getClassName())) {
Reference ref = (Reference) obj;
Set<String> hikariPropSet = PropertyElf.getPropertyNames(HikariConfig.class);
Set<String> hikariPropSet = PropertyElf.getPropertyNames(HikariConfig.class);
Properties properties = new Properties();
Enumeration<RefAddr> enumeration = ref.getAll();
while (enumeration.hasMoreElements()) {
RefAddr element = enumeration.nextElement();
String type = element.getType();
if (type.startsWith("dataSource.") || hikariPropSet.contains(type)) {
properties.setProperty(type, element.getContent().toString());
Properties properties = new Properties();
Enumeration<RefAddr> enumeration = ref.getAll();
while (enumeration.hasMoreElements()) {
RefAddr element = enumeration.nextElement();
String type = element.getType();
if (type.startsWith("dataSource.") || hikariPropSet.contains(type)) {
properties.setProperty(type, element.getContent().toString());
}
}
return createDataSource(properties, nameCtx);
}
return createDataSource(properties, nameCtx);
return null;
}
private DataSource createDataSource(final Properties properties, final Context context) throws NamingException
......
File mode changed from 100755 to 100644
......@@ -34,13 +34,15 @@ public abstract class PoolStats
protected volatile int idleConnections;
protected volatile int activeConnections;
protected volatile int pendingThreads;
protected volatile int maxConnections;
protected volatile int minConnections;
public PoolStats(final long timeoutMs)
{
this.timeoutMs = timeoutMs;
this.reloadAt = new AtomicLong();
}
public int getTotalConnections()
{
if (shouldLoad()) {
......@@ -77,6 +79,22 @@ public abstract class PoolStats
return pendingThreads;
}
public int getMaxConnections() {
if (shouldLoad()) {
update();
}
return maxConnections;
}
public int getMinConnections() {
if (shouldLoad()) {
update();
}
return minConnections;
}
protected abstract void update();
private boolean shouldLoad()
......
......@@ -44,6 +44,8 @@ public final class CodaHaleMetricsTracker implements IMetricsTracker
private static final String METRIC_NAME_IDLE_CONNECTIONS = "IdleConnections";
private static final String METRIC_NAME_ACTIVE_CONNECTIONS = "ActiveConnections";
private static final String METRIC_NAME_PENDING_CONNECTIONS = "PendingConnections";
private static final String METRIC_NAME_MAX_CONNECTIONS = "MaxConnections";
private static final String METRIC_NAME_MIN_CONNECTIONS = "MinConnections";
public CodaHaleMetricsTracker(final String poolName, final PoolStats poolStats, final MetricRegistry registry)
{
......@@ -85,6 +87,22 @@ public final class CodaHaleMetricsTracker implements IMetricsTracker
return poolStats.getPendingThreads();
}
});
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MAX_CONNECTIONS),
new Gauge<Integer>() {
@Override
public Integer getValue() {
return poolStats.getMaxConnections();
}
});
registry.register(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MIN_CONNECTIONS),
new Gauge<Integer>() {
@Override
public Integer getValue() {
return poolStats.getMinConnections();
}
});
}
/** {@inheritDoc} */
......@@ -99,6 +117,8 @@ public final class CodaHaleMetricsTracker implements IMetricsTracker
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_IDLE_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_ACTIVE_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_PENDING_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MAX_CONNECTIONS));
registry.remove(MetricRegistry.name(poolName, METRIC_CATEGORY, METRIC_NAME_MIN_CONNECTIONS));
}
/** {@inheritDoc} */
......
......@@ -2,33 +2,56 @@ package com.zaxxer.hikari.metrics.micrometer;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.PoolStats;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.stats.quantile.WindowSketchQuantiles;
import java.util.concurrent.TimeUnit;
import static io.micrometer.core.instrument.stats.hist.CumulativeHistogram.buckets;
import static io.micrometer.core.instrument.stats.hist.CumulativeHistogram.linear;
/**
* {@link IMetricsTracker Metrics tracker} for Micrometer.
* HikariCP metrics can be configured in your application by applying a
* {@link io.micrometer.core.instrument.config.MeterFilter MeterFilter} to metrics starting with
* {@link #HIKARI_METRIC_NAME_PREFIX}. For example, to configure client-side calculated percentiles:
*
* <blockquote><pre>
* new MeterFilter() {
* &#064;Override
* public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
* if (id.getName().startsWith(MicrometerMetricsTracker.HIKARI_METRIC_NAME_PREFIX)) {
* return DistributionStatisticConfig.builder()
* .percentiles(0.5, 0.95)
* .build()
* .merge(config);
* }
* return config;
* }
* };
* </pre></blockquote>
*/
public class MicrometerMetricsTracker implements IMetricsTracker
{
/** Prefix used for all HikariCP metric names. */
public static final String HIKARI_METRIC_NAME_PREFIX = "hikaricp";
private static final String METRIC_CATEGORY = "pool";
private static final String METRIC_NAME_WAIT = "Wait";
private static final String METRIC_NAME_USAGE = "Usage";
private static final String METRIC_NAME_CONNECT = "ConnectionCreation";
private static final String METRIC_NAME_TIMEOUT_RATE = "ConnectionTimeoutRate";
private static final String METRIC_NAME_TOTAL_CONNECTIONS = "TotalConnections";
private static final String METRIC_NAME_IDLE_CONNECTIONS = "IdleConnections";
private static final String METRIC_NAME_ACTIVE_CONNECTIONS = "ActiveConnections";
private static final String METRIC_NAME_PENDING_CONNECTIONS = "PendingConnections";
private static final String METRIC_NAME_WAIT = HIKARI_METRIC_NAME_PREFIX + ".connections.acquire";
private static final String METRIC_NAME_USAGE = HIKARI_METRIC_NAME_PREFIX + ".connections.usage";
private static final String METRIC_NAME_CONNECT = HIKARI_METRIC_NAME_PREFIX + ".connections.creation";
private static final String METRIC_NAME_TIMEOUT_RATE = HIKARI_METRIC_NAME_PREFIX + ".connections.timeout";
private static final String METRIC_NAME_TOTAL_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections";
private static final String METRIC_NAME_IDLE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.idle";
private static final String METRIC_NAME_ACTIVE_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.active";
private static final String METRIC_NAME_PENDING_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.pending";
private static final String METRIC_NAME_MAX_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.max";
private static final String METRIC_NAME_MIN_CONNECTIONS = HIKARI_METRIC_NAME_PREFIX + ".connections.min";
private final Timer connectionObtainTimer;
private final DistributionSummary connectionTimeoutMeter;
private final DistributionSummary connectionUsage;
private final DistributionSummary connectionCreation;
private final Counter connectionTimeoutCounter;
private final Timer connectionUsage;
private final Timer connectionCreation;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final Gauge totalConnectionGauge;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
......@@ -37,54 +60,67 @@ public class MicrometerMetricsTracker implements IMetricsTracker
private final Gauge activeConnectionGauge;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final Gauge pendingConnectionGauge;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final Gauge maxConnectionGauge;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final Gauge minConnectionGauge;
@SuppressWarnings({"FieldCanBeLocal", "unused"})
private final PoolStats poolStats;
MicrometerMetricsTracker(final String poolName, final PoolStats poolStats, final MeterRegistry meterRegistry)
{
this.connectionObtainTimer = meterRegistry
.timerBuilder(METRIC_NAME_WAIT)
this.poolStats = poolStats;
this.connectionObtainTimer = Timer.builder(METRIC_NAME_WAIT)
.description("Connection acquire time")
.tags(METRIC_CATEGORY, poolName)
.create();
.register(meterRegistry);
this.connectionCreation = meterRegistry
.summaryBuilder(METRIC_NAME_CONNECT)
this.connectionCreation = Timer.builder(METRIC_NAME_CONNECT)
.description("Connection creation time")
.tags(METRIC_CATEGORY, poolName)
.quantiles(WindowSketchQuantiles.quantiles(0.5, 0.95).create())
.histogram(buckets(linear(0, 10, 20), TimeUnit.MILLISECONDS))
.create();
.register(meterRegistry);
this.connectionUsage = meterRegistry
.summaryBuilder(METRIC_NAME_USAGE)
this.connectionUsage = Timer.builder(METRIC_NAME_USAGE)
.description("Connection usage time")
.tags(METRIC_CATEGORY, poolName)
.quantiles(WindowSketchQuantiles.quantiles(0.5, 0.95).create())
.histogram(buckets(linear(0, 10, 20), TimeUnit.MILLISECONDS))
.create();
.register(meterRegistry);
this.connectionTimeoutMeter = meterRegistry
.summaryBuilder(METRIC_NAME_TIMEOUT_RATE)
this.connectionTimeoutCounter = Counter.builder(METRIC_NAME_TIMEOUT_RATE)
.description("Connection timeout total count")
.tags(METRIC_CATEGORY, poolName)
.quantiles(WindowSketchQuantiles.quantiles(0.5, 0.95).create())
.histogram(buckets(linear(0, 10, 20), TimeUnit.MILLISECONDS))
.create();
.register(meterRegistry);
this.totalConnectionGauge = meterRegistry
.gaugeBuilder(METRIC_NAME_TOTAL_CONNECTIONS, Integer.class, (i) -> poolStats.getTotalConnections())
this.totalConnectionGauge = Gauge.builder(METRIC_NAME_TOTAL_CONNECTIONS, poolStats, PoolStats::getTotalConnections)
.description("Total connections")
.tags(METRIC_CATEGORY, poolName)
.create();
.register(meterRegistry);
this.idleConnectionGauge = meterRegistry
.gaugeBuilder(METRIC_NAME_IDLE_CONNECTIONS, Integer.class, (i) -> poolStats.getIdleConnections())
this.idleConnectionGauge = Gauge.builder(METRIC_NAME_IDLE_CONNECTIONS, poolStats, PoolStats::getIdleConnections)
.description("Idle connections")
.tags(METRIC_CATEGORY, poolName)
.create();
.register(meterRegistry);
this.activeConnectionGauge = meterRegistry
.gaugeBuilder(METRIC_NAME_ACTIVE_CONNECTIONS, Integer.class, (i) -> poolStats.getActiveConnections())
this.activeConnectionGauge = Gauge.builder(METRIC_NAME_ACTIVE_CONNECTIONS, poolStats, PoolStats::getActiveConnections)
.description("Active connections")
.tags(METRIC_CATEGORY, poolName)
.create();
.register(meterRegistry);
this.pendingConnectionGauge = meterRegistry
.gaugeBuilder(METRIC_NAME_PENDING_CONNECTIONS, Integer.class, (i) -> poolStats.getPendingThreads())
this.pendingConnectionGauge = Gauge.builder(METRIC_NAME_PENDING_CONNECTIONS, poolStats, PoolStats::getPendingThreads)
.description("Pending threads")
.tags(METRIC_CATEGORY, poolName)
.create();
.register(meterRegistry);
this.maxConnectionGauge = Gauge.builder(METRIC_NAME_MAX_CONNECTIONS, poolStats, PoolStats::getMaxConnections)
.description("Max connections")
.tags(METRIC_CATEGORY, poolName)
.register(meterRegistry);
this.minConnectionGauge = Gauge.builder(METRIC_NAME_MIN_CONNECTIONS, poolStats, PoolStats::getMinConnections)
.description("Min connections")
.tags(METRIC_CATEGORY, poolName)
.register(meterRegistry);
}
/** {@inheritDoc} */
......@@ -98,18 +134,18 @@ public class MicrometerMetricsTracker implements IMetricsTracker
@Override
public void recordConnectionUsageMillis(final long elapsedBorrowedMillis)
{
connectionUsage.record(elapsedBorrowedMillis);
connectionUsage.record(elapsedBorrowedMillis, TimeUnit.MILLISECONDS);
}
@Override
public void recordConnectionTimeout()
{
connectionTimeoutMeter.count();
connectionTimeoutCounter.increment();
}
@Override
public void recordConnectionCreatedMillis(long connectionCreatedMillis)
{
connectionCreation.record(connectionCreatedMillis);
connectionCreation.record(connectionCreatedMillis, TimeUnit.MILLISECONDS);
}
}
......@@ -42,7 +42,11 @@ class HikariCPCollector extends Collector {
createGauge("hikaricp_pending_threads", "Pending threads",
PoolStats::getPendingThreads),
createGauge("hikaricp_connections", "The number of current connections",
PoolStats::getTotalConnections)
PoolStats::getTotalConnections),
createGauge("hikaricp_max_connections", "Max connections",
PoolStats::getMaxConnections),
createGauge("hikaricp_min_connections", "Min connections",
PoolStats::getMinConnections)
);
}
......
......@@ -17,10 +17,9 @@
package com.zaxxer.hikari.metrics.prometheus;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Summary;
import java.util.concurrent.TimeUnit;
class PrometheusMetricsTracker implements IMetricsTracker
......@@ -30,23 +29,31 @@ class PrometheusMetricsTracker implements IMetricsTracker
.labelNames("pool")
.help("Connection timeout total count")
.register();
private static final Summary ELAPSED_ACQUIRED_SUMMARY = Summary.build()
.name("hikaricp_connection_acquired_nanos")
.labelNames("pool")
.help("Connection acquired time (ns)")
.register();
private static final Summary ELAPSED_BORROWED_SUMMARY = Summary.build()
.name("hikaricp_connection_usage_millis")
.labelNames("pool")
.help("Connection usage (ms)")
.register();
private static final Summary ELAPSED_CREATION_SUMMARY = Summary.build()
.name("hikaricp_connection_creation_millis")
.labelNames("pool")
.help("Connection creation (ms)")
.register();
private static final Summary ELAPSED_ACQUIRED_SUMMARY =
registerSummary("hikaricp_connection_acquired_nanos", "Connection acquired time (ns)");
private static final Summary ELAPSED_BORROWED_SUMMARY =
registerSummary("hikaricp_connection_usage_millis", "Connection usage (ms)");
private static final Summary ELAPSED_CREATION_SUMMARY =
registerSummary("hikaricp_connection_creation_millis", "Connection creation (ms)");
private final Counter.Child connectionTimeoutCounterChild;
private static Summary registerSummary(String name, String help) {
return Summary.build()
.name(name)
.labelNames("pool")
.help(help)
.quantile(0.5, 0.05)
.quantile(0.95, 0.01)
.quantile(0.99, 0.001)
.maxAgeSeconds(TimeUnit.MINUTES.toSeconds(5))
.ageBuckets(5)
.register();
}
private final Summary.Child elapsedAcquiredSummaryChild;
private final Summary.Child elapsedBorrowedSummaryChild;
private final Summary.Child elapsedCreationSummaryChild;
......
This diff is collapsed.
......@@ -16,10 +16,21 @@
package com.zaxxer.hikari.pool;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_SCHEMA;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import com.zaxxer.hikari.util.DriverDataSource;
import com.zaxxer.hikari.util.PropertyElf;
import com.zaxxer.hikari.util.UtilityElf;
import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import java.lang.management.ManagementFactory;
import java.sql.Connection;
import java.sql.SQLException;
......@@ -30,45 +41,23 @@ import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import com.zaxxer.hikari.pool.HikariPool.PoolInitializationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.util.DriverDataSource;
import com.zaxxer.hikari.util.PropertyElf;
import com.zaxxer.hikari.util.UtilityElf;
import com.zaxxer.hikari.util.UtilityElf.DefaultThreadFactory;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_AUTOCOMMIT;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_CATALOG;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_ISOLATION;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_NETTIMEOUT;
import static com.zaxxer.hikari.pool.ProxyConnection.DIRTY_BIT_READONLY;
import static com.zaxxer.hikari.util.ClockSource.currentTime;
import static com.zaxxer.hikari.util.ClockSource.elapsedMillis;
import static com.zaxxer.hikari.util.ClockSource.elapsedNanos;
import static com.zaxxer.hikari.pool.ProxyConnection.*;
import static com.zaxxer.hikari.util.ClockSource.*;
import static com.zaxxer.hikari.util.UtilityElf.createInstance;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
abstract class PoolBase
{
private final Logger LOGGER = LoggerFactory.getLogger(PoolBase.class);
protected final HikariConfig config;
public final HikariConfig config;
public IMetricsTrackerDelegate metricsTracker;
protected final String poolName;
long connectionTimeout;
long validationTimeout;
IMetricsTrackerDelegate metricsTracker;
private static final String[] RESET_STATES = {"readOnly", "autoCommit", "isolation", "catalog", "netTimeout", "schema"};
private static final int UNINITIALIZED = -1;
......@@ -155,15 +144,15 @@ abstract class PoolBase
try {
setNetworkTimeout(connection, validationTimeout);
final long validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;
final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000;
if (isUseJdbc4Validation) {
return connection.isValid((int) validationSeconds);
return connection.isValid(validationSeconds);
}
try (Statement statement = connection.createStatement()) {
if (isNetworkTimeoutSupported != TRUE) {
setQueryTimeout(statement, (int) validationSeconds);
setQueryTimeout(statement, validationSeconds);
}
statement.execute(config.getConnectionTestQuery());
......
......@@ -78,6 +78,7 @@ public abstract class ProxyConnection implements Connection
LOGGER = LoggerFactory.getLogger(ProxyConnection.class);
ERROR_STATES = new HashSet<>();
ERROR_STATES.add("0A000"); // FEATURE UNSUPPORTED
ERROR_STATES.add("57P01"); // ADMIN SHUTDOWN
ERROR_STATES.add("57P02"); // CRASH SHUTDOWN
ERROR_STATES.add("57P03"); // CANNOT CONNECT NOW
......