본문 바로가기

분류 전체보기

(238)
가상스레드 가상 스레드는 자바 동시성 도구에 추가된 획기적인 기능이며, 개발자가 동시성 프로그램을 작성하는 방식을 근본적으로 바꾸고 있다. 덕분에 스레드를 굉장히 큰 규모의 동시성을 다루는 데 필수적인 기본 단위로 사용하는 것이 현실적으로 가능해졌다. 가상 스레드는 플랫폼 스레드와 다르게 JVM에 의해 관리된다. 운영체제가 관리하는 스레드에서 JVM이 관리하는 스레드로의 전환은 단순히 구현 세부 내용만 달라진 것이 아니다. 애플리케이션의 메모리 고갈이나 성능 저하 없이 수백만 개의 스레드를 생성해서 사용하는 방식은 전통적인 플랫폼 스레드에서는 아예 불가능했지만 가상 스레드를 사용하면 가능하며, 덕분에 훨씬 더 편리하게 동시성 프로그램을 작성할 수 있게 됐다. 가상 스레드는 캐리어 스레드(carrier threa..
false sharing 문제 스레드를 무분별하게 생성하는 위험을 피하려면 자바 동시성 프레임워크에서 제공하는 ExecutorService 같은 기능을 사용하는 것이 좋다. ExecutorService는 더 구조화된 방식으로 스레드를 사용할 수 있게 해준다. ExecutorService는 단지 동시에 실행되는 스레드의 수를 제어하는 데 그치지 않고 스레드의 생애주기를 관리해서 스레드를 효율적으로 재사용 할 수 있게 해준다. 그러나 이그제큐터 프레임워크의 한계 중 하나로, 캐시 일관성 유지 부하와 거짓 공유 위험이 있다. 스레드 풀에 제출된 태스크는 메인 스레드 말고 서로 다른 CPU 코어에서 실행되는 다른 스레드에서 실행된다. 코어마다 캐시 상태가 달라질 수 있고 그 결과 캐시 일관성 프로토콜(cache coherence protoc..
No Offset, DTO 조회, 복합 인덱스를 통한 페이징 조회 API 성능 개선 1. 기존에 Pageable을 사용하던 방식둘러보기 API는 미니홈 리스트를 각종 정렬 조건에 따라 반환하는 API이다. 기존에는 다음과 같이 Pageable 객체를 통해 간편하게 미니홈 리스트를 데이터베이스에서 조회해왔다.@Repository@RequiredArgsConstructorpublic class MinihomeEntityRepository implements MinihomeRepository { private final MinihomeJpaRepository minihomeJpaRepository; private final MinihomeMetaJpaRepository minihomeMetaJpaRepository; @Override public Slice fin..
구매 요청 동시성 제어하기 1. 문제 상황사용자는 마켓에서 아이템을 구매할 수 있다. 이때 아이템 A를 판매하는 판매자가 n명이라면, 아이템 A의 수량은 n개가 된다. 구매자가 구매를 요청하면, 해당 아이템을 가장 처음에 등록한 판매자의 상품을 구매하게 된다. 그런데 멀티 스레드 환경에서 구매 요청이 동시에 올 경우, 재고 수량 이상의 사용자가 구매에 성공하는 동시성 문제가 발생한다.2. 해결 방안2-1. JPA 낙관적 락JPA의 버전 관리를 통한 방법이다. 다음과 같이 Trade 엔티티에 버전 관리 필드를 추가해주고, JPA 데이터 조회 메서드에 '@Lock(value = LockModeType.OPTIMISTIC)' 를 붙여주면 된다. 그러나 낙관적 락은 현재 상황에 적합하지 않다. 그 이유는 공정하지 않기 때문이다. 예를 들..
목록 페이징 조회 시 좋아요 수를 보여주는 구현 전략 1. 문제 상황미니홈 목록 조회 시 각 미니홈의 좋아요 수를 함께 보여줘야 한다. 따라서 미니홈 테이블과 좋아요 테이블 간 조인이 발생한다. 또한 좋아요 순으로 정렬 조회할 경우, 미니홈 테이블과 좋아요 테이블의 모든 데이터에 대해 조인, 그룹화, 정렬이 발생한다. 다음 코드는 좋아요 순 정렬 조회 코드이다.@Repository@RequiredArgsConstructorpublic class MinihomeEntityRepository implements MinihomeRepository { private final MinihomeJpaRepository minihomeJpaRepository; private final LikeJpaRepository likeJpaRepository; @..
방문자수 동시성 문제 제어 방식 1. 문제 상황특정 사용자의 미니홈에 방문하면 방문자 수가 1 증가한다. 그러나 멀티 스레드 환경에서 방문자 수 필드에 대한 동시성 문제가 발생한다. JMeter를 통해 20명의 사용자가 동시에 user_id=1인 사용자의 미니홈에 방문하는 상황을 테스트해보자. 테스트 결과, 총 방문자 수 필드가 20이 아닌 7이 된 것을 확인했다. 애플리케이션 코드를 보면, 동시에 여러 스레드가 미니홈 데이터를 조회한 후, 동시에 +1을 하기 때문에 race condition이 발생한 것을 알 수 있다. @Transactional public Minihome visitMinihome(User minihomeUser) { Minihome minihome = minihomeRepository.fi..
SSE 사용 시 데이터베이스 커넥션 고갈 문제 문제 상황개발 서버에서 갑자기 데이터베이스 커넥션 획득 실패로 인한 'Connection is not available, request timed out after 30000ms' 에러가 엄청 발생했다. 처음에는 커넥션을 오래 잡고있는 트랜잭션이 있는지 찾아봤는데, 원인을 알 수 없었다. 그런데 원래 해당 오류가 발생하지 않다가 프론트 측에서 sse를 구현하며 sse 연결 요청을 보낸 후부터 해당 문제가 발생한 것이다. 따라서 sse와 연관지어 생각해봤고, 검색해보니 sse 연결과 osiv 간 관계로 인해 발생한 문제였다. osiv 옵션을 설정하지 않으면 default는 true이다. 즉, 요청과 응답 내내 영속성 컨텍스트가 살아있으며 커넥션을 점유한다. 이때 SSE 연결을 맺으면 sse 연결이 만료될 ..
복권 발급시 성능과 데이터 일관성을 모두 지키기 위한 방법 기존 설계의 문제아이템 획득시 특정 등급의 아이템을 모두 획득하면 복권이 발급된다. 따라서 아이템 획득시 복권 발급 조건을 충족했는지 확인한 후, 충족할 경우 Redis stream 메시지를 발행한다.@RestController@RequiredArgsConstructorpublic class GachaController { @PostMapping("/gacha") public ApiResponse gacha(HttpServletRequest request) { User user = userService.readUserById(jwtUtils.getUserIdFromHeader(request)); Item addedItem = itemService.gacha(us..