멀티 쓰레드 순서 보장 SortedSet

문정현·2024년 2월 1일
0

기나긴 핫딜 구현이 끝났다.. 정리할 내용이 많은것..

1. 구매 순서 보장 불가능


아마 동시성 처리기 2 포스트에서 언급했던 내용으로 쓰레드가 구매 요청을 시도한 순서대로 구매가 이루어지지 않고 있는 것을 제어하고자 시작했던 여정이였다

2. SortedSet 도입

이를 해결하기 위해 도입한 것이 바로 Redis SortedSet 핫딜이라는 event Key에 유저이메일을 value : 요청 시간의 System.currentTimeMillis을 score값으로 순서대로 정렬한다

    @Override
    @Transactional
    public HotdealWaitResponse waitHotdeal(String hotdealId, Member member) {

        Hotdeal hotdeal = findHotdeal(Long.parseLong(hotdealId));
        int waitCount = redisUtil.getQueueSize(hotdealId);

        if (hotdeal.getDealQuantity() < waitCount * 10) { // 발급 전에 재고 확인후 대기열 추가
            return new HotdealWaitResponse("현재 구매 요청이 많아 대기열에 추가할 수 없습니다", waitCount);
        }

        redisUtil.addPurchaseHotdealMemberToQueueString(hotdealId, member.getEmail(),
            System.currentTimeMillis());

        return new HotdealWaitResponse("success", waitCount + 1);
    }
    
    public void addPurchaseHotdealMemberToQueueString(String queueName, String memberName, double score) {
        ZSetOperations<String, String> zSetOperations = stringRedisTemplate.opsForZSet();
        zSetOperations.add(queueName, memberName, score);
    }

처음에는 purchase 로직에 다 우겨 넣으려고 했으나 메서드를 분리하여 대기열에 담는 메서드, 바로 밑에 언급할 대기열에서 확인하는 메서드, 그리고 핫딜 구매 메서드를 떼어내어 역할을 명확히 하고자 했다.


이제 score를 기준으로 정렬되어 지금은 10개 씩 꺼내서 지금 내 차례가 왔는지 확인 하고 구매 로직을 타고 갈 것이다

    @GetMapping("/hotdeals/{hotdealId}/isMyTurn")
    public ResponseEntity<?> isMyTurn(
        @AuthenticationPrincipal MemberDetailsImpl memberDetails,
        @PathVariable Long hotdealId
    ) {
        boolean isTurn = hotdealService.isMyHotdealTurn(memberDetails.getMember(),
            hotdealId.toString());

        if (isTurn) {
            return ResponseEntity.ok(true);
        } else {
            // 상태 코드 202과 함께 false 반환. 다른 상태 코드를 선택할 수도 있음.
            return ResponseEntity.status(HttpStatus.ACCEPTED).body(false);
        }
    }

차례가 왔는지 확인은 client단에서 구매 대기 유저의 차례가 왔는지 polling 방식으로 지속적으로 요청을 날려 statusCode가 200에 true가 왔을때 처리하여 최종적으로 핫딜 구매가 이루어 지는 것 이다.

왜 굳이 polling 방식으로 계속 요청을 해서 부담을 주는가? 라는 의문점 또한 들 수 있다. 따라서 V3에서는 대기열에 담고 eventListener를 통해 n초마다 스케쥴러를 돌려 set안에 구매자들의 구매 대기를 허가해주는 방식으로 추가 구현하였다

profile
주니어 개발자를 꿈꾸며

0개의 댓글