최근 플로니 앱의 론칭을 앞두고 베타테스트에 들어갔다.
그래서 그런지..AWS 프리티어를 사용하는 서버가 자꾸 터진다.
여러 원인이 있겠지만 로그는 다음과 같이 찍힌다
결국 같은 백엔드 팀원과 비상 회의가 열렸다.
(1) 추측 1 - HIkari pool이 모자란걸 보아, 한 번의 요청에 여러 Pool이 쓰이나?
근데 난 Hikari Pool이 뭔지부터 알아야했다.
1. 전통적인 WAS 와 DB 연결 방법
회사에서 레거시 시스템을 담당하는 중이라 아주 잘 안다 ㅎㅎ..
보통 드라이버를 로드 하고, conntection을 열어서 query를 직접 날린다
근데, 매 쿼리마다 이렇게 하면 비용이 많이 든다. DB나 웹서버나 서로서로 TCP 커넥션을 생성해야 함
2. Connection Pool
Hikari Pool : DB랑 연결해주는 Connection Pool 오픈 소스
Connection Pool은 미리 WAS 가 시행될 때 DB 연결을 위해 객체를 만들어서 POOL에 담아뒀다가 하나씩 꺼내 쓴다.
(1) 서버의 부하를 줄여준다.
(2) 한정적인 서버 자원 관리가 된다
3. 그럼 무작정 크게 만들면 좋은가?
자원 사용 효율적 X : 사용하지 않고 남는 Connection은 작업을 하지 못하고 놀게 되어, 메모리만 차지한다.
DISK 측면 : DB에서는 HDD 하나당 하나의 I/O를 처리하기에 Blocking이 발생, 병렬 처리가 아니니깐.. 앞에 끝날 때까지 기다려야 함
DB 측면 : context switching에 오버헤드 발생
HikariCP Dead lock에서 벗어나기 (이론 편) | 우아한 형제들 기술블로그 (woowahan.com)
관련 블로그를 읽고 pool을 두번 쓰이는 곳이 있나..?라는 내 추측에 힘을 실어 코드를 다시 살펴보았다.
* 서비스 계층 메서드
* repository 계층 메서드
둘 다 @Transactional을 붙여놓은 게 있어서 혹시 이것 때문인가? 싶었다.
하지만, 팀원분의 말로는 우리의 프로젝트 트랜잭션은 한 개가 달려있어도 새로운 Transaction이 생기지 않는다고 알려주셨다. 창피하게도 이제야 알았다니!
@Transactional을 달면, 예상 동작 방식은 아마 이럴 것이다
public class UserService{
public Long registerUser(User user){
Connection connection = dataSource.getConnection();
try(connection){
connection.setAutoCommit(false); //트랜잭션 auto 꺼놓았을 경우
connection.ommit();
}catch(Exception e){
connection.rollback()
}
근데 내가 작성한 코드 위에 이런 코드를 얹기란 어려운 법이니깐, 스프링은 Proxy를 이용한다.
Proxy는 딱 DB의 connection 열고 닫기를 관여하고 나머지는 그 안에서 우리가 짠 코드에 역할을 위임한다.
이렇게 하면 Controller입장에서는 Service가 Proxy인지 Reals인지 알 수 없게 된다
이러한 원리로 만약 Proxy가 열리고 그 안에서 다른 @Transactional이 붙은 메서드를 만나면 Proxy가 열리지 않는다. 그러면 @Transactional을 두 번 써서 pool이 모잘랐다! => 이 가설은 틀렸다.
2. 서버가 튕기는 시간 == 빌드할 때??
서버가 죽을 때는 다음과 같이 CPU 사용량이 90%까지 치솟는다..(왜 그래...)
아주 가아아아끔 해당 시간이랑 코드가 머지돼서 빌드할 때랑 동일할 때가 있었다.
그래서, 저 문제가 나타났던 시간에 머지되었던 코드를 Re-Build 해봤지만 너무 정상작동했고,,
머지될 시점뿐만 아니라 그냥 튕기는 날도 있어서 애매한 가설이라 탈락
3. 램 문제이다
EC2를 프리티어로 사용하고 있는 Floney의 서버는 치명적인 단점이 있다.
바로 작고 소중한 램.. AWS의 EC2는 30GB SSD와 1GB의 메모리를 가지고 있다.,..
현재 EC2의 메모리 사용량을 보니깐 약 53% 정도의 메모리를 사용하고 있는 것을 확인했지만 이 정도론 램에 문제가 없을 것 같다. + 메모리가 부족했다면 Out Of Memory를 뱉지 않았을까.?
4. 결론
근본적인 원인을 파악할 수 없었다. 어떤 특정 현상에서 재현이 되는 상황도 아니어서
(1) 우선 램 메모리 이슈는 잠재적인 문제이니, swap 메모리를 할당하여 메모리를 늘려준다
(2) 우선 지켜본다! 다행히 조치 이후에 서버는 잘 동작 중이다.(진짜 램 문제였을까?)
(3) 실제 배포할 때, 모니터링 서버를 구축하거나..(비용 측면 고려하여)하도록 하자
(이번 상황 같은 일을 대비하고자 2024.1월 기준 모니터링 서버를 구축했다 -
2024.01.05 - [Floney] - [플로니] 에러에 대비하는 서비스 만들기)
팀원이 어느날 원인을 알려주셨다(짝짝짝)
우리의 예상대로 github action을 통해 서버가 빌드 될때, 문제가 있었다.
'Floney' 카테고리의 다른 글
[플로니] 가계부 금액 타입 정하기 (double vs decimal) (4) | 2023.11.12 |
---|---|
[플로니] 데이터 동시성 제어하기 (0) | 2023.10.08 |
[플로니] Spring Batch로 이월 설정 구현하기 (0) | 2023.07.12 |
[플로니] 초기화된 캘린더 응답 방식 포함 시켜 만들기 (0) | 2023.05.30 |
[플로니] default 설정을 했는데 null이 나와요 (0) | 2023.05.09 |