티스토리 뷰

JVM/Spring

[Hikari CP] 光 살펴보기 - 1

글을 쓰는 개발자 2022. 4. 26. 08:40
반응형

설정값은 어떻게 처리되나?


application.properties 혹은 application.yml 의 값들은 어떻게 처리되는 것일까?

해당 내용을 자세히 확인해보고 싶으면 HikariConfig
을 참조 해주세요.

자주 쓰이는 값들

✔autoCommit

   // Properties NOT changeable at runtime
   //
   private boolean isAutoCommit;
   public HikariConfig()
   {
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

      isAutoCommit = true;

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

설정된 코드를 보면 Default로 autoCommit이 참으로 되어 있는 것을 확인 할 수 있습니다.

auto commit 이란

the intention of auto-commit mode is to lift the burden from developers of having to manage transactions themselves

Baeldung auto commit
번역을 하자면 개발자가 모든 코드에서 try catch finally를 쓰지 않게 해주는 옵션이라고 생각하시면 됩니다.

🕰 connectionTimeout

클라이언트가 하나의 커넥션을 물고 있을 수 있는 시간입니다. (최소는 25초이며 default는 30초로 되어 있습니다.)

   private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);

   // Properties changeable at runtime through the HikariConfigMXBean
   private volatile long connectionTimeout;
   public HikariConfig()
   {
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

      connectionTimeout = CONNECTION_TIMEOUT;

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

   private static final long SOFT_TIMEOUT_FLOOR = Long.getLong("com.zaxxer.hikari.timeoutMs.floor", 250L);

   public void setConnectionTimeout(long connectionTimeoutMs)
   {
      if (connectionTimeoutMs == 0) {
         this.connectionTimeout = Integer.MAX_VALUE;
      }
      else if (connectionTimeoutMs < SOFT_TIMEOUT_FLOOR) {
         throw new IllegalArgumentException("connectionTimeout cannot be less than " + SOFT_TIMEOUT_FLOOR + "ms");
      }
      else {
         this.connectionTimeout = connectionTimeoutMs;
      }
   }

보시다시피 0으로 설정하면 사실상 클라이언트에서 처리할 때까지 무한대로 물 수 있으며, 25초 이하의 값으로 설정하면 에러가 나는 것을 확인할 수 있습니다.

🕰 idleTimeout

   private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);

   private volatile long idleTimeout;
   public HikariConfig()
   {
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

      idleTimeout = IDLE_TIMEOUT;

      String systemProp = System.getProperty("hikaricp.configurationFile");
      if (systemProp != null) {
         loadProperties(systemProp);
      }
   }
      if (idleTimeout + SECONDS.toMillis(1) > maxLifetime && maxLifetime > 0 && minIdle < maxPoolSize) {
         LOGGER.warn("{} - idleTimeout is close to or more than maxLifetime, disabling it.", poolName);
         idleTimeout = 0;
      }
      else if (idleTimeout != 0 && idleTimeout < SECONDS.toMillis(10) && minIdle < maxPoolSize) {
         LOGGER.warn("{} - idleTimeout is less than 10000ms, setting to default {}ms.", poolName, IDLE_TIMEOUT);
         idleTimeout = IDLE_TIMEOUT;
      }
      else  if (idleTimeout != IDLE_TIMEOUT && idleTimeout != 0 && minIdle == maxPoolSize) {
         LOGGER.warn("{} - idleTimeout has been set but has no effect because the pool is operating as a fixed size pool.", poolName);
      }

default 값은 10분입니다.

idleTimeout이 maxLifetime보다 크고 minIdle 크기가 maxPoolSize보다 작으면 idleTimeout이 0이 됩니다.
idleTimeout이 10분보다 작게 설정되었으며 minIdle < maxPoolSize로 되어 있으면 설정값이 적용되며
minIdle 과 maxPoolSize가 같으면 idleTimeout 설정값은 무의미해집니다.

🕰 keepaliveTime

커넥션을 얼마나 유지할 것인지에 대한 설정 값
데이터베이스나 네트워크 인프라에 의해 타임아웃 되는 것을 방지하기 위한 설정 값
default는 0 (disabled)

   private static final long DEFAULT_KEEPALIVE_TIME = 0L;

   private long keepaliveTime;
      // keepalive time must larger then 30 seconds
      if (keepaliveTime != 0 && keepaliveTime < SECONDS.toMillis(30)) {
         LOGGER.warn("{} - keepaliveTime is less than 30000ms, disabling it.", poolName);
         keepaliveTime = DEFAULT_KEEPALIVE_TIME;
      }

      // keepalive time must be less than maxLifetime (if maxLifetime is enabled)
      if (keepaliveTime != 0 && maxLifetime != 0 && keepaliveTime >= maxLifetime) {
         LOGGER.warn("{} - keepaliveTime is greater than or equal to maxLifetime, disabling it.", poolName);
         keepaliveTime = DEFAULT_KEEPALIVE_TIME;
      }

설정값을 주게 되면 30초 보다 크거나 같아야 하며, maxLifetime 값보다 작아야 합니다.

hikariPool에서 keepaliveTime

         final long keepaliveTime = config.getKeepaliveTime();
         if (keepaliveTime > 0) {
            // variance up to 10% of the heartbeat time
            final long variance = ThreadLocalRandom.current().nextLong(keepaliveTime / 10);
            final long heartbeatTime = keepaliveTime - variance;
            poolEntry.setKeepalive(houseKeepingExecutorService.scheduleWithFixedDelay(new KeepaliveTask(poolEntry), heartbeatTime, heartbeatTime, MILLISECONDS));
         }

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

🕰 maxLifetime

커넥션이 살 수 있는 최대시간

We strongly recommend setting this value, and it should be several seconds shorter than any database or infrastructure imposed connection time limit
   private static final long MAX_LIFETIME = MINUTES.toMillis(30);

   private volatile long maxLifetime;
         final long maxLifetime = config.getMaxLifetime();
         if (maxLifetime > 0) {
            // variance up to 2.5% of the maxlifetime
            final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;
            final long lifetime = maxLifetime - variance;
            poolEntry.setFutureEol(houseKeepingExecutorService.schedule(new MaxLifetimeTask(poolEntry), lifetime, MILLISECONDS));
         }

위와 같이 설정한 이유는 주석으로 다음과 같이 되어 있다.

Creating new poolEntry.  If maxLifetime is configured, create a future End-of-life task with 2.5% variance from the maxLifetime time to ensure there is no massive die-off of Connections in the pool.

2.5%를 분산하여 향후 대량으로 커넥션이 소멸되지 않도록 생성을 하기 위해서 다음과 같이 진행한다고 합니다.

minimumIdle

idle한 커넥션의 최소 개수를 설정하는 config

we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize

왠만해서는 건들지 말고 주어진 값대로 쓰는 것이 좋다고 합니다.


   private volatile int minIdle;

   public HikariConfig()
   {
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

      minIdle = -1;
      maxPoolSize = -1;
      String systemProp = System.getProperty("hikaricp.configurationFile");
      if (systemProp != null) {
         loadProperties(systemProp);
      }
   }
   private static final int DEFAULT_POOL_SIZE = 10;

      if (maxPoolSize < 1) {
         maxPoolSize = DEFAULT_POOL_SIZE;
      }

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

default값은 -1이며 maxPoolSize도 -1입니다.
이 때 validateNumerics 메소드에서 초기화 작업이 일어나는데 둘 다 설정 값을 넣지 않으면 DEFAULT_POOL_SIZE 값이 설정되며 그 값은 10입니다.

maximumPoolSize

A reasonable value for this is best determined by your execution environment. 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.

환경에 맞게 값을 설정을 해줘야 한다고 한다. 해당 설정에 대한 자세한 내용은 아래의 링크를 참고해주시면 좋을 것 같습니다.
우아한 형제들 HikariCP - 이론편

HikariCP pool sizing

Even a computer with one CPU core can "simultaneously" support dozens or hundreds of threads. But we all [should] know that this is merely a trick by the operating system though the magic of time-slicing. In reality, that single core can only execute one thread at a time; then the OS switches contexts and that core executes code for another thread, and so on. It is a basic Law of Computing that given a single CPU resource, executing A and B sequentially will always be faster than executing A and B "simultaneously" through time-slicing. Once the number of threads exceeds the number of CPU cores, you're going slower by adding more threads, not faster.

요약을 하자면 하나의 코어는 하나의 스레드만 실행할 수 있으면 동시에 진행되는 스레드는 결국 문맥교환이 일어나기 때문에 많은 스레드의 갯수는 오히려 더 느려지는 상황을 만들 수 있다는 것!!

HikariPool constructor

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

maxPoolSize기준으로 커넥션 큐를 생성하는 모습을 볼 수 있습니다.

간단하게 HikariCP config 설정 값이 어떻게 내부에서 돌아가는 지 알아봤습니다.
다음에는 좀 더 내부 내용을 다뤄보도록 하겠습니다.

반응형

'JVM > Spring' 카테고리의 다른 글

[Hikari CP] 光 살펴보기 - 3  (0) 2022.04.28
[Hikari CP] 光 살펴보기 - 2  (0) 2022.04.27
[Spring] AOP  (0) 2022.04.05
[Spring] @Transactional  (0) 2022.04.02
[JAVA][Spring][dirty checking] 준영속 엔티티 대해서  (0) 2021.12.09
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함