Hikari database connection pool default configuration for springboot2

  jdbc

Order

Spring-Boot-2.0.0-M1 version changed the default database connection pool from tomcat jdbc pool to hikari. here, the default configuration of hikari is mainly studied.

spring-configuration-metadata.json

spring-boot-autoconfigure-2.0.0.M7.jar! /META-INF/spring-configuration-metadata.json

      {
      "sourceType": "org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari",
      "name": "spring.datasource.hikari",
      "sourceMethod": "dataSource(org.springframework.boot.autoconfigure.jdbc.DataSourceProperties)",
      "type": "com.zaxxer.hikari.HikariDataSource"
    },
      {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.allow-pool-suspension",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.auto-commit",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.catalog",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.connection-init-sql",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.connection-test-query",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.connection-timeout",
      "type": "java.lang.Long"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.data-source-class-name",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.data-source-j-n-d-i",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.data-source-properties",
      "type": "java.util.Properties"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.driver-class-name",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.health-check-properties",
      "type": "java.util.Properties"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.health-check-registry",
      "type": "java.lang.Object"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.idle-timeout",
      "type": "java.lang.Long"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "deprecated": true,
      "name": "spring.datasource.hikari.initialization-fail-fast", //initializationFailTimeout > 0
      "type": "java.lang.Boolean",
      "deprecation": {}
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.initialization-fail-timeout",
      "type": "java.lang.Long"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.isolate-internal-queries",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.jdbc-url",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "deprecated": true,
      "name": "spring.datasource.hikari.jdbc4-connection-test", //废弃
      "type": "java.lang.Boolean",
      "deprecation": {}
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.leak-detection-threshold",
      "type": "java.lang.Long"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.login-timeout", //在HikariDataSource及PoolBase中
      "type": "java.lang.Integer"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.max-lifetime",
      "type": "java.lang.Long"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.maximum-pool-size",
      "type": "java.lang.Integer"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.metric-registry",
      "type": "java.lang.Object"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.metrics-tracker-factory",
      "type": "com.zaxxer.hikari.metrics.MetricsTrackerFactory"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.minimum-idle",
      "type": "java.lang.Integer"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.password",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.pool-name",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.read-only",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.register-mbeans",
      "type": "java.lang.Boolean"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.scheduled-executor",
      "type": "java.util.concurrent.ScheduledExecutorService"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "deprecated": true,
      "name": "spring.datasource.hikari.scheduled-executor-service",
      "type": "java.util.concurrent.ScheduledThreadPoolExecutor",
      "deprecation": {}
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.schema",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.transaction-isolation", //transactionIsolationName
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.username",
      "type": "java.lang.String"
    },
    {
      "sourceType": "com.zaxxer.hikari.HikariDataSource",
      "name": "spring.datasource.hikari.validation-timeout",
      "type": "java.lang.Long"
    },

HikariConfig

HikariCP-2.7.6-sources.jar! /com/zaxxer/hikari/HikariConfig.java

@SuppressWarnings({"SameParameterValue", "unused"})
public class HikariConfig implements HikariConfigMXBean
{
   private static final Logger LOGGER = LoggerFactory.getLogger(HikariConfig.class);

   private static final char[] ID_CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
   private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
   private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
   private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
   private static final long MAX_LIFETIME = MINUTES.toMillis(30);
   private static final int DEFAULT_POOL_SIZE = 10;
   //......

   /**
    * Default constructor
    */
   public HikariConfig()
   {
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

      minIdle = -1;
      maxPoolSize = -1;
      maxLifetime = MAX_LIFETIME;
      connectionTimeout = CONNECTION_TIMEOUT;
      validationTimeout = VALIDATION_TIMEOUT;
      idleTimeout = IDLE_TIMEOUT;
      initializationFailTimeout = 1;
      isAutoCommit = true;

      String systemProp = System.getProperty("hikaricp.configurationFile");
      if (systemProp != null) {
         loadProperties(systemProp);
      }
   }

   @Override
   public void setConnectionTimeout(long connectionTimeoutMs)
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started.  Use HikariConfigMXBean for runtime changes.");

      if (connectionTimeoutMs == 0) {
         this.connectionTimeout = Integer.MAX_VALUE;
      }
      else if (connectionTimeoutMs < 250) {
         throw new IllegalArgumentException("connectionTimeout cannot be less than 250ms");
      }
      else {
         this.connectionTimeout = connectionTimeoutMs;
      }
   }

   @Override
   public void setIdleTimeout(long idleTimeoutMs)
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started.  Use HikariConfigMXBean for runtime changes.");

      if (idleTimeoutMs < 0) {
         throw new IllegalArgumentException("idleTimeout cannot be negative");
      }
      this.idleTimeout = idleTimeoutMs;
   }

   @Override
   public void setMaximumPoolSize(int maxPoolSize)
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started.  Use HikariConfigMXBean for runtime changes.");

      if (maxPoolSize < 1) {
         throw new IllegalArgumentException("maxPoolSize cannot be less than 1");
      }
      this.maxPoolSize = maxPoolSize;
   }

   @Override
   public void setMinimumIdle(int minIdle)
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started.  Use HikariConfigMXBean for runtime changes.");

      if (minIdle < 0) {
         throw new IllegalArgumentException("minimumIdle cannot be negative");
      }
      this.minIdle = minIdle;
   }

   @Override
   public void setValidationTimeout(long validationTimeoutMs)
   {
      if (sealed) throw new IllegalStateException("The configuration of the pool is sealed once started.  Use HikariConfigMXBean for runtime changes.");

      if (validationTimeoutMs < 250) {
         throw new IllegalArgumentException("validationTimeout cannot be less than 250ms");
      }

      this.validationTimeout = validationTimeoutMs;
   }

   public void validate()
   {
      if (poolName == null) {
         poolName = generatePoolName();
      }
      else if (isRegisterMbeans && poolName.contains(":")) {
         throw new IllegalArgumentException("poolName cannot contain ':' when used with JMX");
      }

      // treat empty property as null
      catalog = getNullIfEmpty(catalog);
      connectionInitSql = getNullIfEmpty(connectionInitSql);
      connectionTestQuery = getNullIfEmpty(connectionTestQuery);
      transactionIsolationName = getNullIfEmpty(transactionIsolationName);
      dataSourceClassName = getNullIfEmpty(dataSourceClassName);
      dataSourceJndiName = getNullIfEmpty(dataSourceJndiName);
      driverClassName = getNullIfEmpty(driverClassName);
      jdbcUrl = getNullIfEmpty(jdbcUrl);

      // Check Data Source Options
      if (dataSource != null) {
         if (dataSourceClassName != null) {
            LOGGER.warn("{} - using dataSource and ignoring dataSourceClassName.", poolName);
         }
      }
      else if (dataSourceClassName != null) {
         if (driverClassName != null) {
            LOGGER.error("{} - cannot use driverClassName and dataSourceClassName together.", poolName);
            // NOTE: This exception text is referenced by a Spring Boot FailureAnalyzer, it should not be
            // changed without first notifying the Spring Boot developers.
            throw new IllegalStateException("cannot use driverClassName and dataSourceClassName together.");
         }
         else if (jdbcUrl != null) {
            LOGGER.warn("{} - using dataSourceClassName and ignoring jdbcUrl.", poolName);
         }
      }
      else if (jdbcUrl != null || dataSourceJndiName != null) {
         // ok
      }
      else if (driverClassName != null) {
         LOGGER.error("{} - jdbcUrl is required with driverClassName.", poolName);
         throw new IllegalArgumentException("jdbcUrl is required with driverClassName.");
      }
      else {
         LOGGER.error("{} - dataSource or dataSourceClassName or jdbcUrl is required.", poolName);
         throw new IllegalArgumentException("dataSource or dataSourceClassName or jdbcUrl is required.");
      }

      validateNumerics();

      if (LOGGER.isDebugEnabled() || unitTest) {
         logConfiguration();
      }
   }

   private void validateNumerics()
   {
      if (maxLifetime != 0 && maxLifetime < SECONDS.toMillis(30)) {
         LOGGER.warn("{} - maxLifetime is less than 30000ms, setting to default {}ms.", poolName, MAX_LIFETIME);
         maxLifetime = MAX_LIFETIME;
      }

      if (idleTimeout + SECONDS.toMillis(1) > maxLifetime && maxLifetime > 0) {
         LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
         idleTimeout = 0;
      }

      if (idleTimeout != 0 && idleTimeout < SECONDS.toMillis(10)) {
         LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName, IDLE_TIMEOUT);
         idleTimeout = IDLE_TIMEOUT;
      }

      if (leakDetectionThreshold > 0 && !unitTest) {
         if (leakDetectionThreshold < SECONDS.toMillis(2) || (leakDetectionThreshold > maxLifetime && maxLifetime > 0)) {
            LOGGER.warn("{} - leakDetectionThreshold is less than 2000ms or more than maxLifetime, disabling it.", poolName);
            leakDetectionThreshold = 0;
         }
      }

      if (connectionTimeout < 250) {
         LOGGER.warn("{} - connectionTimeout is less than 250ms, setting to {}ms.", poolName, CONNECTION_TIMEOUT);
         connectionTimeout = CONNECTION_TIMEOUT;
      }

      if (validationTimeout < 250) {
         LOGGER.warn("{} - validationTimeout is less than 250ms, setting to {}ms.", poolName, VALIDATION_TIMEOUT);
         validationTimeout = VALIDATION_TIMEOUT;
      }

      if (maxPoolSize < 1) {
         maxPoolSize = (minIdle <= 0) ? DEFAULT_POOL_SIZE : minIdle;
      }

      if (minIdle < 0 || minIdle > maxPoolSize) {
         minIdle = maxPoolSize;
      }
   }
}   

You can see that parameter verification is added to the set method, and the validate method is also called in the configuration constructor and getConnection method.

   public HikariDataSource(HikariConfig configuration)
   {
      configuration.validate();
      configuration.copyStateTo(this);
      this.seal();

      LOGGER.info("{} - Starting...", configuration.getPoolName());
      pool = fastPathPool = new HikariPool(this);
      LOGGER.info("{} - Start completed.", configuration.getPoolName());
   }

   /** {@inheritDoc} */
   @Override
   public Connection getConnection() throws SQLException
   {
      if (isClosed()) {
         throw new SQLException("HikariDataSource " + this + " has been closed.");
      }

      if (fastPathPool != null) {
         return fastPathPool.getConnection();
      }

      // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
      HikariPool result = pool;
      if (result == null) {
         synchronized (this) {
            result = pool;
            if (result == null) {
               validate();
               LOGGER.info("{} - Starting...", getPoolName());
               try {
                  pool = result = new HikariPool(this);
                  this.seal();
               }
               catch (PoolInitializationException pie) {
                  if (pie.getCause() instanceof SQLException) {
                     throw (SQLException) pie.getCause();
                  }
                  else {
                     throw pie;
                  }
               }
               LOGGER.info("{} - Start completed.", getPoolName());
            }
         }
      }

      return result.getConnection();
   }

Springboot’s autoconfig initializes HikariDataSource with reflection from BeanUtils and takes the default constructor, so the verification depends on the set method and the subsequent getConnection method.

PoolBase.setLoginTimeout

HikariCP-2.7.6-sources.jar! /com/zaxxer/hikari/pool/PoolBase.java

   /**
    * Set the loginTimeout on the specified DataSource.
    *
    * @param dataSource the DataSource
    */
   private void setLoginTimeout(final DataSource dataSource)
   {
      if (connectionTimeout != Integer.MAX_VALUE) {
         try {
            dataSource.setLoginTimeout(Math.max(1, (int) MILLISECONDS.toSeconds(500L + connectionTimeout)));
         }
         catch (Throwable e) {
            LOGGER.info("{} - Failed to set login timeout for data source. ({})", poolName, e.getMessage());
         }
      }
   }

This is initialized with loginTimeout, and the initial value is Math.max (1) (int) Milliseconds.to seconds (500L+Connectiontimeout))

Summary

The default values for the default configuration of springboot’s HikariDataSource are as follows

name Constructor defaults Values after validate are configured by default Validate reset
minIdle -1 10 MinIdle<0 or minIdle>maxPoolSize is reset to maxPoolSize.
maxPoolSize -1 10 If maxPoolSize is less than 1, it will be reset. When minIdle<=0 is reset to DEFAULT_POOL_SIZE, it is 10; Reset to minIdle value if minIdle>0
maxLifetime MINUTES.toMillis(30) = 1800000 1800000 If it is not equal to 0 and less than 30 seconds, it will be reset back to 30 minutes.
connectionTimeout SECONDS.toMillis(30) = 30000 30000 If it is less than 250 milliseconds, it is reset back to 30 seconds.
validationTimeout SECONDS.toMillis(5) = 5000 5000 If it is less than 250 milliseconds, it will be reset back to 5 seconds.
loginTimeout 10 30 Math.max (1, (int) milliseconds.to seconds (500l+connectiontimeout)), converts connectionTimeout+500ms to the maximum of seconds rounded and 1 rounded.
idleTimeout MINUTES.toMillis(10) = 600000 600000 If idleTimeout+1 second > maxLifetime and maxLifetime>0, it will be reset to 0; If idleTimeout! =0 and less than 10 seconds, it will be reset to 10 seconds.
leakDetectionThreshold 0 0 If it is greater than 0 and is not a unit test, it is further determined that: (leakdtactionthreshold < seconds. tomillis (2) or (leakdtactionthreshold > maxLifetime & & maxLifetime > 0) will be reset to 0. that is, if it is to take effect, it must be > 0 and cannot be less than 2 seconds, and it cannot be greater than maxlifetime when maxlifetime > 0
initializationFailTimeout 1 1
isAutoCommit true true
isReadOnly false fasle
isAllowPoolSuspension false false
isIsolateInternalQueries false false
isRegisterMbeans false false
sealed false true This flag is true after the run starts, indicating that the modification will not be run again.
poolName null HikariPool-1
catalog null null
connectionInitSql null null
connectionTestQuery null null
dataSourceClassName null null
schema null null
transactionIsolationName null null
dataSource null null
dataSourceProperties {} {}
threadFactory null null
scheduledExecutor null null
metricsTrackerFactory null null
metricRegistry null null
healthCheckRegistry null null
healthCheckProperties {} {}

doc