쿠폰시스템 개발을 위한 repository 입니다.
docker-compose up -ddocker-compose up -d --scale worker=3
동시성 이슈 발생
- 쿠폰 갯수는 정상적으로 소진되었지만 더많은 쿠폰을 발급함
- 쿠폰 한정수량 : 500
- 발급된 쿠폰 : 505
검증
- 쿠폰 존재 검증
- 쿠폰 발급 유효 기간 검증
- 쿠폰 발급 수량 검증
- 중복 발급 검증
발급
- 쿠폰 발급 수량 증가
- 쿠폰 발급 내역 기록
예상 시나리오
- 유저 요청이 들어온다. (coupon_id, user_id)
- 쿠폰 캐시를 통한 유효성 검증
- 쿠폰의 존재
- 쿠폰의 유효기간
- Sorted Set에 요청을 추가 (ZADD score = time stamp)
- ZADD의 응답 값 기반 발급 검증
- 현재 요청의 순서 조회 (ZRANK) 및 발급 성공 여부 응답
- 발급에 성공했다면 쿠폰 발급 Queue에 적재
Problem
-
score에 timestamp는 동시 요청에 대한 순서를 보장할 수 있을까?
-> score를 관리해야할 필요가 없음
-
발행 수 가 소진 되면 요청을 막을 건데 굳이 sorted set을 사용하는게 의미가 있을까?
-
시간 복잡도도 O(logn) 이므로 Set을 사용하는게 좋을것 같다.
- Lock 을 사용했을때 RPS : 약 1000
- Lock을 사용하지 않았을때 RPS: 약 7000 (동시성 보장 - 중복 발급 가능성 존재)
-> 약 7배 이상 차이 발생
-
Redis 는 싱글스레드 이기 떄문에 스크립트로 일련의 로직 한번에 처리
private RedisScript<String> issueRequestScript() { String script = """ if redis.call('SISMEMBER', KEYS[1], ARGV[1]) == 1 then return '2' end if tonumber(ARGV[2]) > redis.call('SCARD', KEYS[1]) then redis.call('SADD', KEYS[1], ARGV[1]) redis.call('RPUSH', KEYS[2], ARGV[3]) return '1' end return '3' """; return RedisScript.of(script, String.class); }
- 동시성을 보장하면서 빠른 RPS 보장 가능