@Override
@Transactional
public synchronized void pay(Long userId, Long itemId) {
SalesItemEntity item = entityManager.find(SalesItemEntity.class, itemId);
if (item.getStock() < 1) {
throw new AlcoholException(ErrorCode.NO_SUCH_ITEM, String.format("%s님의 주문이 실패했습니다. 재고가 부족합니다.", userId));
}
item.setStock(item.getStock()-1);
entityManager.merge(item);
}
평균시간 : 35~38초
API 호출 : item used: {1: 1027, 2: 984, 3: 1017, 4: 1002, 5: 996, 6: 998, 7: 969, 8: 1005, 9: 1010, 10: 992}
재고 수
생각보다 동시성 이 잘 지켜지진 않는 모습
SalesItemEntity item = entityManager.find(SalesItemEntity.class, itemId, LockModeType.PESSIMISTIC_WRITE);
if (item.getStock() < 1) {
throw new AlcoholException(ErrorCode.NO_SUCH_ITEM, String.format("%s님의 주문이 실패했습니다. 재고가 부족합니다.", userId));
}
item.setStock(item.getStock()-1);
entityManager.merge(item);
를 사용한다.
평균시간 : 30~38초
에러 수: 115개
사용 item: {1: 1000, 2: 1000, 3: 1000, 4: 1000, 5: 989, 6: 957, 7: 972, 8: 1000, 9: 1000, 10: 967}
동시성 보장이 잘 된 것을 볼 수 있다. 사실 성능 차이는 크게 없어 보인다.
사실 낙관적인 상황이 아니라(데이터가 반드시 충돌하는 상황) 낙관적인 락이 어울리진 않는다.
// entity에 버전을 추가해준다.
@Version
private int version;
평균시간: 35~45초
비관적인 락보다 시간 효율이 높게 예상했지만, 충돌이 자주 발생하면서 더 많은 시간이 걸린 듯하다.
에러 수: 다양한데 이번 케이스에선 3499번
호출한 상품 수 : {1: 699, 2: 631, 3: 651, 4: 661, 5: 631, 6: 653, 7: 655, 8: 605, 9: 647, 10: 668}
그 와중에 동시성은 잘 지켜졌다.
별도의 동시성 처리를 하지 않고, Redis만 써봤다.
평균시간 : 25초대
에러수 : 0
호출한 상품수 : {1: 1004, 2: 1001, 3: 956, 4: 975, 5: 1019, 6: 1005, 7: 999, 8: 1010, 9: 1015, 10: 1016}
별도의 처리를 하지 않고는 동시성이 지켜지진 않는다. Redis 자체의 성능이 RDBMS보단 뛰어나기 때문에 비교적 많은 요청을 잘 처리한 것 같다.