'댕댕어디가' 서비스에는 선착순으로 티켓을 발급받을 수 있는 기능이 있습니다. (ex. 펫 페스티벌 입장권)
1.이벤트 시스템 구현
유저의 티켓 발급 요청을 받으면, 애플리케이션에서 유저의 아이디를 추출해 Redis에 저장합니다.
이때, Redis에는 HashMap 자료형으로 {"유저ID" : "발급시간"} 값을 저장합니다. 기존 발급 가능 인원까지 저장한 후, 목표 인원이 모이면 마감되었다는 응답을 전달합니다.
이벤트가 마감되면 Redis에 저장된 유저의 정보와 발급시간을 활용해, 가장 빨리 발급한 순서대로, 가능 인원수만큼 DB에 저장하여 영속화하는 과정을 거칩니다.
Redis에 저장하는 이유
선착순 이벤트 기능은 동시에 트래픽이 가장 많이 몰리는 기능 중 하나입니다.
이때, 유저가 요청할 때마다 유저 정보를 DB에 저장하게 된다면 잦은 쓰기 작업 때문에 DB 과부하가 발생할 수 있습니다.
이를 방지하기 위해 읽기/쓰기가 빠른 Redis를 활용하여 임시로 유저 정보를 저장합니다.
Redis에는 여러 자료형이 있지만, 그 중에서 HashMap 자료형을 사용하였습니다. HashMap을 고르게 된 여러 고민은 다음과 같습니다.
1. 유저의 아이디와 발급 시간 , 2가지 정보를 어떻게 저장하지?
HashMap을 이용하면 키와 값을 활용하여 2가지 정보를 저장할 수 있습니다.
2. Sorted Set을 활용하면 중복처리와 미리 순서를 정렬할 수 있지 않을까?
이벤트에서는 쓰기 작업의 시간을 최소한으로 줄이는 것이 중요한데, SortedSet은 저장할때마다 정렬이 필요하기 때문에 O(log N) 시간 복잡도로 저장됩니다. HashMap은 쓰기/읽기 작업의 시간복잡도가 O(1)라서, Sorted Set보다 빠르게 읽고 쓸 수 있습니다. 정렬하는 것은 이벤트가 마감된 이후에 정렬되어도 괜찮기 때문에 쓰기 작업이 빠른 HashMap을 이용하였습니다.
2. 부하 테스트
부하테스트 기준 및 목표
'2023년 한국 반려 동물 보고서'에 따르면 우리나라 반려동물을 기르는 반려가구는 552만 가구로, 1262만여명이 반려동물을 기르고 있다고 합니다.
보통 전체 타겟 시장의 5~15% 수준에서 초기 사용자를 목표로 잡는다는 점을 고려하여, '댕댕어디가' 서비스의 MAU를 5%인 60만명을 목표로 잡았습니다.
(참고로 유사 서비스 '포동'은 44만 유저를 보유, '핏펫'은 77만 유저 중 MAU 30만명을 유지, '펫프렌즈'는 MAU 32만명을 유지하고 있다고 합니다.)
일상적으로 서비스를 사용하는 유저가 이벤트에 참여한다고 가정한다면 WAU만큼 선착순이벤트에 응모한다고 목표로 잡을 수 있습니다. 이때, WAU와 MAU 비율이 1:4이기 때문에 WAU를 15만명으로 잡을 수 있습니다.
선착순 이벤트 시작 후 10분동안이 가장 많은 트래픽이 몰린다고 생각하기 때문에, 10분동안 부하테스트를 진행할 예정입니다.
선착순 이벤트 참여 수 중 2/3 유저가 시작 후 5분동안 참여하고, 나머지 1/3 유저가 5분 후 나머지 5분동안 참여한다는 시나리오로 부하테스트를 진행해 보겠습니다.
총 10분간 15만 트래픽을 부하테스트 진행할 예정이며 , 평균 응답속도를 0.5초 넘기지 않는 것이 목표입니다.
(시작 5분간 10만 트래픽, 이후 5분간 5만 트래픽)
이벤트 : 펫 페스티벌 100명 선착순 티켓 발급 이벤트
10분간 15만 선착순 이벤트 요청 부하테스트
vus를 늘려가며 약 15만 요청 테스트를 진행했습니다.
평균 응답시간은 17ms이고, failed는 99%이지만 실제 응답 성공률은 100%입니다.
(선착순 이벤트가 마감되었을 때, 400에러를 전달하기 때문에 failed가 99%이지만, 응답값이 200 또는 400인 경우를 계산하는 checks 값이 100%이기 때문에 , 실패한 요청은 없습니다.)
10분간 15만 선착순 이벤트 요청 + 기존 API 요청 부하테스트
이벤트 기간 동안, 다른 요청들도 정상적으로 처리되어야 하며, 다른 요청들로 인해 이벤트 처리 속도가 늦어지면 안됩니다.
그렇기에 이를 감안하여 다른 API 요청이 와도, 선착순 이벤트 기능이 정상적인 속도 내에 처리되는지 확인해 봐야합니다.
많은 API들이 있지만, 가장 요청이 많을 API 4개만 추가해서 부하테스트를 진행해 봤습니다.
- 장소 검색 API
- 리뷰 목록 조회 API
- 장소 상세 조회 API
선착순 이벤트는 이전과 동일하게 15만 트래픽 요청을 보내며, 다른 API는 DAU를 고려해 10분동안 270vus가 요청한다고 가정하여 테스트를 진행했습니다.
테스트 결과 선착순 이벤트 응답 속도는 평균 75.5ms로 목표한 것에 비해 좋은 결과입니다.
그 외에도 다른 테스트도 성공률 100%, 응답 속도가 0.5초 미만입니다.
3. 동시성 문제 해결하기
동시성 문제 발견
하지만, 정확히 매번 300개의 티켓만 발급할까요?
몇번의 테스트 도중, 300개보다 더 많은 티켓을 발급한 경우를 확인할 수 있었습니다.
해당 과정은 다음 글에 정리하여 남겨뒀습니다.
https://gani-dev.tistory.com/156
[댕댕어디가] 선착순 이벤트 시스템 구현기 - 동시성 문제 해결
선착순 이벤트 시스템이란?댕댕어디가에는 펫 페스티벌 티켓을 선착순으로 받을 수 있는 이벤트가 존재합니다. 빠른 저장과 부하를 위해 이벤트 당시에는 티켓을 발급한 유저의 정보를 Redis에
gani-dev.tistory.com
'댕댕어디가 프로젝트 > 트러블슈팅' 카테고리의 다른 글
[댕댕어디가] 선착순 이벤트 시스템 구현기 - 동시성 문제 해결 (0) | 2025.02.09 |
---|---|
[댕댕어디가] 땅따먹기 주인 조회 API 속도 개선 (0) | 2025.02.06 |