Java, Kotlin 백엔드 개발자
About Me
안녕하세요! 백엔드 개발자 김성산입니다.
간결하고 명확한 코드를 작성하기 위해 노력합니다.
안정적인 프로세스를 제공하는 것에 관심이 많습니다.
페어 프로그래밍과 코드 리뷰를 즐깁니다.
TOC
Posting
깊이 학습한 내용을 공유하기 위한 포스팅 공간입니다.
Search
목차
배경
서비스를 개발하고 고도화가 진행되면 필연적으로 동시성 문제를 마주하게 되는데, 저희 모락도 예외는 아니었습니다. 기본적으로 웹 환경에서는 같은 시간에 여러 개의 요청이 들어올 수 있고, 스프링같은 멀티쓰레드 환경에서는 여러 쓰레드가 한 자원을 공유할 수 있어, 동시성 문제가 심할 때는 데드락이 발생할 수 있습니다. 저희 모락에서는 쿼리 성능을 위해 반정규화를 하여 업데이트를 하는 쿼리가 생겼고, 여러 쓰레드에서 한 레코드에 대해 공유락을 획득한 뒤 배타락 획득을 시도하여 데드락이 발생하였습니다. 이에 이전 게시물에서는 데드락 문제를 해결하기 위해 비관적 락, 낙관적 락 등에 대한 고민과 결론적으로 비즈니스 로직을 나눠 데드락을 회피하면서 문제를 해결하였습니다. 하지만 이렇게 비즈니스 로직으로도 완전하지 않을 수 있습니다. 지난 로직은 단순 데이터를 삽입하는 로직이었지만 선착순 투표와 같이 자원에 제한이 있을 때에는 정합성 문제가 여전히 발생할 수 있습니다. 그래서 동시성 문제를 조금 다른 방법으로 접근해보려고 합니다.
비즈니스 코드
선착순으로 약속잡기 선택을 10명만 할 수 있는 로직입니다.
위와 같은 코드에서 중요한 비즈니스 로직은 다음과 같습니다.
•
선착순 수(limitCount)를 보고 선착순 수가 다 찼는지 확인한다(isLimit()).
•
선착순 수가 아직 다 차지 않았다면 약속잡기 선택 인원을 추가한다(addAll()).
•
선택 인원을 +1한다(updateSelectedCount()).
이러한 로직에서 만약 100명의 멤버가 동시에 선택잡기를 요청한다면 결과가 어떻게 되는지 확인해보겠습니다.
테스트 코드
분산락으로 동시성 문제 해결하기(Redis, 네임드 락)
2023/03/23
Redis
Spring
Transaction
Aop
MySQL
목차
데드락이 발생한 이유
저희 서비스에서는 투표/약속잡기 목록 조회시 투표 또는 약속잡기를 마친 인원의 수를 표시하는 기능이 있습니다. 기존에는 연관 관계로 매핑된 결과 테이블에서 해당 투표 또는 약속잡기의 id 로 조회하고, 그 결과의 수를 세는 로직이었습니다. 하지만 데이터가 쌓이면 쌓일수록 조회 성능이 저하가 되는 단점을 발견했고, 이를 해결하기 위해 투표/약속잡기를 마친 인원의 수를 update 하는 selected_count 칼럼을 추가하는 반정규화를 진행하였습니다. 반정규화를 한 투표/약속잡기 진행 시의 로직은 다음과 같습니다.
1.
투표/약속잡기 조회
2.
투표/약속잡기 선택 결과 레코드 삽입
3.
투표/약속잡기 selected_count + 1 수정
처음 이런 프로세스를 구성할 당시에는 이 과정에서 동시성과 관련된 문제가 발생할 수 있다는 사실을 인지하지 못하고 넘어갔습니다. 어떤 동시성 문제가 발생할까요?
반정규화 하면서 생겨날 수 있는 데드락 문제
저희 로직은 투표/약속잡기 진행 시 해당 멤버가 아직 진행하지 않은 멤버라면 selected_count 를 +1 합니다. 그러면 JPA 는 트랜잭션 커밋 종료 시점 또는 영속성 컨텍스트의 flush가 일어나는 시점에 엔티티의 스냅샷을 비교하는 더티 체킹을 하고 변경된 컬럼이 있으면 변경된 엔티티에 대해 update 쿼리를 실행합니다. 직접 쿼리를 작성하지 않고 JPA 기능을 활용하여 최대한 도메인 객체 내부에 비즈니스 로직을 만들어 응집도를 높일 수 있도록 하였습니다.
문제가 발생한 지점
위의 1, 2, 3 로직은 하나의 트랜잭션 내에서 순차적으로 진행됩니다. 그리고 2번 로직에서 선택 결과를 삽입할 때에는, 주 테이블의 외래키를 함께 저장합니다. 이 부분으로 기인하여 동시성 문제가 발생하였습니다. 저희가 사용하고 있는 MySQL 의 Inno DB 의 특성상, 연관관계에 있는 테이블에 레코드 삽입 시 연관관계에 있는 레코드에 공유락을 잡습니다.
데드락 원인과 해결방안 고민(비관적 락, 낙관적 락)
2023/02/03
Spring
Transaction
MySQL
JPA
목차
배경
지난 포스팅에서는 상호 객체간 의존도를 낮추고, 비즈니스 로직을 도메인 객체로 넣어 응집도를 높이는 도메인 재설계를 진행하고 적용해 보았습니다. 이러한 과정에서 비즈니스 로직이 서비스 계층의 레포지토리 메서드에서 도메인 객체 내부로 이동하게 되었고, 대부분의 쿼리를 JPA 기능에 맡기게 되었습니다. 이로 인해 과거에 쿼리 튜닝을 진행했던 쿼리들이 사라지게 되었고, JPA 가 자동으로 만들어주는 새로운 쿼리의 성능을 확인해보고자 하였습니다.
테스트 환경
테스트할 도메인은 재설계를 진행했던 투표 도메인으로 테스트를 진행하였고, 더미 데이터는 쿼리 튜닝을 했던 때와 같은 수로 넣어주었습니다.
•
멤버: 500
•
팀: 1000
•
팀 멤버: 2996
•
투표: 30000
•
투표 항목: 90000
다사다난 도메인 재설계 - 심화편
2022/12/20
Spring
MySQL
Refactoring
목차
배경
모락 서비스를 배포한 이후, 사용자의 피드백과 추가 기능들을 함께 반영하며 운영 서버로 자주 배포하게 되었습니다. 이 과정에서 기존 버전의 운영 서버를 종료시키고, 새로운 버전의 운영 서버를 실행시키며 약 20초 정도의 다운타임이 발생하게 되었습니다. 이 20초 간의 다운타임도 문제였지만, 개발 서버에서 미처 알아차리지 못한 에러가 발생할 때도 문제가 있었습니다. 저희 프로젝트에서 배포하는 방식은 기존 버전의 jar 파일을 새로운 버전의 jar 파일로 오버라이드하고 새로운 버전의 jar 파일을 실행하는 방식이었습니다. 이러한 방식으로 인해 새로운 버전에서 에러가 발생한다면 이전 버전의 jar 파일이 없어, 롤백하기 위해서는 이전 버전의 jar 파일로 다시 오버라이드하여 서버를 실행해야 했습니다. 이와 같은 이유로 한번 에러가 발생하면 다운타임이 길어져서비스 운영에 치명적으로 다가오게 됩니다. 이에 따라 안정적인 서비스 운영을 위해 다운타임을 최소화시키고, 빠르게 롤백할 수 있는 무중단 배포 방식 을 적용하고자 하였습니다. 또한 서버가 예기치 못하게 종료되었을 때를 대비하고 트래픽 분산을 위해 로드 밸런싱 환경도 함께 적용해 보겠습니다.
무중단 배포 전략
무중단 배포 전략에는 롤링 방식, 블루/그린 방식, 카나리아 방식 등 크게 세 가지가 있습니다. 각 방식에 대한 세부적인 내용은 후디의 무중단 배포 아키텍처와 배포 전략 포스팅에 자세하게 나와있기에 이 포스팅에서는 생략하겠습니다. 저희 모락 프로젝트에서 선택한 무중단 배포 전략은 블루/그린 방식입니다. 이유는 다음과 같습니다.
•
롤링, 카나리아 방식은 호환성의 문제가 있을 것이라 판단
•
블루/그린 방식은 롤백이 쉬움
물론 블루/그린 배포 방식은 리소스가 2배로 들어간다는 단점이 있습니다. 하지만 추가적으로 사용할 인스턴스는 t4g micro 이기에 비용이 상대적으로 적게 소모되고, 하나의 인스턴스를 더 사용하는 비용보다 블루/그린 방식을 사용하면서 얻을 이점이 더 높다고 판단하였습니다.
로드 밸런싱
무중단 배포 환경을 적용하면서, 안정적인 서비스 제공을 위해 로드 밸런싱 환경도 함께 구축해 보았습니다. 로드 밸런싱 환경은 서버가 처리해야 할 요청을 여러 대의 서버로 나누어 처리할 수 있는 환경을 의미합니다. 로드 밸런싱은 트래픽을 나누어 처리하기때문에 하나의 서버가 처리하는 부하를 줄일 수 있고, 만일의 상황에 하나의 서버가 예상치 못하게 다운되었을 때, 남은 서버로 서비스를 지속적으로 이용할 수 있도록 대비할 수 있는 환경을 만드는 이점도 있습니다.
인프라 구조
저희 프로젝트에서는 위와 같이 블루/그린 무중단 배포 방식과 로드 밸런싱을 모두 적용시키기로 하였고, 이러한 환경을 단순히 적용하면 총 4개의 인스턴스를 사용해야 합니다.
무중단 배포 with jenkins
2022/11/04
infra
linux
jenkins
Personal Learning
개인적으로 학습한 내용을 기록한 공간입니다.
︎ Backend
JPA
Spring Core
JDBC
WebFlux
Infra
Basic
Tomcat
Netty
Spring MVC
Java
Query DSL
Kotlin
Books
학습한 도서의 내용을 요약한 공간입니다.
Studies & Presentations
스터디 및 영상 발표 내용입니다.
Effective Java
HTTP & Network
Spring 5 입문
재민님 영상 정리
CS
CS 지식 공부 내용입니다.
ETC
깃, IntelliJ 등 툴 관련 팁 공간입니다.