Talk about fixed pool design of hikari connection pool.

  jdbc

Order

This article mainly studies the fixed pool design of hikari connection pool.

fixed pool design

Hikari’s authors prefer the concept of fixed pool design, which suggests that the minimumIdle and maximumPoolSize should be set to the same as the connection pool with fixed connection size. The author thinks that if the minimumIdle is less than maximumPoolSize, additional connections will be needed when traffic surges. at this time, processing new connections in the request method will cause performance loss, i.e. the database will reduce the speed of connection establishment on the one hand and the completion of existing connection transactions on the other hand, which will indirectly affect the speed of returning these existing connections to the connection pool.

The author thinks that the setting of minimumIdle is the same as the setting of maximumPoolSize, and redundant idle connections will not have any serious impact on the overall performance. if the setting of minimumIdle is less than maximumPoolSize , it is to release connections to release memory for other functions when necessary, but connection pool may also reach maximumPoolSize during peak periods, so this purpose seems to have no effect.

HikariPool.houseKeeperTask

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

   private final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", SECONDS.toMillis(30));

   /**
    * Construct a HikariPool with the specified configuration.
    *
    * @param config a HikariConfig instance
    */
   public HikariPool(final HikariConfig config)
   {
      super(config);

      this.connectionBag = new ConcurrentBag<>(this);
      this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;

      this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();

      checkFailFast();

      if (config.getMetricsTrackerFactory() != null) {
         setMetricsTrackerFactory(config.getMetricsTrackerFactory());
      }
      else {
         setMetricRegistry(config.getMetricRegistry());
      }

      setHealthCheckRegistry(config.getHealthCheckRegistry());

      registerMBeans(this);

      ThreadFactory threadFactory = config.getThreadFactory();

      LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());
      this.addConnectionQueue = unmodifiableCollection(addConnectionQueue);
      this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());
      this.closeConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

      this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);

      this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, HOUSEKEEPING_PERIOD_MS, MILLISECONDS);
   }

HouseKeeperTask is initialized when HikariPool is initialized.

HikariPool.HouseKeeper

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

   /**
    * The house keeping task to retire and maintain minimum idle connections.
    */
   private final class HouseKeeper implements Runnable
   {
      private volatile long previous = plusMillis(currentTime(), -HOUSEKEEPING_PERIOD_MS);

      @Override
      public void run()
      {
         try {
            // refresh timeouts in case they changed via MBean
            connectionTimeout = config.getConnectionTimeout();
            validationTimeout = config.getValidationTimeout();
            leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());

            final long idleTimeout = config.getIdleTimeout();
            final long now = currentTime();

            // Detect retrograde time, allowing +128ms as per NTP spec.
            if (plusMillis(now, 128) < plusMillis(previous, HOUSEKEEPING_PERIOD_MS)) {
               LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",
                           poolName, elapsedDisplayString(previous, now));
               previous = now;
               softEvictConnections();
               return;
            }
            else if (now > plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) {
               // No point evicting for forward clock motion, this merely accelerates connection retirement anyway
               LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now));
            }

            previous = now;

            String afterPrefix = "Pool ";
            if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {
               logPoolState("Before cleanup ");
               afterPrefix = "After cleanup  ";

               final List<PoolEntry> notInUse = connectionBag.values(STATE_NOT_IN_USE);
               int toRemove = notInUse.size() - config.getMinimumIdle();
               for (PoolEntry entry : notInUse) {
                  if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {
                     closeConnection(entry, "(connection has passed idleTimeout)");
                     toRemove--;
                  }
               }
            }

            logPoolState(afterPrefix);

            fillPool(); // Try to maintain minimum connections
         }
         catch (Exception e) {
            LOGGER.error("Unexpected exception in housekeeping task", e);
         }
      }
   }

Assuming that the minimumIdle and maximumPoolSize are set to the same, this task will directly execute fillPool when it is first executed.

fillPool

   /**
    * Fill pool up from current idle connections (as they are perceived at the point of execution) to minimumIdle connections.
    */
   private synchronized void fillPool()
   {
      final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections())
                                   - addConnectionQueue.size();
      for (int i = 0; i < connectionsToAdd; i++) {
         addConnectionExecutor.submit((i < connectionsToAdd - 1) ? POOL_ENTRY_CREATOR : POST_FILL_POOL_ENTRY_CREATOR);
      }
   }

For this fillPool, at the initialization time, the minimumIdle and maximumPoolSize values are the same, and the totalConnections and idleConnections are both 0, then the value of connectionsToAdd is maximumPoolSize.
That is to say, this task will add a maximumPoolSize size connection.

Summary

  • tomcat jdbc pool

There is an initial-size parameter to specify how many connections are initialized at the beginning, min-idle and max-idle to control the minimum and maximum values of idle connections, and max-active to control the total size of connection pool. Min-evitable-idle-time-millis is used to specify the duration of idle connections, and time-between-eviction-runs-millis is used to specify the scheduling interval of tasks that clean up idle connections.

  • hikari connection pool

MinIdle is used to specify the minimum number of idle connections. MAXPOLSIZE specifies the maximum value of connection pool connections. The default initialization is to initialize minIdle-sized connections. If minIdle is equal to MAXPOLSIZE, then the connection pool is filled up during initialization. Idletime is used to specify the duration of idle connections and maxLifetime is used to specify the duration of all connections. Com.zaxer.hikari.housekeeping.periodms is used to specify the scheduling interval of HouseKeeper tasks for connection pool idle connection processing and connection pool number supplement.

In other words, hikari has a maxLifetime more than tomcat jdbc pool, which means all connections must be reconnected after maxLifetime to ensure the activity of the connection pool.

doc