인프런 커뮤니티 질문&답변

Sanghoon Lee님의 프로필 이미지

작성한 질문수

실습으로 배우는 선착순 이벤트 시스템

Consumer 사용하기

쿠폰 테이블 조회 시 user_id 값

해결된 질문

24.06.15 11:31 작성

·

145

·

수정됨

0

db에 등록도 잘 되었는지 확인해 보기 위해서

coupon 테이블 조회 해보았는데

[coupon_id], [user_id]

테스트 로직에서 userId 값은 단순히 for을 통해 0부터 순차적으로 받은 뒤
쿠폰 개수에 맞춰 userId 도 0~99 사이의 값이 들어 올 것이라 생각했는데

예상과 다르게 103 이라는 id가 들어왔습니다
이런 이유가 궁금합니다

답변 1

0

최상용님의 프로필 이미지
최상용
지식공유자

2024. 06. 16. 19:32

Sanghoon Lee 님 테스트 코드와 로직을 어떻게 작성하셨는지 공유해주실 수 있으실까요 ?

Sanghoon Lee님의 프로필 이미지
Sanghoon Lee
질문자

2024. 06. 17. 20:56

테스트 코드 입니다

@Test
public void 여러명응모() throws InterruptedException {
    int threadCount = 1000;
    ExecutorService executorService = Executors.newFixedThreadPool(32);
    CountDownLatch latch = new CountDownLatch(threadCount);

    for (int i = 0; i < threadCount; i++) {
        long userId = i;
        executorService.submit(() -> {
            try {
                applyService.apply(userId);
            } finally {
                latch.countDown();;
            }
        });
    }
    latch.await();

    Thread.sleep(10000);

    long count = couponRepositroy.count();

    assertThat(count).isEqualTo(100);
}

 

ApplyService

@Service
public class ApplyService {

    private final CouponRepositroy couponRepositroy;

    private final CouponCountRepository couponCountRepository;

    private final CouponCreateProducer couponCreateProducer;

    private final AppliedUserRepository appliedUserRepository;

    public ApplyService(CouponRepositroy couponRepositroy, CouponCountRepository couponCountRepository, CouponCreateProducer couponCreateProducer, AppliedUserRepository appliedUserRepository) {
        this.couponRepositroy = couponRepositroy;
        this.couponCountRepository = couponCountRepository;
        this.couponCreateProducer = couponCreateProducer;
        this.appliedUserRepository = appliedUserRepository;
    }

    public void apply(Long userId) {
        Long apply = appliedUserRepository.add(userId);

        if (apply != 1) {
            return;
        }

        // lock start
        Long count = couponCountRepository.increment();

        if (count > 100) {
            return;
        }

        couponCreateProducer.create(userId);
        // lock end
    }
}

 

AppliedUserRepository

@Repository
public class AppliedUserRepository {

    private final RedisTemplate<String, String> redisTemplate;

    public AppliedUserRepository(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public Long add(Long userId) {
        return redisTemplate
                .opsForSet()
                .add("applied_user", userId.toString());
    }
}
최상용님의 프로필 이미지
최상용
지식공유자

2024. 06. 23. 20:35

Sanghoon Lee 님 안녕하세요.
답변이 늦어져서 죄송하다는 말씀 먼저 드립니다.

여러개의 스레드풀을 이용하여 병렬적으로 실행하면 실행하는 순서가 보장되지 않습니다.
예를들어 1~100 번의 id 를 가진 유저가 먼저 요청하지만 먼저 실행이 된다는 보장은 없습니다.

해당 내용에 대해 궁금하시다면 멀티스레드에 대해 공부해보시면 좋을것 같습니다!