[TIL #35] 데드락에 죽다 살다

2024. 12. 15. 20:24·Camp/T.I.L.

현재 진행 중인 프로젝트는 레이드 컨텐츠 입장을 위해 큐를 사용한다.

최초로 개발하며 개념 검증 시엔 원시적인 큐만 사용했고, 이는 동시성 문제어 이어질 수 있었다.

따라서 Bull을 도입하게 됐다. 

 

하지만 Bull만 도입한다고 되는 것은 아니었다.

Bull 자체의 ``Job``에 대해선 원자적 처리를 보장하지만 그 이외의 부분에 대해선 원자적 처리가 보장되지 않는 상황이다.

각각의 필요한 모든 부분에 대해 명시적으로 락을 걸어 완전한 원자적 처리를 보장해야 했다.

이러한 처리를 위해 async-lock을 도입했다.

 

// pendingGroups 접근을 안전하게 하기 위한 헬퍼 메서드
async withPendingGroupsLock(fn) {
  return this.pendingGroupsLock.acquire('pendingGroups', fn);
}

위와 같이 락을 걸기 위한 헬퍼를 만들었다.

``fn``의 형태로 함수를 인자로 받는데, 그 함수는 락이 걸린 상태로 동작하게 된다.

 

await this.withPendingGroupsLock(async () => {
  for (const [groupId, group] of this.pendingGroups.entries()) {
    const allUsersExist = Array.from(group.userIds).every((uid) => sessionManager.getUser(uid));
    if (!allUsersExist) {
      logger.info(`그룹 ${groupId}의 일부 유저가 존재하지 않아 그룹 제거`);
      this.pendingGroups.delete(groupId);
    }
  }
});

이런 식으로 락을 걸어 사용할 수 있다.

 

하지만 데드락을 항상 조심해야 한다.

임시로 처리되게 한 부분을 좀 더 확실한 형태로 바꾸면서 데드락이 발생했다.

분명 코드만 보면 정상적이었음에도 불구, 프로그램이 더 이상 진행되지 않는 것이다.

로그를 찍어보며 어디에서 진행이 막히는지는 파악했으나, 그 이유를 알 수 없었다.

좀 더 들여다보니 그 이유는 `데드락`이었다.

 

async acceptUserInGroup(user, groupId) {
    // ...
    
    
    // 유효한 플레이어 모두 수락 처리
    for (const p of actualMatchedPlayers) {
      await this.removeAcceptQueueInUser(p);
      p.setMatched(false);
    }
    
    // ...
  }
  return null;
}

``acceptUserInGroup``을 호출하는 상위 함수엔 이미 ``withPendingGroupsLock``이 걸려있다.

그 상태에서 ``this.removeAcceptQueueInUser``가 정상 진행 시 호출되는데, 여기가 문제였다.

 

async removeAcceptQueueInUser(user) {
  return this.withPendingGroupsLock(async () => {
    //...
  });
}

이미 잡은 락을 또 잡고 있었다.

이러니 진행이 될 수가 있나...

``removeAcceptQueueInUser``의 락을 제거하고 나니 정상적으로 동작하는 것을 확인할 수 있었다.

 

이건 에러도 없다.

티를 안 내니 찾기가 쉽지 않다.

동시성 문제라는 것이 정말 만만하지 않다.

사람 잡는 요물이라고 하기에 손색없으리라.

'Camp > T.I.L.' 카테고리의 다른 글

[TIL #34] Docker 및 Docker Compose 올려보기  (0) 2024.12.03
[TIL #33] 마법의 엘리베이터  (0) 2024.11.20
[TIL #32] 자바스크립트와 싱글톤  (0) 2024.11.18
[TIL #31] 상태 업데이트의 벽  (0) 2024.10.31
[TIL #30] 모의 면접을 준비하며  (0) 2024.10.30
'Camp/T.I.L.' 카테고리의 다른 글
  • [TIL #34] Docker 및 Docker Compose 올려보기
  • [TIL #33] 마법의 엘리베이터
  • [TIL #32] 자바스크립트와 싱글톤
  • [TIL #31] 상태 업데이트의 벽
BVM
BVM
  • BVM
    E:\
    BVM
  • 전체
    오늘
    어제
    • 분류 전체보기 (173)
      • Thoughts (14)
      • Study (75)
        • Japanese (3)
        • C++ & C# (50)
        • Javascript (3)
        • Python (14)
        • Others (5)
      • Play (1)
        • Battlefield (1)
      • Others (10)
      • Camp (73)
        • T.I.L. (57)
        • Temp (1)
        • Standard (10)
        • Challenge (3)
        • Project (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 본 블로그 개설의 목적
  • 인기 글

  • 태그

    c#
    베데스다
    FF14
    Asio
    db
    네트워크 프로그래밍
    docker
    프로그래머스
    포인터
    Server
    네트워크
    암호화
    클라우드
    7계층
    서버
    discord
    discord py
    JS
    C++
    Dalamud
    스타필드
    bot
    로깅
    Python
    Selenium
    IOCP
    OSI
    boost
    Network
    cloudtype
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
BVM
[TIL #35] 데드락에 죽다 살다
상단으로

티스토리툴바