티스토리 뷰

JVM/Spring

[Hikari CP] 光 살펴보기 - 4

글을 쓰는 개발자 2022. 4. 29. 08:45
반응형

getConnection

   public Connection getConnection(final long hardTimeout) throws SQLException
   {
      suspendResumeLock.acquire();
      final long startTime = currentTime();

      try {
         long timeout = hardTimeout;
         do {
            PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
            if (poolEntry == null) {
               break; // We timed out... break and throw exception
            }

            final long now = currentTime();
            if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
               closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
               timeout = hardTimeout - elapsedMillis(startTime);
            }
            else {
               metricsTracker.recordBorrowStats(poolEntry, startTime);
               return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
            }
         } while (timeout > 0L);

         metricsTracker.recordBorrowTimeoutStats(startTime);
         throw createTimeoutException(startTime);
      }
      catch (InterruptedException e) {
         Thread.currentThread().interrupt();
         throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
      }
      finally {
         suspendResumeLock.release();
      }
   }

connectionBag.borrow(timeout, MILLISECONDS)

   public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException
   {
      // Try the thread-local list first
      final List<Object> list = threadList.get();
      for (int i = list.size() - 1; i >= 0; i--) {
         final Object entry = list.remove(i);
         @SuppressWarnings("unchecked")
         final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
         if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            return bagEntry;
         }
      }

      // Otherwise, scan the shared list ... then poll the handoff queue
      final int waiting = waiters.incrementAndGet();
      try {
         for (T bagEntry : sharedList) {
            if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
               // If we may have stolen another waiter's connection, request another bag add.
               if (waiting > 1) {
                  listener.addBagItem(waiting - 1);
               }
               return bagEntry;
            }
         }

         listener.addBagItem(waiting);

         timeout = timeUnit.toNanos(timeout);
         do {
            final long start = currentTime();
            final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);
            if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
               return bagEntry;
            }

            timeout -= elapsedNanos(start);
         } while (timeout > 10_000);

         return null;
      }
      finally {
         waiters.decrementAndGet();
      }
   }

초반 부분

      // Try the thread-local list first
      final List<Object> list = threadList.get();
      for (int i = list.size() - 1; i >= 0; i--) {
         final Object entry = list.remove(i);
         @SuppressWarnings("unchecked")
         final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;
         if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {
            return bagEntry;
         }
      }

ThreadLocalMap & ThreadLocal

  • ThreadLocalMap
public class Thread implements Runnable {
    private static native void registerNatives();
    static {
        registerNatives();
    }

    ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap은 Thread 필드로 가지고 있으며, 이는 특정 스레드의 정보를 ThreadLocal에서 직접 호출할 수 있도록 되어 있다.
ThreadLocal을 이용하면 쓰레드 영역에 변수를 설정할 수 있기 때문에, 특정 쓰레드가 실행하는 모든 코드에서 설정된 변수 값을 사용할 수 있게 된다.
참고: ThreadLocal

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

for (int i = list.size() - 1; i >= 0; i--)

뒤에서 부터 읽는 것은 바로 삭제하기 때문이다. 만약에 0부터 시작했다면 삭제했을 때 데이터의 순서보장이 이루어지지 않기 때문에 뒤에서부터 데이터를 읽는 것으로 확인된다.

final Object entry = list.remove(i);

WeakReference

참고: Naver D2 Reference와 GC

LRU 캐시와 같은 애플리케이션에서는 softly reachable 객체보다는 weakly reachable 객체가 유리하므로 LRU 캐시를 구현할 때에는 대체로 WeakReference를 사용한다

  • weakly reachable: strongly reachable 객체도 softly reachable 객체도 아닌 객체 중에서, phantom reference 없이 weak reference만 통과하는 참조 사슬이 하나라도 있는 객체

나머지 borrow 부분은 다른 스레드가 먼저 들어올 때 처리와 마지막에 해당 타임아웃을 검사하면서 해당 스레드가 존재할 때 그 스레드를 가져오고 존재하지 않는다면 null 값을 반환하는 작업을 하고 있다.

getConnection 핵심 부분


if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
    closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
    timeout = hardTimeout - elapsedMillis(startTime);
}
else {
    metricsTracker.recordBorrowStats(poolEntry, startTime);
    return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}

참고: understanding-hikaricps-connection-pooling-behaviour

 

코드를 그래프로 표시했을 때 다음과 같이 작동됨을 알 수 있습니다.

 

반응형

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

[Spring] @Scheduled, @EnableScheduling  (0) 2022.10.01
[Spring] Scope (Prototype & Singleton)  (2) 2022.09.13
[Hikari CP] 光 살펴보기 - 3  (0) 2022.04.28
[Hikari CP] 光 살펴보기 - 2  (0) 2022.04.27
[Hikari CP] 光 살펴보기 - 1  (0) 2022.04.26
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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 31
글 보관함