sequence

The spring-boot-2.0.0-M1 version changes the default database connection pool from Tomcat JDBC Pool to HIkari. Here we will focus on the default hikari configuration

spring-configuration-metadata.json

Spring – the 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", / / scrap"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"// In HikariDataSource and 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"
    },
Copy the code

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 */ publicHikariConfig()
   {
      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 voidsetConnectionTimeout(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; }}}Copy the code

You can see that parameter validation is added to the set method and that the validate method is called in the Configuration constructor and in the 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();
   }
Copy the code

Springboot’s Autoconfig uses BeanUtils reflection to initialize HikariDataSource using the default constructor, so validation relies on set and subsequent getConnection methods.

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()); }}}Copy the code

Math.max(1, (int) MILLISECONDS. ToSeconds (500L + connectionTimeout))

summary

The default values of springboot HikariDataSource are as follows

name Constructor default value The default value after validate is configured Validate the reset
minIdle – 1 10 MinIdle <0 or minIdle>maxPoolSize is reset to maxPoolSize
maxPoolSize – 1 10 If maxPoolSize is less than 1, it is reset. When minIdle<=0 is reset to DEFAULT_POOL_SIZE, it is 10. If minIdle>0, reset the value to minIdle
maxLifetime MINUTES.toMillis(30) = 1800000 1800000 If it is not 0 and less than 30 seconds, it is reset back to 30 minutes
connectionTimeout SECONDS.toMillis(30) = 30000 30000 If less than 250 milliseconds, it is reset back to 30 seconds
validationTimeout SECONDS.toMillis(5) = 5000 5000 If less than 250 milliseconds, it is reset back to 5 seconds
loginTimeout 10 30 Math.max(1, (int) MILLISECONDS. ToSeconds (500L + connectionTimeout)), connectionTimeout+500ms
idleTimeout MINUTES.toMillis(10) = 600000 600000 If idleTimeout+1 second >maxLifetime and maxLifetime>0, it will be reset to 0. If idleTimeout! If =0 and less than 10 seconds, it is reset to 10 seconds
leakDetectionThreshold 0 0 If it is greater than 0 and not a unit test, then further judge: (leakDetectionThreshold < seconds.tomillis (2) or (leakDetectionThreshold > maxLifetime && maxLifetime > 0), will be reset to 0. That is, to take effect, the value must be greater than 0 and cannot be less than 2 seconds. When maxLifetime is greater than 0, the value cannot be greater than maxLifetime
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 is started, indicating that the changes are no longer being run
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

  • Spring – the Boot – 2.0.0 – M1 – Release – the Notes