금액 도메인 → Pessimistic Lock

부분 환불에서는 동시 요청으로 인한 초과 환불이 절대 발생하면 안 됩니다.

@Transactional(isolation = Isolation.REPEATABLE_READ)
public Refund refund(Long paymentId, BigDecimal amount, String idempotencyKey) {
    // SELECT FOR UPDATE — 다른 트랜잭션이 같은 결제를 수정하지 못하게 잠금
    Payment payment = loadPaymentPort.loadForUpdate(paymentId);
    
    if (payment.getRefundableAmount().compareTo(amount) < 0) {
        throw new RefundExceedsPaymentException();
    }
    // ...
}

재고 도메인 → Optimistic Lock

SKU 재고 차감은 짧은 트랜잭션 + 높은 동시성이라 재시도가 효율적입니다.

// @Version 기반 Optimistic Lock + 최대 5회 재시도
for (int attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
    try {
        txTemplate.execute(status -> {
            ProductVariant v = loadPort.loadById(variantId).orElseThrow();
            v.decreaseStock(quantity);  // stockQuantity < quantity면 예외
            savePort.save(v);           // UPDATE WHERE version = N
            return null;
        });
        return; // 성공
    } catch (OptimisticLockException e) {
        Thread.sleep(10L << (attempt - 1)); // 지수 백오프
    }
}

선택 기준

기준 Pessimistic Optimistic
충돌 빈도 높음 낮음
트랜잭션 길이 길어도 OK 짧아야 효과적
실패 비용 높음 (금액) 낮음 (재시도)
처리량 제한적 높음

핵심: 도메인 특성에 따라 선택하되, 100스레드 동시성 테스트로 검증해야 합니다.