주문 - ekdan38/HotDealService GitHub Wiki

테스트 환경

  • CPU : Intel i5-8250U 1.6GHz
  • RAM : 8GB
  • OS : Window10
  • Databse : MySQL 8.0
  • Test Tool : K6

주문 생성

테스트시나리오 

1~3개 종류의 상품을 랜덤 주문

성능 개선 전 k6 테스트

k6 테스트 결과
  • VU 100
    VU100 결과
  • VU 200
    VU200 결과
  • VU 300
    VU300 결과
주문 생성 (3분) 총 처리량 Latency(mean) Latency(P95) TPS
VU 100 6028 3.01s 3.74s 32
VU 200 5587 6.56s 7.86s 29
VU 300 5657 9.83s 18.12s 29

테스트 결과 분석

  • VU 증가에 따라 Latency 큰 증가
  • TPS 또한 감소하거나 VU200 부터 동일

VU 300에서 아래와 같은 오류가 나타남

Image

성능 개선을 하며 해당 오류의 발생 원인도 찾아볼 예정이다.

주문 흐름

성능 개선 계획에 앞서, 현재 주문 흐름을 정리

[OrderService]

  1. 상품 재고 점유 요청(HotDealService로 FeignClient 요청 -> 동기 처리)
    • -> 상품 존재 검증, 핫딜 활성 유무 검증, 재고 존재시 재고 예약 테이블에 재고 점유
  2. Order, OrderProduct, Delivery 생성
  3. Payment 생성 요청(PaymentService로 FeignClient 요청 -> 동기 처리)
  4. 응답 반환

HotDealService, PaymentService로 2번의 FeignClient 동기 통신이 이뤄지고 있음

HotDealService의 재고 점유API 성능 개선, PaymentService로 payment 생성은 비동기 처리 혹은, 방식을 변경

[HotDealService]

  1. 상품 조회 및 검증
  2. 핫딜 조회 및 활성화 검증
  3. 재고 점유

재고 점유 로그

2개 종류의 상품에 대한 주문을 요청했을때, 다음과 같은 쿼리 발생

Image

  • 2개의 상품 IN 절 조회 -> 쿼리 1번
  • 2개의 상품에 대한 핫딜 IN 절 조회 -> 쿼리 1번
  • 2개의 상품에 대한 재고 점유 조회 -> 쿼리 2번

상품, 핫딜 조회의 경우에는 IN 절을 통해 1번의 조회가 이루어지지만, 재고 점유 조회의 경우 각 상품마다 조회 쿼리 발생

성능 개선 계획 

  • [OrderService]
    • HotDealService의 재고 점유API 성능 개선
    • PaymentService로 payment 생성 과정을 비동기 처리 혹은, 방식을 변경
  • [HotDealService]
    • 상품 조회 
      • 조회 쿼리 분석 및 개선
      • 캐싱
    • 핫딜 조회
      • 조회 쿼리 분석 및 개선
      • 캐싱
    • 재고 점유
      • COUNT 쿼리 최적화

1. 성능 개선

1.1. 재고 점유

재고 점유 API 성능 개선에 앞서, 재고 점유 API 자체의 성능 테스트 진행

재고 점유 API 성능 개선 전 k6 테스트

k6 테스트 결과
  • VU 100
    VU100 결과
  • VU 200
    VU200 결과
재고 점유 조회 (3분) 총 처리량 Latency(mean) Latency(P95) TPS
VU 100 13278 1.35s 1.83s 73
VU 200 13203 2.74s 3.43s 72
  • VU 100 에서 처리량 한계

1.1.1. 조회 쿼리 최적화 및 인덱싱

1. 상품 조회

[조회 쿼리 분석]

JPA 코드 및 실제 실행 쿼리

List<HotDealProduct> findByIdIn(List<Long> hotDealProductIds);
SELECT hdp.product_id,hdp.created_at,hdp.hotdeal_id,hdp.modified_at,hdp.price,hdp.stock,hdp.title 
FROM hot_deal_product hdp 
WHERE hdp.product_id IN (99951,99981);

실행 계획

EXPLAIN SELECT hdp.product_id,hdp.created_at,hdp.hotdeal_id,hdp.modified_at,hdp.price,hdp.stock,hdp.title
        FROM hot_deal_product hdp
        WHERE hdp.product_id IN (99951,99981);

Image

  • WHERE 조건문이 PK컬럼이며, PK 클러스터링 인덱스로 효율적인 탐색 중

2. 핫딜 조회

[조회 쿼리 분석]

JPA 코드 및 실제 실행 쿼리

@Query("SELECT new com.hong.hotdealservice.dto.projection.HotDealSimpleDto" +
        "(h.id, h.title, h.description, h.status, h.startTime, h.endTime) " +
        "FROM HotDeal h " +
        "WHERE h.deleted = false " +
        "AND h.id IN :hotDealIds")
List<HotDealSimpleDto> findByIds(@Param("hotDealIds") List<Long> hotDealIds);
SELECT hd.hotdeal_id,hd.title,hd.description,hd.status,hd.start_time,hd.end_time
FROM hot_deal hd
WHERE hd.deleted=0
	AND hd.hotdeal_id IN (9996,9999);

실행계획

EXPLAIN SELECT hd.hotdeal_id,hd.title,hd.description,hd.status,hd.start_time,hd.end_time
        FROM hot_deal hd
        WHERE hd.deleted=0
          AND hd.hotdeal_id IN (9996,9999);

Image

  • PK 클러스터링 인덱스 사용
  • deleted 컬럼의 카디널리티는 2로 복합 인덱스 생성 불필요
  • 효율적인 탐색 중

3. 상품의 재고 점유 현황 조회

[조회 쿼리 분석]

JPA 코드 및 실제 실행 쿼리

    @Query("SELECT COALESCE(SUM(sr.reservedQuantity), 0) " +
            "FROM StockReservation sr " +
            "WHERE sr.productId = :productId " +
            "AND sr.status = :reserveStatus")
    Integer sumReservedQuantityByProductId(@Param("productId") Long productId,
                                           @Param("reserveStatus") ReserveStatus reserveStatus);
SELECT sr.product_id, coalesce(sum(sr.reserved_quantity), 0)
FROM stock_reservation sr
WHERE sr.product_id=99951
  AND sr.status = 'RESERVED';
  • 위에서 확인한 재고 점유 로그를 보면 해당 쿼리로는 N개의 상품 종류를 주문하면, N개의 쿼리가 발생
  • N개의 상품 종류를 주문해도, 1번의 조회 쿼리가 발생하도록 수정 필요

코드 수정

    @Query("SELECT new com.hong.hotdealservice.dto.projection.ProductReservedQuantityDto" +
            "(sr.productId, COALESCE(SUM(sr.reservedQuantity), 0L)) " +
            "FROM StockReservation sr " +
            "WHERE sr.productId IN :productIds " +
            "AND sr.status = :reserveStatus " +
            "GROUP BY sr.productId")
    List<ProductReservedQuantityDto> sumReservedQuantityByProductId(@Param("productIds") List<Long> productIds,
                                                                    @Param("reserveStatus") ReserveStatus reserveStatus);
SELECT sr.product_id, coalesce(sum(sr.reserved_quantity), 0)
FROM stock_reservation sr
WHERE sr.product_id IN (99951, 99991)
  AND sr.status = 'RESERVED'
GROUP BY sr.product_id;
  • IN + GROUP BY 를 사용하여 각 상품의 재고 점유 개수 처리
  • DtoProjection 적용 및 List로 return 하여 여러 상품에 대한 재고 점유 현황 조회

[수정 후 로그]

Image

N개의 상품 종류를 주문해도, 1번의 조회 쿼리가 발생하도록 수정 되었음

실행계획

EXPLAIN
SELECT sr.product_id, coalesce(sum(sr.reserved_quantity), 0)
FROM stock_reservation sr
WHERE sr.product_id IN (99951, 99991)
  AND sr.status = 'RESERVED'
GROUP BY sr.product_id;

Image

  • 현재 인덱스를 사용하고 있지 않음, 인덱스 생성 필요

인덱스 생성 전 소요 시간

Image

소요 시간 : 약 47ms

인덱스 생성

CREATE INDEX idx_reserved_product_stock
    ON stock_reservation (product_id, reserved_quantity);

Image

인덱스 생성 후 실행계획

Image

  • 커버링 인덱스를 사용하고 있음

인덱스 생성 후 소요 시간

Image

소요 시간 : 약 27ms

47ms -> 27ms로, 42% 개선

조회 성능 개선, 인덱스 적용 후 k6 테스트

k6 테스트 결과
  • VU 100
    VU100 결과
  • VU 200
    VU200 결과
재고 점유 조회 (3분) 총 처리량 Latency(mean) Latency(P95) TPS
VU 100 14156 1.27s 1.71s 78
VU 200 13891 2.6s 3.17s 76

테스트 결과 분석

Latency

  • Latency(mean)
    • VU 100: 1.35s → 1.27s (약 5.9% 개선)
    • VU 200: 2.74s → 2.60s (약 5.1% 개선)

Latency(mean) : 약 5% 개선

  • Latency(P95)
    • VU 100: 1.83s → 1.71s (약 6.6% 개선)
    • VU 200: 3.43s → 3.17s (약 7.6% 개선)

Latency(P95) : 약 7%* 개선

TPS

  • VU 100: 73 → 78 (약 6.8% 증가)
  • VU 200: 72 → 76 (약 5.6% 증가)

TPS : 약 6% 개선

  • 상품별 재고 점유 조회 쿼리를 N개의 상품일때, 1회 조회로 수정
  • 인덱스 생성을 통해 쿼리 실행 시간을 (47ms->27ms)로 약 42% 개선
  • TPS, Latency 모두 약 6% 의 개선을 보임

1.1.2. 캐싱

상품, 핫딜 조회에 캐싱을 적용하여 조회 성능 개선 시도

캐싱 적용 방식

  • 핫딜, 상품의 경우 조회 API 성능 개선에서 캐싱을 적용하였음
  • 해당 캐싱 데이터를 주문 처리 과정에서도 사용
[상품 조회 캐시 적용]
    private Map<Long, ProductCacheDto> getProductsAndValidate(List<Long> productIds){
        Map<Long, ProductCacheDto> productMap = new HashMap<>();
        List<Long> cacheMissProducts = new ArrayList<>();
        // 1. Redis 조회
        List<ProductCacheDto> cachedProducts = productRedisRepository.findAllByIds(productIds);
        // 2. cacheHit, Miss 정리
        for(int i = 0; i < productIds.size(); i++){
            ProductCacheDto cachedData = cachedProducts.get(i);
            // cacheHit
            if(cachedData != null){
                log.info("CacheMissHit, product = {}", productIds.get(i));
                productMap.put(productIds.get(i), cachedData);
            }
            // cacheMiss
            else {
                log.info("CacheMiss, product = {}", productIds.get(i));
                cacheMissProducts.add(productIds.get(i));
            }
        }

        // 3. cacheMiss 존재 하면 DB 조회 후 cache save
        if (!cacheMissProducts.isEmpty()){
            // DB 조회
            log.info("상품 재고 점유. [상품 조회 쿼리 실행] : productIds = {}", cacheMissProducts);
            List<HotDealProduct> foundProducts = hotDealProductRepository.findByIdIn(cacheMissProducts);
            log.info("상품 재고 점유. [상품 조회 쿼리 종료] : productIds = {}", cacheMissProducts);
            // DB 에 존재 하지 않는 product 예외 처리
            if(cacheMissProducts.size() != foundProducts.size()){
                List<Long> noneMatchIds = cacheMissProducts.stream()
                        .filter(id -> foundProducts.stream().noneMatch(hp -> hp.getId().equals(id)))
                        .toList();
                log.error("요청된 핫딜 상품이 존재하지 않습니다. productIds = {}", noneMatchIds);
                throw new HotDealProductException(ErrorCode.HOTDEAL_PRODUCT_NOT_FOUND, noneMatchIds);
            }

            // productMap 에 정리 (cacheHit, cacheMiss 데이터 관리)
            List<ProductCacheDto> productsToCache = new ArrayList<>();
            foundProducts.forEach(hp -> {
                ProductCacheDto productCacheDto = new ProductCacheDto(hp);
                productsToCache.add(productCacheDto);
                productMap.put(hp.getId(), productCacheDto);
            });
            // 4. Redis Save
            productRedisRepository.saveAllWithTTL(productsToCache);
        }
        return productMap;
    }
[핫딜 조회 캐싱 적용]
    private void getHotDealsAndValidateOrderAble(Map<Long, ProductCacheDto> productMap){
        Map<Long, HotDealCacheDto> hotDealMap = new HashMap<>();
        List<Long> cacheMissIds = new ArrayList<>();

        // 1. hotDealIds 추출
        List<Long> hotDealIds = productMap.values().stream()
                .map(ProductCacheDto::getHotDealId)
                .distinct()
                .sorted()
                .toList();

        // 2. Redis 조회
        List<HotDealCacheDto> cachedHotDeals = hotDealRedisRepository.findAllByIds(hotDealIds);

        // 3. cacheHit, Miss 정리
        for(int i = 0; i < hotDealIds.size(); i++){
            HotDealCacheDto hotDealCacheDto = cachedHotDeals.get(i);
            // cacheHit
            if(hotDealCacheDto != null){
                log.info("CacheMissHit, hotDeal = {}", hotDealIds.get(i));
                hotDealMap.put(hotDealIds.get(i), hotDealCacheDto);
            }
            // cacheMiss
            else {
                log.info("CacheMiss, hotDeal = {}", hotDealIds.get(i));
                cacheMissIds.add(hotDealIds.get(i));
            }
        }

        // 4. cacheMiss 존재 하면 DB 조회 후 cache save
        if (!cacheMissIds.isEmpty()){
            // DB 조회
            log.info("상품 재고 점유. [핫딜 조회 쿼리 실행] : hotDealIds = {}", cacheMissIds);
            List<HotDealSimpleDto> foundHotDeals = hotDealRepository.findByIds(cacheMissIds);
            log.info("상품 재고 점유. [핫딜 조회 쿼리 실행] : hotDealIds = {}", cacheMissIds);
            // DB 에 존재 하지 않는 product 예외 처리
            if(cacheMissIds.size() != foundHotDeals.size()){
                List<Long> noneMatchIds = cacheMissIds.stream()
                        .filter(id -> foundHotDeals.stream().noneMatch(h -> h.getId().equals(id)))
                        .toList();
                log.debug("핫딜이 존재하지 않습니다. hotDealId = {}", noneMatchIds);
                throw new HotDealException(ErrorCode.HOTDEAL_NOT_FOUND, noneMatchIds);
            }
            // hotDealMap 에 정리 (cacheHit, cacheMiss 데이터 관리)
            List<HotDealCacheDto> hotDealsToCache = new ArrayList<>();
            foundHotDeals.forEach(h -> {
                HotDealCacheDto hotDealCacheDto = new HotDealCacheDto(h);
                hotDealsToCache.add(hotDealCacheDto);
                hotDealMap.put(h.getId(), hotDealCacheDto);
            });
            // Redis Save
            hotDealRedisRepository.saveAllWithTTL(hotDealsToCache);
        }

        // 5. hotDeal 활성화 검증
        List<Long> noneActiveHotDealIds = new ArrayList<>();
        hotDealMap.values().forEach(h -> {
            if(h.getStatus() != HotDealStatus.ACTIVE) noneActiveHotDealIds.add(h.getId());
            LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MILLIS);
            if(!(now.isAfter(h.getStartTime()) && now.isBefore(h.getEndTime()))){
                noneActiveHotDealIds.add(h.getId());
            }
        });
        // 주문 가능 상태가 아닌 hotDeal 포함 되어 예외 처리
        if (!noneActiveHotDealIds.isEmpty()) {
            List<Long> sortedHotDealIds = noneActiveHotDealIds.stream().sorted().collect(toList());
            log.error("활성화 된 핫딜이 아닙니다. hotDealId = {}", sortedHotDealIds);
            throw new HotDealException(ErrorCode.HOTDEAL_NON_ACTIVE, sortedHotDealIds);
        }
    }

캐싱 적용 후 k6 테스트

k6 테스트 결과
  • VU 100
    VU100 결과
  • VU 200
    VU200 결과
재고 점유 조회 (3분) 총 처리량 Latency(mean) Latency(P95) TPS
VU 100 15615 1.15.s 1.54s 86
VU 200 16067 2.25s 3.13s 88

테스트 결과 분석

Latency

  • Latency(mean)
    • VU 100: 1.35s → 1.15s (약 14.8% 개선)
    • VU 200: 2.74s → 2.25s (약 17.9% 개선)

Latency(mean) : 약 15 ~ 18% 개선 효과를 보임

  • Latency(P95)
    • VU 100: 1.83s → 1.54s (약 15.8% 개선)
    • VU 200: 3.43s → 3.13s (약 8.7% 개선)

Latency(P95) : 약 9 ~ 16% 개선 효과를 보이지만 여전히 지연 모습을 보임

TPS

  • VU 100: 73 → 86 (약 17.8% 증가)
  • VU 200: 72 → 88 (약 22.2% 증가)

TPS : 약 18 ~ 22% 개선

  • 상품, 핫딜 캐싱 조회로 인해 Latency는 약 16%, TPS 는 약 20% 개선

1.1.3. 재고 점유 성능 개선 최종 정리

항목(각 개선 단계는 이전 단계 포함) 로직 개선 및 인덱싱 캐싱
평균 Latency 감소율 6% 16%
TPS 증가율 6% 20%

1.2. 결제 생성

1.2.1. 로직 수정

결제 생성은 PaymentService로 동기 처리 되고있음 "Outbox + FeignClient + 이벤트" 기반의 비동기 처리로 수정

Outbox Entity
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name = "create_payment_outbox",
        indexes = {@Index(name = "idx_outbox_status", columnList = "outbox_status")})
public class CreatePaymentOutbox extends TimeEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String orderId;

    @Column(nullable = false)
    private Long userId;

    @Column(nullable = false)
    private BigDecimal amount;

    @Column(nullable = false)
    private LocalDateTime expireAt;

    @Enumerated(value = EnumType.STRING)
    private OutboxStatus outboxStatus;

    @Column(nullable = false)
    private Integer tryCount;

    private CreatePaymentOutbox(String orderId, Long userId, BigDecimal amount, LocalDateTime expireAt) {
        this.orderId = orderId;
        this.userId = userId;
        this.amount = amount;
        this.expireAt = expireAt;
        this.outboxStatus = OutboxStatus.PENDING;
        this.tryCount = 0;
    }

    // == 생성 메서드 ==
    public static CreatePaymentOutbox create(String orderId, Long userId, BigDecimal amount, LocalDateTime expireAt) {
        return new CreatePaymentOutbox(orderId, userId, amount, expireAt);
    }

    // == 이벤트 성공 처리 ==
    public void updateToSent(){
        this.outboxStatus = OutboxStatus.SENT;
    }

    // == 이벤트 실패 처리 ==
    public void updateToFailed(){
        this.outboxStatus = OutboxStatus.FAILED;
    }

    // == 이벤트 재시도 처리 스킵 처리 ==
    public void updateToSkip(){
        this.outboxStatus = OutboxStatus.SKIP;
    }

    // == 이벤트 진행중 처리 ==
    public void updateToInProgress(){
        this.outboxStatus = OutboxStatus.IN_PROGRESS;
        this.tryCount++;
    }

}
Outbox 생성
    private void saveCreatePaymentOutbox(Long userId, Order savedOrder){
        CreatePaymentOutbox outbox = CreatePaymentOutbox.create(savedOrder.getId(), userId, savedOrder.getAmount(), savedOrder.getExpiresAt());
        createPaymentOutboxRepository.save(outbox);
    }
Outbox Scheduler
@Component
@Profile("!test")
@RequiredArgsConstructor
@Slf4j(topic = "[CreatePaymentScheduler]")
@Transactional
public class CreatePaymentScheduler {
    private final CreatePaymentOutboxRepository outboxRepository;
    private final ApplicationEventPublisher eventPublisher;

    @Scheduled(fixedDelay = 3_000) // 3초 주기
    @SchedulerLock(
            name = "CreatePaymentScheduler",
            lockAtMostFor = "10s",
            lockAtLeastFor = "3s")
    public void publishOutbox(){
        // 1. outbox DB 조회(오래된 데이터부터 가져와서 순서 보장)
        List<OutboxStatus> statuses = List.of(OutboxStatus.FAILED, OutboxStatus.PENDING);
        List<CreatePaymentOutbox> foundOutboxes = outboxRepository.findByOutboxStatusInOrderByIdAsc(statuses);

        // 2. 조회된 outbox FeignClient 이벤트 발행(비동기 처리)
        if(!foundOutboxes.isEmpty()){
            log.info("Payment 생성 outbox {} 건 진행중", foundOutboxes.size());

            for (CreatePaymentOutbox outbox : foundOutboxes) {
                // Status = IN_PROGRESS, tryCount++ 처리;
                outbox.updateToInProgress();
                // 이벤트 발행
                eventPublisher.publishEvent(outbox);
            }
        }

    }
}
Outbox EventListener
@Component
@RequiredArgsConstructor
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Slf4j(topic = "[CreatePaymentEventListener]")
public class CreatePaymentEventListener {
    private final PaymentServiceClient paymentServiceClient;
    private final CreatePaymentOutboxRepository outboxRepository;

    @Async("EventExecutor")
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleCreatePaymentOutbox(CreatePaymentOutbox outbox) {

        Long userId = outbox.getUserId();
        String orderId = outbox.getOrderId();

        try {
            // 1. FeignClient RequestDto 생성
            PaymentCreateRequestDto requestDto =
                    new PaymentCreateRequestDto(orderId, userId, outbox.getAmount(), outbox.getExpireAt());

            // 2. FeignClient 호출
            PaymentCreateResponseDto responseDto = paymentServiceClient.createPayment(requestDto);

            // 3. Payment 생성 성공이면, Outbox Status = sent
            if (responseDto.isSuccess()) {
                log.info("Payment 생성 완료. userId = {}, orderId = {}", userId, orderId);
                outbox.updateToSent();
            }
            // 4. Payment 생성 실패(circuitBreaker, Retry Fallback -> 재시도 대상)
            else if(responseDto.isRetriable()){
                log.error("Payment 생성 실패. (CircuitBreaker OR Retry Fallback 발생). userId = {}, orderId = {}", userId, orderId);
                outbox.updateToFailed();
            }
        }
        // 5. OrderException 이면, 비지니스 로직상 의도된 예외이기 때문에 Outbox Status = skip 처리,
        // 다시 예외 던져서 GlobalHandler 로 예외 응답 처리
        catch (OrderException e){
            log.error("Payment 생성 실패. 비지니스 로직상 예외 발생. userId = {}, orderId = {}", userId, orderId);
            outbox.updateToSkip();
            // 다시 예외 던져서 GlobalHandler 로 예외 응답 처리
            throw e;
        }
        // 6. 예기치 못한 예외
        catch (Exception e){
            log.error("알 수 없는 오류 발생. userId = {}, orderId = {}, errorMessage = {}",
                    userId, orderId, e.getMessage());
            outbox.updateToFailed();
        }

        // 7. 상태 변경된 Outbox 저장
        outboxRepository.save(outbox);
    }
}

2. 재고 점유, 결제 생성 개선 후 주문 테스트

k6 테스트

k6 테스트 결과
  • VU 100
    VU100 결과
  • VU 200
    VU200 결과
  • VU 300
    VU300 결과
주문 생성 (3분) 총 처리량 Latency(mean) Latency(P95) TPS
VU 100 7507 2.41s 3.14s 41
VU 200 7678 4.75s 5.58s 41
VU 300 7925 6.94s 11.67s 42

테스트 결과 분석

Latency

  • Latency(mean)
    • VU 100: 3.01s → 2.41s (약 19.9% 개선)
    • VU 200: 6.56s → 4.75s (약 27.6% 개선)
    • VU 300: 9.83s → 6.94s (약 29.4% 개선)

Latency(mean) : 약 20 ~ 30% 개선

  • Latency(P95)
    • VU 100: 3.74s → 3.14s (약 16.0% 개선)
    • VU 200: 7.86s → 5.58s (약 29.0% 개선)
    • VU 300: 18.12s → 11.67s (약 35.6% 개선)

Latency(P95) : 약 16 ~ 36% 개선

TPS

  • VU 100: 32 → 41 (약 28.1% 증가)
  • VU 200: 29 → 41 (약 41.4% 증가)
  • VU 300: 29 → 42 (약 44.8% 증가)

TPS : 약 28 ~ 45% 개선

  • 개선 전 테스트 VU 300 에서 발생했던 오류는 더이상 발생하지 않음
  • 재고 점유API 개선, 결제 생성 비동기처리로 Latency, TPS 모두 눈에 띄게 개선
  • 최종적으로 Latency 약 25% 개선, TPS 약 36% 개선
⚠️ **GitHub.com Fallback** ⚠️