본문 바로가기

분류 전체보기

(234)
방문자수 동시성 문제 제어 방식 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..
카프카 오프셋 커밋과 메시지 손실, DLT 프로젝트에서 카프카를 사용 중인데 처음 사용해보는거라 아직 모르는 부분이 많다. 카프카를 사용해보며 들었던 의문들을 정리해보고자 한다.자동 커밋 vs 수동 커밋카프카의 자동 커밋(auto commit) 기능을 사용하면, 메시지를 소비한 후 일정 주기마다 오프셋이 자동으로 커밋된다. 이 과정에서 메시지를 가져온 후 처리하는 과정에 예외가 발생하더라도, 이미 오프셋이 커밋되었기 때문에 해당 메시지를 다시 소비할 수 없는 문제가 발생한다. 예제를 통해 살펴보자. 다음은 카프카 설저어 코드와 리스너 코드이다.@EnableKafka@Configurationpublic class KafkaConsumerConfig { @Bean public ConsumerFactory consumerFactory() {..
Redis Stream을 메시지 브로커로 사용하기 0. 개요이 글은 프로젝트를 진행하며 메시지 브로커로 Redis Stream을 활용한 경험을 작성한 글이다. 메인 서버(가챠 서버)에서 사용자가 특정 등급의 아이템을 모두 획득한 경우, 로또를 발급해준다. 이때 로또 발급 로직을 별도의 프로세스로 진행하고자 로또 서버를 생성했다. 그리고 메시지 브로커로 두 서버 간 통신을 이뤄 비동기 통신을 구현하고자 했다. Kafka, RabbitMQ 등의 메시지 브로커가 있는데, 기존에 Redis를 사용 중이었기 때문에 Redis를 활용했다. Redis의 pub/sub과 stream을 메시지 브로커로 사용할 수 있다.1. Redis Stream일반적으로 레디스를 이용해 메시지를 발행할 때는 pub/sub을 많이 사용한다. 하지만 이 방식은 메시지를 발행했을 때 수신자..
구매 요청 동시성 제어하기 가챠가챠 프로젝트는 스프링 부트 서버와 MySQL만 사용 중이다. 따라서 프로젝트에서 마주했던 동시성 문제를 레디스나 다른 도구 없이 해결한 과정을 기록하고자 해당 글을 쓴다.0. 문제 상황사용자는 마켓에서 아이템을 구매할 수 있다. 이때 아이템 A를 판매하는 판매자가 n명이라면, 아이템 A의 수량은 n개가 된다. 구매자가 구매를 요청하면, 해당 아이템을 가장 처음에 등록한 판매자의 상품을 구매하게 된다. 예를 들어, 아이템 수량이 1개, 즉 판매자가 1명이고, 이를 구매하려는 구매자가 5명이라고 가정하자. user 테이블에는 다음과 같이 구매자 5명(user1~5), 판매자 1명(seller)가 저장되어 있다. 그리고 아직 거래가 이뤄지기 전이므로 6명의 coin 값은 초기 값인 20,000이다. 그리..
MDC(Mapped Diagnostics Context)를 사용하여 로그 추적하기 로깅이란로깅이란, 시스템이 동작할 때 시스템의 상태 및 동작 정보를 기록하는 것을 의미한다. 로깅을 통해 개발자는 개발 과정 또는 개발 후에 발생할 수 있는 예상치 못한 애플리케이션의 문제를 진단할 수 있고, 다양한 정보를 수집할 수 있다. 사용자 로그의 경우 분석 데이터로 활용할 수도 있다. 하지만 적절한 수준의 로그 기록 기준을 잡지 못하면 방대한 양의 로그 파일이 생성되는 문제를 겪거나, 의미 있는 로그를 쌓지 못하는 경우가 발생할 수 있다. 결국 효율적으로 로깅하는 방법을 이해하는 것이 중요하다.로깅 라이브러리로깅 관련 프레임워크는 대표적으로 log4j, logback, log4j2가 있고, 이들으르 통합해서 인터페이스로 제공하는 slf4j 라이브러리가 있다. 현재 log4j는 deprecated..
[백준] 5567. 결혼식 https://www.acmicpc.net/problem/55671. BFS로 푸는 방법import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.*;public class Main { static BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); static StringTokenizer st; public static void main(String[] args) throws IOException { int n = Integer.parseInt(br.readLine..