잘못된 Queue 사이즈로 인한 문제 상황
최근 회사에서 새로운 기능을 배포했는데 특정 인스턴스들에서 간헐적으로 RejectedExecutionException이 발생했습니다.
예외 상세 메세지는 아래와 같았는데요.
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor$1@d02af99[Running, pool size = 1000, active threads = 8, queued tasks = 100, completed tasks = 6805414]
보시면 pool size에 비해 active thread가 여유가 있는데도 예외가 발생했습니다.
분석 시 문제는 Thread Pool에 설정된 coreSize, queueSize와 관련이 있었는데요.
아래와 같은 시나리오로 조건이 충족된 인스턴스들에서 문제가 발생했던 것이었습니다.
(Thread Pool의 maxSize=1000, coreSize=400, queueSize=100 일때)
- 한 인스턴스에 많은 쓰레드를 요하는 요청들이 동시 발생
- Thread Pool 의 쓰레드 수가 400(coreSize)을 넘어감 - 이 조건 만족 시 무조건 queue에 넣었다가 실행
- 100개(queueSize)보다 많은 수의 쓰레드를 동시에 생성하는 요청이 문제 인스턴스로 유입
- Thread Pool에 queue가 100개(queueSize) 넘어가면서 예외 발생
queue로 한꺼번에 많은 요청을 넣었던 코드는 아래와 같습니다.
CompletableFuture<Foo> fooFutures = largeList.stream()
.map(service.asyncAction)
.toList();
적정 Thread Pool Queue Size
이번 장애를 겪으면서 Queue Size는 maxSize과 비슷하거나 커야 한다고 판단했습니다.
그 이유는 queueSize가 maxSize보다 적다면 갑자기 maxSize만큼의 async 실행이 발생할 때 queueSize가 병목지점이 되어 maxSize를 다 활용할 수 없기 때문입니다.
maxSize 큰데 얼마나 커야 할지는 서버 자원, 서비스의 성격, 평균 처리 속도에 따라 달라집니다.
백오피스 성격이라 서버 자원도 넉넉하고 오래 걸려도 실패하지 않는게 중요하다면 maxSize의 수십배까지도 queueSize를 설정할 수 있습니다.
반대로 사용자에게 빨리 실패를 알려줄 필요가 있는 서비스라면 딱 maxSize 정도로 설정하는게 좋습니다. 빠른 실패 후 재시도 한다면 정상인 다른 서버로 연결 후 성공할 수도 있으니까요.
'제안&정리' 카테고리의 다른 글
| 이제야 해보는 맥북 설정 자동화 (1) | 2024.04.28 |
|---|---|
| [Git Strategy] 팀의 속도를 올리는 방법 - Ship Show Ask (0) | 2024.03.03 |
| [Spring] 동일 타입 여러 빈을 @Primary, @Qualifier 으로 사용하기 (1) | 2023.10.29 |
| [JAVA] LocalDate atStartOfDay (1) | 2023.10.23 |
| [Java] 스레드 풀 설정 - corePoolSize, maxPoolSize, queueCapacity (2) | 2023.10.15 |