핫스팟 환경에서 API 서버를 증설하면 TPS가 높아질까?

개요

이전 실험에서는 hot spot 상황의 모놀리식 시스템에서 Lock이 끼치는 영향과 그 내부에서 일어나는 일과 DB Connection Pool Size에 대해서 알아보았다.

v0.1 버전에서의 병목 변수가 되었던 DB Connection Pool Size가 v0.2 버전의 Lock을 통해서 병목이 해소될 것이라는 예상과는 다르게 여전히 병목이 될 수 있음을 확인했고, 낙관적 락을 적용했을 경우 빠른 TPS, 응답 시간을 얻지만 높은 오류율이라는 트레이드 오프가 있음을 확인했다. 또한 비관적 락을 적용한 경우는 100%의 응답률을 보이나 TPS가 가장 낮다는 트레이드 오프가 존재했다.

Gold Rush는 동시 접속자 중 가장 빠르게 100개의 금을 채굴한 사람이 승리한다. 따라서 빠른 응답 속도와 더불어 사용자의 요청에 동시성 문제로 인한 오류를 반환해서는 안 된다.

위 요구사항에 맞춘 최적의 다음 시나리오를 구성하기 위해 다음을 적용하여 실험하고, 그 성능을 비교하고자 한다.

  • API 1대 증설
  • API 1대 증설에 따른 LoadBalancer 추가
  • 재시도 로직(20회)이 포함된 낙관적 락을 적용한 v0.3-optimistic_lock 적용 및 실험
  • 기존 비관적 락을 적용한 v0.3-pessimistic_lock 적용 및 실험

추가적으로, 실험을 진행하며 꼭 Lock이 필요한지에 대한 의문이 생겼다. v0.2에서는 실험하지 못했지만 원자적 업데이트를 대조군으로서 추가하여 실험을 진행하기로 했다.

  • Lock이 적용되지 않은 원자적 업데이트 추가

가설

가설 1. 재시도 로직이 포함된 낙관적 락의 오류율은 1% 이상일 것이다.

  • 낙관적 락은 동일한 Row에 대한 충돌을 예외로 감지하고 재시도를 수행한다. 그러나 재시도 횟수(20회)를 초과하거나 요청 시간이 제한되는 경우 일부 요청은 최종적으로 실패할 가능성이 있다.

가설 2. 재시도 로직이 포함된 낙관적 락은 비관적 락보다 평균 응답 시간과 P95 응답 시간이 증가할 것이다.

  • 충돌이 발생한 요청은 여러 번의 재시도를 수행해야 하므로 단일 요청이 완료되기까지의 시간이 증가할 것으로 예상된다.

가설 3. API를 증설하더라도 Hot Spot에서는 DB가 여전히 병목이 될 것이다.

  • Load Balancer는 HTTP 요청을 여러 API 인스턴스로 분산하지만, 모든 요청은 동일한 Mine Row를 대상으로 한다. 따라서 병목은 애플리케이션이 아니라 동일 Row에 대한 DB 접근에서 계속 발생할 것으로 예상된다.

가설 4. API를 Scale-out 하면 단일 API 인스턴스의 Process CPU Peak 평균값은 감소할 것이다.

  • 요청이 여러 API 인스턴스로 분산되므로 각 인스턴스가 처리해야 하는 요청 수가 감소하여 CPU 부하가 분산될 것으로 예상된다.

실험 방법

  • k6는 App VM(192.168.0.41)로 직접 요청하지 않고 Load Balancer VM(192.168.0.47)에 요청한다.
  • 각 실험은 v0.3/optimistic-lock, v0.3/pessimistic-lock 브랜치에 구현 후 수행한다.
    • v0.3/optimistic-lock 은 재시도 20회 로직을 추가한다.
  • DB Connection Pool Size의 영향은 v0.1과 v0.2에서 이미 확인하였다. 이번 실험부터는 기본값(10)을 유지하여 실험한다.
  • VU(가상 유저)는 10, 50, 100, 300, 500으로 실험한다.
  • 각 실험마다 자동화 스크립트를 통해 DB 초기화 → 부하 → 기록 → 30초 대기(실험 구간 분리)를 5회 반복한다.

실험 환경

Gold-Rush-Lab 분산 시스템 실험 환경

  • Host Machine
    • OS: Windows 11
    • CPU: AMD Ryzen 5 5600X
    • Memory: 32 GB
  • Virtualization
    • Hyper-V
  • Network
    • External Virtual Switch, 모든 VM은 동일한 Hyper-V Virtual Switch를 사용

  • APP VM-01, APP VM-02
    • OS: Ubuntu Server 24.04
    • vCPU: 2
    • Memory: 2 GB
  • DB VM
    • OS: Ubuntu Server 24.04
    • vCPU: 2
    • Memory: 2 GB
  • LB(LoadBalancer) VM
    • OS: Ubuntu Server 24.04
    • vCPU: 2
    • Memory: 2 GB
  • Monitoring VM
    • OS: Ubuntu Server 24.04
    • vCPU: 2
    • Memory: 2 GB

  • Load Generator
    • OS : Mac OS Tahoe(26)
    • CPU: M1
    • Memory: 8GB

결과

Optimistic Lock

재시도 로직

재시도 로직은 다음과 같이 구현했다. @Retryable을 통해 최대 20회의 재시도, 최초 1회 총 21회의 시도를 수행하며 5ms의 딜레이에 50%의 가중치가 추가되며, 이 증가된 수치는 0.1초, 100ms를 넘어가지 않도록 구성하였다.

또한 재시도 횟수에 대한 지표를 얻기 위해 OptimisticRetry 지표를 Prometheus에 추가하였다. 지표 수집은 최초 1회 시도를 ‘재시도 지표’로서 추가하지 않기 위해 @EventListener를 적용한 별도의 함수로 구성하였다.

@Service
@RequiredArgsConstructor
public class MiningRetryService {

    private final MineService mineService;
    private final MiningMetrics miningMetrics;
    private final MiningFailureClassifier miningFailureClassifier;

    @Retryable(
            includes = OptimisticLockingFailureException.class,
            maxRetries = 20L,
            delay = 5,
            multiplier = 1.5,
            maxDelay = 100
    )
    public void mine(UUID sessionId, Long amount) {
        mineService.mine(sessionId, amount);
    }

    @EventListener
    public void recordRetry(MethodRetryEvent event) {
        if (event.getMethod().getDeclaringClass() != MiningRetryService.class) {
            return;
        }

        if (event.isRetryAborted()) {
            MiningFailureType failureType = miningFailureClassifier.classify(event.getFailure());
            if (failureType == MiningFailureType.OPTIMISTIC_LOCK) {
                miningMetrics.recordFailure(LockStrategy.OPTIMISTIC, failureType);
            }
            return;
        }

        miningMetrics.incrementOptimisticRetry();
    }
}

이후 컨트롤러에서는 재시도 기능을 포함한 채굴 함수를 호출하기 위해 MineService.mine()이 아닌 MiningRetryService.mine()을 호출한다.

 @PostMapping("/mine")
    public ApiResponse<MineRequestDto> mine(
            @RequestParam(name = "sessionId") UUID sessionId
            ) {
        miningRetryService.mine(sessionId, 1L);
        UserEntity foundUser = userService.findBySessionId(sessionId);

        Long totalMinedGold = foundUser.getTotalMinedGold();
        Long remainingAmount = foundUser.getMine().getRemainingAmount();

        return ApiResponse.success(new MineRequestDto(1L, totalMinedGold, remainingAmount));
    }

MineService.mine()에도 변경 사항이 추가되었다.

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW) // 추가된 변경 사항 - 의도 명시적 선언
public void mine(UUID sessionId, Long amount) {
    LockStrategy strategy = LockStrategy.OPTIMISTIC;
    try {
        UserEntity foundUser = userRepository.findBySessionId(sessionId);
        MineEntity foundMine = foundUser.getMine();

        foundMine.mine(amount);
        foundUser.addGold(amount);

        miningLogRepository.save(
                MiningLogEntity.create(foundUser, foundMine, amount)
        );

        mineRepository.flush(); // 추가된 변경 사항
        recordSuccessAfterCommit(strategy);
    } catch (OptimisticLockingFailureException exception) { // 추가된 변경 사항
        throw exception;
    }
    catch (RuntimeException exception) {
        MiningFailureType failureType = miningFailureClassifier.classify(exception);
        miningMetrics.recordFailure(strategy, failureType);
        throw exception;
    }
}

먼저 MiningRetryService.mine(...)@Transactional 이 적용되어도 MineService.mine(...) 이 지장 받지 않기 위해 명시적으로 매 실행마다 새로운 트랜잭션이 실행되게 propagation 옵션을 추가했다. 이는 현재 MiningRetryService.mine(...)@Transactional 이 적용되어 있지 않기 때문에, Propagation.REQUIRES_NEW 옵션이 존재하지 않아도 의도한 retry 로직은 수행될 것이다.

mineRepository.flush() 는 OptimisticLock 예외를 메서드 내부에서 발생시켜 Retry가 즉시 수행될 수 있도록 flush()를 호출했다.

마지막으로 기존 OptimisticLockingFailureExceptionGlobalExceptionHandler 클래스가 HttpStatus 409 를 반환하지 않게 하기 위해 미리 try-catch 구문을 통해 별도의 실패 처리 로직을 수행하지 않고 그대로 전파했다.

결과 기록 표 (스압)
Record Type Target Type Target Version Hikari Max Pool Size VU Run TPS (req/s) System CPU Peak (%) Process CPU Peak (%) JVM Heap Peak (%) Hikari Active Peak Avg Latency (ms) P95 (ms) P99 (ms) Error Rate (%) Observed Lock Waits Observed Lock Wait Total (ms) Observed Lock Wait Avg (ms) Observed Lock Wait Max (ms) 초기 잔량 사용자 총 채굴량 Mining Log 총 채굴량 실제 잔량 정합성 Started At Finished At
WARMUP LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 1 74.074 req/s       10 100.583 417.307 1051.603 0 0 0 0 0 100 100 100 0 2026-07-31T12:55:22+0900 2026-07-31T12:55:26+0900
WARMUP BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 1 23.658 req/s 83.5 82.759 12.654                             2026-07-31T12:55:22+0900 2026-07-31T12:55:26+0900
WARMUP BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 1 6.010 req/s 30 29.851 9.706                             2026-07-31T12:55:22+0900 2026-07-31T12:55:26+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 1 209.161 req/s       8 41.49 139.66 572.48 0.1 0 0 0 0 1000 999 999 1 2026-07-31T12:56:24+0900 2026-07-31T12:56:31+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 1 113.069 req/s 63.122 56.938 12.818                             2026-07-31T12:56:24+0900 2026-07-31T12:56:31+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 1 113.296 req/s 68.752 62.857 12.208                             2026-07-31T12:56:24+0900 2026-07-31T12:56:31+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 2 263.158 req/s       2 29 88.497 460.734 0 1 0.416 0.416 0.416 1000 1000 1000 0 2026-07-31T12:57:28+0900 2026-07-31T12:57:34+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 2 143.541 req/s 1.5 0.995 12.963                             2026-07-31T12:57:28+0900 2026-07-31T12:57:34+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 2 143.541 req/s 20.588 17.241 12.256                             2026-07-31T12:57:28+0900 2026-07-31T12:57:34+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 3 274.725 req/s       13 32.68 119.341 455.894 0 1 0.577 0.577 0.577 1000 1000 1000 0 2026-07-31T12:58:32+0900 2026-07-31T12:58:37+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 3 151.099 req/s 70.688 63.636 11.08                             2026-07-31T12:58:32+0900 2026-07-31T12:58:37+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 3 151.099 req/s 62.429 54.976 13.258                             2026-07-31T12:58:32+0900 2026-07-31T12:58:37+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 4 303.951 req/s       0 27.368 111.998 450.834 0 0 0 0 0 1000 1000 1000 0 2026-07-31T12:59:34+0900 2026-07-31T12:59:39+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 4 167.173 req/s 9.362 6.373 12.145                             2026-07-31T12:59:34+0900 2026-07-31T12:59:39+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 4 167.173 req/s 38.007 31.579 11.63                             2026-07-31T12:59:34+0900 2026-07-31T12:59:39+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 5 309.215 req/s       4 28.402 113.809 338.807 0 1 0.309 0.309 0.309 1000 1000 1000 0 2026-07-31T13:00:37+0900 2026-07-31T13:00:42+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 5 170.068 req/s 33.521 23.256 13.26                             2026-07-31T13:00:37+0900 2026-07-31T13:00:42+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 5 170.068 req/s 42.249 33.645 10.323                             2026-07-31T13:00:37+0900 2026-07-31T13:00:42+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 10 AVERAGE 272.042 req/s       5.4 31.788 114.661 455.75 0.02 0.6 0.26 0.26 0.26 1000 999.8 999.8 0.2    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   10 AVERAGE 148.990 req/s 35.639 30.24 12.453                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   10 AVERAGE 149.035 req/s 46.405 40.06 11.935                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 1 353.832 req/s       20 131.563 348.106 838.613 0.12 5 3.111 0.622 1.04 5000 4994 4994 6 2026-07-31T13:01:40+0900 2026-07-31T13:01:56+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 1 185.341 req/s 46.364 33.641 12.755                             2026-07-31T13:01:40+0900 2026-07-31T13:01:56+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 1 185.267 req/s 53.745 42.593 14.536                             2026-07-31T13:01:40+0900 2026-07-31T13:01:56+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 2 367.188 req/s       20 123.933 311.822 846.627 0.1 9 4.877 0.542 1.053 5000 4995 4995 5 2026-07-31T13:02:53+0900 2026-07-31T13:03:09+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 2 191.939 req/s 39.526 24.107 12.824                             2026-07-31T13:02:53+0900 2026-07-31T13:03:09+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 2 191.939 req/s 37.882 23.636 14.841                             2026-07-31T13:02:53+0900 2026-07-31T13:03:09+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 3 380.373 req/s       20 121.472 303.95 876.074 0.16 3 1.643 0.548 0.828 5000 4992 4992 8 2026-07-31T13:04:07+0900 2026-07-31T13:04:22+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 3 199.696 req/s 34.778 17.937 14.536                             2026-07-31T13:04:07+0900 2026-07-31T13:04:22+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 3 199.696 req/s 34.751 18.667 15.456                             2026-07-31T13:04:07+0900 2026-07-31T13:04:22+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 4 379.680 req/s       20 122.951 314.462 856.825 0.06 3 3.298 1.099 1.535 5000 4997 4997 3 2026-07-31T13:05:20+0900 2026-07-31T13:05:35+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 4 199.332 req/s 37.292 20.264 15.116                             2026-07-31T13:05:20+0900 2026-07-31T13:05:35+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 4 199.332 req/s 35.088 17.699 14.723                             2026-07-31T13:05:20+0900 2026-07-31T13:05:35+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 5 382.878 req/s       20 121.888 322.938 792.099 0.06 8 5.14 0.642 1.496 5000 4997 4997 3 2026-07-31T13:06:32+0900 2026-07-31T13:06:47+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 5 201.011 req/s 34.671 18.222 13.617                             2026-07-31T13:06:32+0900 2026-07-31T13:06:47+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 5 201.011 req/s 33.489 17.04 15.794                             2026-07-31T13:06:32+0900 2026-07-31T13:06:47+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 50 AVERAGE 372.790 req/s       20 124.362 320.256 842.048 0.1 5.6 3.614 0.691 1.19 5000 4995 4995 5    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   50 AVERAGE 195.464 req/s 38.526 22.834 13.77                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   50 AVERAGE 195.449 req/s 38.991 23.927 15.07                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 1 383.583 req/s       20 247.622 520.582 1004.078 0.05 4 2.41 0.603 0.957 10000 9995 9995 5 2026-07-31T13:07:45+0900 2026-07-31T13:08:13+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 1 197.603 req/s 36.118 18.86 16.497                             2026-07-31T13:07:45+0900 2026-07-31T13:08:13+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 1 197.603 req/s 38.383 26.316 17.706                             2026-07-31T13:07:45+0900 2026-07-31T13:08:13+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 2 385.505 req/s       20 246.89 500.649 1055.949 0.08 16 8.148 0.509 0.917 10000 9992 9992 8 2026-07-31T13:09:11+0900 2026-07-31T13:09:39+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 2 198.593 req/s 35.999 19.383 16.671                             2026-07-31T13:09:11+0900 2026-07-31T13:09:39+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 2 198.593 req/s 35.812 18.182 17.727                             2026-07-31T13:09:11+0900 2026-07-31T13:09:39+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 3 383.656 req/s       20 247.394 510.116 1023.496 0.15 7 2.715 0.388 0.449 10000 9985 9985 15 2026-07-31T13:10:36+0900 2026-07-31T13:11:04+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 3 197.641 req/s 34.07 17.857 17.891                             2026-07-31T13:10:36+0900 2026-07-31T13:11:04+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 3 197.641 req/s 36.168 19.82 18.617                             2026-07-31T13:10:36+0900 2026-07-31T13:11:04+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 4 383.318 req/s       20 249.73 516.962 1031.175 0.1 10 5.711 0.571 1.155 10000 9990 9990 10 2026-07-31T13:12:01+0900 2026-07-31T13:12:30+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 4 197.296 req/s 35.847 19.469 16.864                             2026-07-31T13:12:01+0900 2026-07-31T13:12:30+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 4 197.296 req/s 35.433 20.27 19.406                             2026-07-31T13:12:01+0900 2026-07-31T13:12:30+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 5 382.205 req/s       20 247.127 510.843 1036.028 0.14 14 7.015 0.501 1.072 10000 9986 9986 14 2026-07-31T13:13:27+0900 2026-07-31T13:13:55+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 5 196.893 req/s 34.19 16.889 17.363                             2026-07-31T13:13:27+0900 2026-07-31T13:13:55+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 5 196.893 req/s 34.821 18.667 19.574                             2026-07-31T13:13:27+0900 2026-07-31T13:13:55+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 100 AVERAGE 383.653 req/s       20 247.753 511.831 1030.145 0.104 10.2 5.2 0.514 0.91 10000 9989.6 9989.6 10.4    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   100 AVERAGE 197.605 req/s 35.245 18.492 17.057                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   100 AVERAGE 197.605 req/s 36.123 20.651 18.606                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 1 385.822 req/s       20 745.542 1886.488 2419.989 0.133 12 7.306 0.609 1.122 30000 29960 29960 40 2026-07-31T13:14:53+0900 2026-07-31T13:16:14+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 1 195.154 req/s 38.359 19.192 28.564                             2026-07-31T13:14:53+0900 2026-07-31T13:16:14+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 1 195.154 req/s 37.572 21.633 32.627                             2026-07-31T13:14:53+0900 2026-07-31T13:16:14+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 2 385.233 req/s       20 740.383 2114.82 2463.35 0.15 26 27.652 1.064 4.422 30000 29955 29955 45 2026-07-31T13:17:11+0900 2026-07-31T13:18:32+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 2 194.856 req/s 39.565 21.304 31.217                             2026-07-31T13:17:11+0900 2026-07-31T13:18:32+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 2 194.856 req/s 37.701 23.56 31.13                             2026-07-31T13:17:11+0900 2026-07-31T13:18:32+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 3 385.996 req/s       20 742.619 1970.217 2371.443 0.12 25 17.978 0.719 3.995 30000 29964 29964 36 2026-07-31T13:19:30+0900 2026-07-31T13:20:50+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 3 195.269 req/s 37.934 22.642 31.271                             2026-07-31T13:19:30+0900 2026-07-31T13:20:50+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 3 195.269 req/s 38.044 21.005 30.935                             2026-07-31T13:19:30+0900 2026-07-31T13:20:50+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 4 386.289 req/s       20 750.808 1486.131 2279.558 0.123 30 15.868 0.529 0.977 30000 29963 29963 37 2026-07-31T13:21:48+0900 2026-07-31T13:23:08+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 4 195.417 req/s 37.925 20.276 31.205                             2026-07-31T13:21:48+0900 2026-07-31T13:23:08+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 4 195.417 req/s 37.533 20.29 29.167                             2026-07-31T13:21:48+0900 2026-07-31T13:23:08+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 5 386.703 req/s       20 739.98 1961.925 2408.228 0.167 25 12.821 0.513 1.285 30000 29950 29950 50 2026-07-31T13:24:05+0900 2026-07-31T13:25:26+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 5 195.600 req/s 36.172 19.731 32.579                             2026-07-31T13:24:05+0900 2026-07-31T13:25:26+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 5 195.600 req/s 37.7 19.643 29.451                             2026-07-31T13:24:05+0900 2026-07-31T13:25:26+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 300 AVERAGE 386.009 req/s       20 743.866 1883.916 2388.514 0.139 23.6 16.325 0.687 2.36 30000 29958.4 29958.4 41.6    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   300 AVERAGE 195.259 req/s 37.991 20.629 30.967                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   300 AVERAGE 195.259 req/s 37.71 21.226 30.662                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 1 385.321 req/s       20 1235.57 3096.279 3530.528 0.116 48 22.191 0.462 0.918 50000 49942 49942 58 2026-07-31T13:26:24+0900 2026-07-31T13:28:37+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 1 194.056 req/s 37.054 20.721 30.494                             2026-07-31T13:26:24+0900 2026-07-31T13:28:37+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 1 194.056 req/s 38.542 20.253 31.626                             2026-07-31T13:26:24+0900 2026-07-31T13:28:37+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 2 385.223 req/s       20 1240.732 2934.965 3502.495 0.128 45 18.43 0.41 0.941 50000 49936 49936 64 2026-07-31T13:29:34+0900 2026-07-31T13:31:47+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 2 194.007 req/s 40.394 22.467 31.586                             2026-07-31T13:29:34+0900 2026-07-31T13:31:47+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 2 194.007 req/s 37.884 21.053 34.716                             2026-07-31T13:29:34+0900 2026-07-31T13:31:47+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 3 385.487 req/s       20 1236.147 2951.093 3471.482 0.108 51 146.655 2.876 37.064 50000 49946 49946 54 2026-07-31T13:32:45+0900 2026-07-31T13:34:58+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 3 194.140 req/s 39.658 23.556 32.611                             2026-07-31T13:32:45+0900 2026-07-31T13:34:58+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 3 194.140 req/s 41.294 23.214 31.519                             2026-07-31T13:32:45+0900 2026-07-31T13:34:58+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 4 385.051 req/s       20 1233.533 3088.048 3492.763 0.148 50 28.304 0.566 4.867 50000 49926 49926 74 2026-07-31T13:35:55+0900 2026-07-31T13:38:08+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 4 193.921 req/s 37.077 20.089 32.697                             2026-07-31T13:35:55+0900 2026-07-31T13:38:08+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 4 193.921 req/s 36.564 19.149 30.626                             2026-07-31T13:35:55+0900 2026-07-31T13:38:08+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 5 385.371 req/s       20 1237.986 3041.492 3532.404 0.142 47 23.979 0.51 1.157 50000 49929 49929 71 2026-07-31T13:39:06+0900 2026-07-31T13:41:19+0900
RUN BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 5 194.082 req/s 37.718 20.705 33.46                             2026-07-31T13:39:06+0900 2026-07-31T13:41:19+0900
RUN BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 5 194.082 req/s 37.414 20.988 32.301                             2026-07-31T13:39:06+0900 2026-07-31T13:41:19+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_optimistic-lock 20 500 AVERAGE 385.291 req/s       20 1236.794 3022.376 3505.934 0.128 48.2 47.912 0.965 8.989 50000 49935.8 49935.8 64.2    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_optimistic-lock   500 AVERAGE 194.041 req/s 38.38 21.507 32.169                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_optimistic-lock   500 AVERAGE 194.041 req/s 38.339 20.931 32.158                                

Pessimistic Lock

비관적 락은 v0.2에서의 로직과 같다.

결과 기록 표 (스압)
Record Type Target Type Target Version Hikari Max Pool Size VU Run TPS (req/s) System CPU Peak (%) Process CPU Peak (%) JVM Heap Peak (%) Hikari Active Peak Avg Latency (ms) P95 (ms) P99 (ms) Error Rate (%) Observed Lock Waits Observed Lock Wait Total (ms) Observed Lock Wait Avg (ms) Observed Lock Wait Max (ms) 초기 잔량 사용자 총 채굴량 Mining Log 총 채굴량 실제 잔량 정합성 Started At Finished At
WARMUP LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 1 148.810 req/s       0 62.704 129.228 174.219 0 8 52.105 6.513 7.069 100 100 100 0 2026-07-31T14:11:00+0900 2026-07-31T14:11:03+0900
WARMUP BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 1 47.113 req/s 1 0.5 10.754                             2026-07-31T14:11:00+0900 2026-07-31T14:11:03+0900
WARMUP BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 1 0.000 req/s 1.01 1.005 9.781                             2026-07-31T14:11:00+0900 2026-07-31T14:11:03+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 1 282.965 req/s       10 34.258 59.728 69.346 0 22 35.742 1.625 3.067 1000 1000 1000 0 2026-07-31T14:12:00+0900 2026-07-31T14:12:05+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 1 155.631 req/s 48.558 41.429 13.109                             2026-07-31T14:12:00+0900 2026-07-31T14:12:05+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 1 155.631 req/s 56.045 50.242 12.685                             2026-07-31T14:12:00+0900 2026-07-31T14:12:05+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 2 364.964 req/s       3 26.459 42.709 49.523 0 13 31.999 2.461 2.906 1000 1000 1000 0 2026-07-31T14:13:03+0900 2026-07-31T14:13:08+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 2 200.730 req/s 6.931 4.95 12.631                             2026-07-31T14:13:03+0900 2026-07-31T14:13:08+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 2 200.730 req/s 1 0.5 11.627                             2026-07-31T14:13:03+0900 2026-07-31T14:13:08+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 3 403.714 req/s       7 24.022 38.912 44.213 0 16 40.629 2.539 3.973 1000 1000 1000 0 2026-07-31T14:14:06+0900 2026-07-31T14:14:10+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 3 224.286 req/s 56.383 47.442 12.275                             2026-07-31T14:14:06+0900 2026-07-31T14:14:10+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 3 224.286 req/s 58.326 51.905 12.229                             2026-07-31T14:14:06+0900 2026-07-31T14:14:10+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 4 419.815 req/s       8 23.055 36.46 44.158 0 14 9.154 0.654 1.155 1000 1000 1000 0 2026-07-31T14:15:07+0900 2026-07-31T14:15:11+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 4 230.898 req/s 37.619 30.332 10.62                             2026-07-31T14:15:07+0900 2026-07-31T14:15:11+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 4 230.898 req/s 9.223 5.854 12.831                             2026-07-31T14:15:07+0900 2026-07-31T14:15:11+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 5 447.027 req/s       0 21.465 34.027 41.237 0 16 17.212 1.076 1.75 1000 1000 1000 0 2026-07-31T14:16:09+0900 2026-07-31T14:16:13+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 5 248.348 req/s 0.5 0.503 11.649                             2026-07-31T14:16:09+0900 2026-07-31T14:16:13+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 5 248.348 req/s 0.5 0.5 13.291                             2026-07-31T14:16:09+0900 2026-07-31T14:16:13+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 10 AVERAGE 383.697 req/s       5.6 25.852 42.367 49.695 0 16.2 26.947 1.671 2.57 1000 1000 1000 0    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   10 AVERAGE 211.979 req/s 29.998 24.931 12.057                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   10 AVERAGE 211.979 req/s 25.019 21.8 12.533                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 1 480.584 req/s       20 99.676 200.168 259.379 0 173 209.33 1.21 3.014 5000 5000 5000 0 2026-07-31T14:17:11+0900 2026-07-31T14:17:24+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 1 253.642 req/s 57.707 50.952 13.074                             2026-07-31T14:17:11+0900 2026-07-31T14:17:24+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 1 253.642 req/s 47.411 38.679 12.111                             2026-07-31T14:17:11+0900 2026-07-31T14:17:24+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 2 528.318 req/s       20 90.917 182.797 219.092 0 157 234.562 1.494 2.61 5000 5000 5000 0 2026-07-31T14:18:22+0900 2026-07-31T14:18:34+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 2 279.698 req/s 49.165 41.706 13.225                             2026-07-31T14:18:22+0900 2026-07-31T14:18:34+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 2 279.698 req/s 48.585 41.038 12.759                             2026-07-31T14:18:22+0900 2026-07-31T14:18:34+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 3 549.028 req/s       20 87.915 173.262 217.576 0 153 245.358 1.604 2.83 5000 5000 5000 0 2026-07-31T14:19:31+0900 2026-07-31T14:19:42+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 3 291.671 req/s 23.148 11.682 13.763                             2026-07-31T14:19:31+0900 2026-07-31T14:19:42+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 3 291.671 req/s 26.887 16.588 12.068                             2026-07-31T14:19:31+0900 2026-07-31T14:19:42+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 4 548.366 req/s       20 87.564 181.655 223.809 0 157 219.442 1.398 3.422 5000 5000 5000 0 2026-07-31T14:20:40+0900 2026-07-31T14:20:51+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 4 291.319 req/s 22.526 12.736 13.11                             2026-07-31T14:20:40+0900 2026-07-31T14:20:51+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 4 291.319 req/s 23.6 11.682 13.098                             2026-07-31T14:20:40+0900 2026-07-31T14:20:51+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 5 552.792 req/s       20 86.928 185.205 225.458 0 143 206.661 1.445 2.449 5000 5000 5000 0 2026-07-31T14:21:48+0900 2026-07-31T14:21:59+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 5 292.654 req/s 21.224 11.268 12.374                             2026-07-31T14:21:48+0900 2026-07-31T14:21:59+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 5 292.654 req/s 20.283 9.906 13.089                             2026-07-31T14:21:48+0900 2026-07-31T14:21:59+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 50 AVERAGE 531.818 req/s       20 90.6 184.618 229.063 0 156.6 223.071 1.43 2.865 5000 5000 5000 0    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   50 AVERAGE 281.797 req/s 34.754 25.669 13.109                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   50 AVERAGE 281.797 req/s 33.353 23.579 12.625                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 1 554.109 req/s       20 172.902 358.187 410.86 0 313 383.383 1.225 2.427 10000 10000 10000 0 2026-07-31T14:22:57+0900 2026-07-31T14:23:17+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 1 288.137 req/s 22.579 10.849 15.096                             2026-07-31T14:22:57+0900 2026-07-31T14:23:17+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 1 288.137 req/s 23.501 11.009 14.67                             2026-07-31T14:22:57+0900 2026-07-31T14:23:17+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 2 557.973 req/s       20 172.642 340.911 407.533 0 315 339.194 1.077 2.661 10000 10000 10000 0 2026-07-31T14:24:15+0900 2026-07-31T14:24:36+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 2 289.717 req/s 22.579 11.111 16.63                             2026-07-31T14:24:15+0900 2026-07-31T14:24:36+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 2 289.717 req/s 23.488 11.321 15.834                             2026-07-31T14:24:15+0900 2026-07-31T14:24:36+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 3 557.445 req/s       20 173.406 337.958 402.022 0 318 422.32 1.328 3.4 10000 10000 10000 0 2026-07-31T14:25:33+0900 2026-07-31T14:25:53+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 3 289.871 req/s 22.879 12.207 16.577                             2026-07-31T14:25:33+0900 2026-07-31T14:25:53+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 3 289.871 req/s 22.685 11.215 15.18                             2026-07-31T14:25:33+0900 2026-07-31T14:25:53+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 4 556.235 req/s       20 173.511 337.745 400.087 0 318 466.221 1.466 2.775 10000 10000 10000 0 2026-07-31T14:26:51+0900 2026-07-31T14:27:11+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 4 289.242 req/s 23.394 11.009 16.796                             2026-07-31T14:26:51+0900 2026-07-31T14:27:11+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 4 289.242 req/s 21.597 10.798 16.518                             2026-07-31T14:26:51+0900 2026-07-31T14:27:11+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 5 558.847 req/s       20 172.639 343.908 404.773 0 316 433.574 1.372 2.496 10000 10000 10000 0 2026-07-31T14:28:08+0900 2026-07-31T14:28:29+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 5 290.600 req/s 24.773 10.455 18.038                             2026-07-31T14:28:08+0900 2026-07-31T14:28:29+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 5 290.600 req/s 22.317 11.06 16.331                             2026-07-31T14:28:08+0900 2026-07-31T14:28:29+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 100 AVERAGE 556.922 req/s       20 173.02 343.742 405.055 0 316 408.938 1.294 2.752 10000 10000 10000 0    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   100 AVERAGE 289.513 req/s 23.241 11.126 16.627                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   100 AVERAGE 289.513 req/s 22.717 11.081 15.707                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 1 551.288 req/s       20 520.766 1081.654 1693.779 0 938 1382.627 1.474 2.869 30000 30000 30000 0 2026-07-31T14:29:26+0900 2026-07-31T14:30:23+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 1 280.090 req/s 26.668 16.744 22.51                             2026-07-31T14:29:26+0900 2026-07-31T14:30:23+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 1 280.090 req/s 23.964 13.023 21.371                             2026-07-31T14:29:26+0900 2026-07-31T14:30:23+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 2 548.035 req/s       20 526.556 986.202 1588.61 0 955 1347.815 1.411 3.078 30000 30000 30000 0 2026-07-31T14:31:21+0900 2026-07-31T14:32:19+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 2 278.367 req/s 24.178 12.037 23.768                             2026-07-31T14:31:21+0900 2026-07-31T14:32:19+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 2 278.367 req/s 25.34 11.881 23.322                             2026-07-31T14:31:21+0900 2026-07-31T14:32:19+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 3 555.011 req/s       20 517.986 1069.053 1631.048 0 947 1349.617 1.425 9.934 30000 30000 30000 0 2026-07-31T14:33:17+0900 2026-07-31T14:34:14+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 3 281.981 req/s 22.58 11.94 26.055                             2026-07-31T14:33:17+0900 2026-07-31T14:34:14+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 3 281.981 req/s 23.959 12.5 23.394                             2026-07-31T14:33:17+0900 2026-07-31T14:34:14+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 4 554.765 req/s       20 517.806 1087.323 1674.386 0 949 1342.294 1.414 6.348 30000 30000 30000 0 2026-07-31T14:35:12+0900 2026-07-31T14:36:09+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 4 281.856 req/s 21.845 10.798 28.232                             2026-07-31T14:35:12+0900 2026-07-31T14:36:09+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 4 281.856 req/s 22.895 11.628 24.5                             2026-07-31T14:35:12+0900 2026-07-31T14:36:09+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 5 554.847 req/s       20 517.054 1061.345 1615.426 0 945 1308.971 1.385 2.591 30000 30000 30000 0 2026-07-31T14:37:06+0900 2026-07-31T14:38:03+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 5 281.898 req/s 30 19.178 24.264                             2026-07-31T14:37:06+0900 2026-07-31T14:38:03+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 5 281.898 req/s 23.593 14.486 26.279                             2026-07-31T14:37:06+0900 2026-07-31T14:38:03+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 300 AVERAGE 552.789 req/s       20 520.033 1057.115 1640.65 0 946.8 1346.265 1.422 4.964 30000 30000 30000 0    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   300 AVERAGE 280.838 req/s 25.054 14.14 24.966                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   300 AVERAGE 280.838 req/s 23.95 12.704 23.773                                
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 1 553.912 req/s       20 869.462 1795.479 2360.526 0 1583 2163.211 1.367 2.824 50000 50000 50000 0 2026-07-31T14:39:01+0900 2026-07-31T14:40:35+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 1 279.754 req/s 23.033 11 27.044                             2026-07-31T14:39:01+0900 2026-07-31T14:40:35+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 1 279.754 req/s 23.252 11.682 28.478                             2026-07-31T14:39:01+0900 2026-07-31T14:40:35+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 2 554.631 req/s       20 862.167 1807.036 2391.894 0 1568 2432.654 1.551 8.665 50000 50000 50000 0 2026-07-31T14:41:33+0900 2026-07-31T14:43:07+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 2 280.117 req/s 22.749 11.399 28.726                             2026-07-31T14:41:33+0900 2026-07-31T14:43:07+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 2 280.117 req/s 24.545 11.872 23.979                             2026-07-31T14:41:33+0900 2026-07-31T14:43:07+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 3 555.111 req/s       20 856.696 1869.355 2418.904 0 1566 2211.285 1.412 2.778 50000 50000 50000 0 2026-07-31T14:44:05+0900 2026-07-31T14:45:39+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 3 280.359 req/s 24.155 11.881 31.547                             2026-07-31T14:44:05+0900 2026-07-31T14:45:39+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 3 280.359 req/s 23.617 12.093 28.757                             2026-07-31T14:44:05+0900 2026-07-31T14:45:39+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 4 553.796 req/s       20 860.191 1839.807 2430.77 0 1568 2094.677 1.336 5.195 50000 50000 50000 0 2026-07-31T14:46:36+0900 2026-07-31T14:48:10+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 4 279.695 req/s 22.826 11.892 27.495                             2026-07-31T14:46:36+0900 2026-07-31T14:48:10+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 4 279.695 req/s 22.674 11.268 30.265                             2026-07-31T14:46:36+0900 2026-07-31T14:48:10+0900
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 5 555.056 req/s       20 856.163 1891.43 2447.289 0 1578 2403.373 1.523 9.335 50000 50000 50000 0 2026-07-31T14:49:08+0900 2026-07-31T14:50:42+0900
RUN BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 5 280.331 req/s 23.464 11.282 27.91                             2026-07-31T14:49:08+0900 2026-07-31T14:50:42+0900
RUN BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 5 280.331 req/s 24.318 14.554 32.923                             2026-07-31T14:49:08+0900 2026-07-31T14:50:42+0900
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_pessimistic-lock 20 500 AVERAGE 554.501 req/s       20 860.936 1840.621 2409.877 0 1572.6 2261.04 1.438 5.759 50000 50000 50000 0    
AVERAGE BACKEND 192.168.0.41:8080 v0.3_pessimistic-lock   500 AVERAGE 280.051 req/s 23.246 11.491 28.545                                
AVERAGE BACKEND 192.168.0.46:8080 v0.3_pessimistic-lock   500 AVERAGE 280.051 req/s 23.681 12.294 28.88                                

Atomic Update

//MineServiceImpl.java
@Override
@Transactional
public void mine(UUID sessionId, Long amount) {
    LockStrategy strategy = LockStrategy.from(configuredLockStrategy);
    try {
        UserEntity foundUser = userRepository.findBySessionId(sessionId);
        MineEntity foundMine = foundUser.getMine();

				// 기존
				// foundUser.addGold(amount);
        int updatedMine = mineRepository.decreaseRemainingAmount(foundMine.getId(), amount);
        if (updatedMine == 0) {
            throw new MineDepletedException();
        }
				// 기존
				// foundMine.mine(amount);
        int updatedUser = userRepository.increaseTotalMinedGold(sessionId, amount);
        if (updatedUser == 0) {
            throw new UserNotFoundException();
        }

        foundUser = userRepository.findBySessionId(foundUser.getSessionId());
        foundMine = foundUser.getMine();

        miningLogRepository.save(
                MiningLogEntity.create(foundUser, foundMine, amount)
        );
        recordSuccessAfterCommit(strategy);
    } catch (RuntimeException exception) {
        MiningFailureType failureType = miningFailureClassifier.classify(exception);
        miningMetrics.recordFailure(strategy, failureType);
        throw exception;
    }
}

유저 엔티티와 광산 엔티티의 비즈니스 로직에 대한 책임을 Repository로 이동시켰다.

또한 각 UPDATE 함수에 flushAutomatically, clearAutomatically를 적용하여 바로 최신 데이터를 조회할 수 있도록 설정하였다.

public interface UserJpaRepository extends JpaRepository<UserEntity, Long> {
    @Modifying(
            flushAutomatically = true
            , clearAutomatically = true
    )
    @Query("""
    update UserEntity u
        set u.totalMinedGold = u.totalMinedGold + :amount
        where u.sessionId=:sessionId
    """)
    int increaseTotalMinedGold(
            @Param("sessionId") UUID sessionId
            , @Param("amount") Long amount
    );
}
public interface MineJpaRepository extends JpaRepository<MineEntity, Long> {
    Optional<MineEntity> findFirstByRemainingAmountGreaterThanOrderByIdAsc(long remainingAmount);
    @Modifying(
            flushAutomatically = true
            , clearAutomatically = true
    )
    @Query("""
    update MineEntity m
       set m.remainingAmount = m.remainingAmount - :amount
     where m.id = :mineId
       and m.remainingAmount >= :amount
    """)
    int decreaseRemainingAmount(
            @Param("mineId") Long mineId
            , @Param("amount") Long amount
    );
}
결과 기록 표 (스압)
Record Type Target Type Target Version Hikari Max Pool Size VU Run TPS (req/s) System CPU Peak (%) Process CPU Peak (%) JVM Heap Peak (%) Hikari Active Peak Avg Latency (ms) P95 (ms) P99 (ms) Error Rate (%) Observed Lock Waits Observed Lock Wait Total (ms) Observed Lock Wait Avg (ms) Observed Lock Wait Max (ms) 초기 잔량 사용자 총 채굴량 Mining Log 총 채굴량 실제 잔량 정합성
WARMUP LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 1 120.192 req/s       0 74.766 174.542 248.38 0 7 13.932 1.99 2.011 100 100 100 0
WARMUP BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 1 6.007 req/s 9.5 9.137 10.414                            
WARMUP BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 1 64.992 req/s 44.807 42.647 12.534                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 1 249.875 req/s       4 38.859 96.933 164.752 0 30 79.297 2.643 4.263 1000 1000 1000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 1 136.295 req/s 61.199 57.843 11.17                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 1 136.295 req/s 38.724 35.437 12.603                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 2 316.056 req/s       8 29.5 78.423 111.702 0 21 29.098 1.386 2.06 1000 1000 1000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 2 173.831 req/s 60.615 55.556 12.666                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 2 173.831 req/s 56.955 50.962 11.088                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 3 349.284 req/s       2 26.93 71.371 103.954 0 9 12.436 1.382 1.61 1000 1000 1000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 3 192.106 req/s 0 0.503 12.918                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 3 192.106 req/s 2.485 1.005 11.904                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 4 396.668 req/s       7 24.027 60.164 80.326 0 16 31.453 1.966 2.58 1000 1000 1000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 4 220.371 req/s 58.189 51.429 12.739                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 4 220.371 req/s 58.927 51.174 10.802                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 5 416.146 req/s       0 22.252 57.409 110.853 0 16 31.398 1.962 2.256 1000 1000 1000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 5 231.192 req/s 2 2.01 11.871                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 5 231.192 req/s 1 0.498 11.596                            
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 10 AVERAGE 345.606 req/s       4.2 28.313 72.86 114.317 0 18.4 36.736 1.868 2.554 1000 1000 1000 0
AVERAGE BACKEND 192.168.0.41:8080 v0.3_atomic-update   10 AVERAGE 190.759 req/s 36.401 33.468 12.273                            
AVERAGE BACKEND 192.168.0.46:8080 v0.3_atomic-update   10 AVERAGE 190.759 req/s 31.618 27.815 11.599                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 1 449.802 req/s       20 106.648 222.73 291.282 0 143 179.781 1.257 2.703 5000 5000 5000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 1 237.396 req/s 32.567 22.685 10.443                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 1 237.396 req/s 38.319 29.245 12.421                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 2 482.393 req/s       20 99.916 195.479 259.3 0 139 147.419 1.061 1.61 5000 5000 5000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 2 255.384 req/s 42.65 34.434 12.865                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 2 255.384 req/s 40.712 30.556 12.889                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 3 502.109 req/s       20 95.366 192.793 255.479 0 136 131.8 0.969 1.952 5000 5000 5000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 3 265.822 req/s 21.96 11.268 11.664                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 3 265.822 req/s 24.312 11.416 12.743                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 4 506.483 req/s       20 95.337 191.492 258.466 0 149 163.717 1.099 2.216 5000 5000 5000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 4 268.138 req/s 34.86 26.761 12.064                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 4 268.138 req/s 29.73 19.811 11.978                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 5 510.204 req/s       20 94.642 187.261 242.321 0 151 192.834 1.277 2.145 5000 5000 5000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 5 270.108 req/s 23.286 10.849 12.495                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 5 270.108 req/s 22.674 11.574 12.986                            
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 50 AVERAGE 490.198 req/s       20 98.382 197.951 261.369 0 143.6 163.11 1.133 2.125 5000 5000 5000 0
AVERAGE BACKEND 192.168.0.41:8080 v0.3_atomic-update   50 AVERAGE 259.370 req/s 31.065 21.199 11.906                            
AVERAGE BACKEND 192.168.0.46:8080 v0.3_atomic-update   50 AVERAGE 259.370 req/s 31.149 20.52 12.603                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 1 506.560 req/s       20 190.961 362.563 430.559 0 317 390.092 1.231 5.303 10000 10000 10000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 1 262.661 req/s 22.216 11.268 14.063                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 1 262.661 req/s 23.505 11.872 13.684                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 2 506.175 req/s       20 191.013 351.128 417.046 0 319 351.011 1.1 2.655 10000 10000 10000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 2 262.461 req/s 22.151 11.682 14.39                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 2 262.461 req/s 23.033 11.737 14.866                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 3 507.460 req/s       20 192.142 308.042 368.881 0 335 278.893 0.833 4.017 10000 10000 10000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 3 263.127 req/s 23.968 10.86 14.292                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 3 263.127 req/s 24.758 11.416 14.898                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 4 502.917 req/s       20 192.393 333.978 405.884 0 313 300.496 0.96 2.092 10000 10000 10000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 4 260.772 req/s 23.042 10.909 15.511                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 4 260.772 req/s 24.081 10.959 15.307                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 5 507.331 req/s       20 191.743 329.422 402.418 0 312 283.627 0.909 2.212 10000 10000 10000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 5 263.060 req/s 22.222 11.06 15.827                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 5 263.060 req/s 23.724 11.009 15.349                            
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 100 AVERAGE 506.089 req/s       20 191.651 337.027 404.958 0 319.2 320.824 1.007 3.256 10000 10000 10000 0
AVERAGE BACKEND 192.168.0.41:8080 v0.3_atomic-update   100 AVERAGE 262.416 req/s 22.72 11.156 14.816                            
AVERAGE BACKEND 192.168.0.46:8080 v0.3_atomic-update   100 AVERAGE 262.416 req/s 23.82 11.399 14.821                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 1 503.694 req/s       20 571.957 1239.478 1768.743 0 793 862.58 1.088 2.244 30000 30000 30000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 1 255.551 req/s 22.721 13.208 20.135                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 1 255.551 req/s 25 16.038 21.041                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 2 504.168 req/s       20 582.924 807.085 1274.984 0 853 832.383 0.976 2.027 30000 30000 30000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 2 255.791 req/s 24.422 12.5 22.519                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 2 255.791 req/s 23.98 12.264 22.319                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 3 506.244 req/s       20 567.443 1221.162 1877.322 0 874 976.908 1.118 6.816 30000 30000 30000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 3 256.900 req/s 23.475 11.737 23.543                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 3 256.900 req/s 23.256 11.737 23.712                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 4 504.965 req/s       20 571.024 1078.158 1666.282 0 952 1047.15 1.1 2.35 30000 30000 30000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 4 256.196 req/s 23.585 11.374 25.453                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 4 256.196 req/s 25.211 11.927 25.439                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 5 503.533 req/s       20 577.489 1009.031 1698.97 0 864 918.19 1.063 2.481 30000 30000 30000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 5 255.469 req/s 24.081 11.06 26.756                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 5 255.469 req/s 24.081 11.982 25.576                            
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 300 AVERAGE 504.521 req/s       20 574.167 1070.983 1657.26 0 867.2 927.442 1.069 3.184 30000 30000 30000 0
AVERAGE BACKEND 192.168.0.41:8080 v0.3_atomic-update   300 AVERAGE 255.981 req/s 23.657 11.976 23.681                            
AVERAGE BACKEND 192.168.0.46:8080 v0.3_atomic-update   300 AVERAGE 255.981 req/s 24.306 12.789 23.617                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 1 501.701 req/s       20 970.561 1792.337 2000.475 0 1460 2762.387 1.892 76.815 50000 50000 50000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 1 253.173 req/s 23.07 11.005 25.971                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 1 253.173 req/s 23.765 11.792 23.813                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 2 504.490 req/s       20 959.103 1939.236 2307.646 0 1508 1652.352 1.096 2.408 50000 50000 50000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 2 254.538 req/s 23.364 12.5 26.63                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 2 254.538 req/s 23.874 11.628 26.439                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 3 502.179 req/s       20 967.974 1787.259 1974.012 0 1534 1596.209 1.041 2.513 50000 50000 50000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 3 253.393 req/s 23.042 11.429 28.413                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 3 253.393 req/s 27.787 16.372 28.482                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 4 502.947 req/s       20 958.546 1898.203 2414.269 0 1517 1629.411 1.074 2.576 50000 50000 50000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 4 253.802 req/s 23.623 11.165 30.311                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 4 253.802 req/s 23.033 12.037 30.417                            
RUN LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 5 503.651 req/s       20 966.232 1804.996 2021.393 0 1539 1630.899 1.06 2.724 50000 50000 50000 0
RUN BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 5 254.136 req/s 25.414 14.706 32.677                            
RUN BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 5 254.136 req/s 25.34 12.903 32.107                            
AVERAGE LOAD_BALANCER http://192.168.0.47/api v0.3_atomic-update 20 500 AVERAGE 502.994 req/s       20 964.483 1844.406 2143.559 0 1511.6 1854.252 1.233 17.407 50000 50000 50000 0
AVERAGE BACKEND 192.168.0.41:8080 v0.3_atomic-update   500 AVERAGE 253.809 req/s 23.703 12.161 28.8                            
AVERAGE BACKEND 192.168.0.46:8080 v0.3_atomic-update   500 AVERAGE 253.809 req/s 24.76 12.946 28.252                            

분석

가설 1. 재시도 로직이 포함된 낙관적 락의 오류율은 1% 이상일 것이다.

모든 VU(10, 50, 100, 300, 500)에서 0.2% 미만의 낮은 오류율을 보였다. 오류는 1회 요청, 총 20회의 재시도에서 모두 실패하여 사용자에게 HttpStatus 409를 반환한 응답이다.

VU **Mining Log 총 채굴량    
평균값** **실제 잔량    
평균값** Error Rate    
10 999.8 0.2 0.02
50 4995 5 0.1
100 9989.6 10.4 0.104
300 29958.4 41.6 0.139
500 49935.8 64.2 0.128

하지만 그 증가폭이 유의미하다고 보기는 어려운 수치이다. 그 이유는 분산 시스템을 적용했다고 해서 v0.2에서 발생한 DB 병목이 애플리케이션 레이어로 이동한 것은 아니며, 재시도 로직을 통해 동시성 문제를 해결한 것이 아니라 ‘완화’했기 때문이다.

v0.3 낙관적 락, 비관적 락, 원자적 업데이트의 평균 오류율

v0.3 락 전략별 로드밸런서 평균 오류율

결국 DB의 병목을 만들어내는 변수는 한 행에 대한 Row-Level Lock이며, Gold-Rush-Lab에서 Row에 대해 경합하는 트랜잭션의 수는 DB Connection Pool Size에 의해 결정되기 때문이다. 즉, 오류율의 증가폭을 결정하는 것은 VU가 아닌 대기 중인 트랜잭션의 수다.

가설 2. 재시도 로직이 포함된 낙관적 락은 다른 락 전략보다 평균 응답 시간과 P95 응답 시간이 증가할 것이다.

실제로 더 높은 P95 응답 시간을 보인 버전은 낙관적 락이었다.

v0.3 낙관적 락, 비관적 락, 원자적 업데이트의 P95 응답 시간

v0.3 락 전략별 P95 응답 시간

이는 재시도의 비용과 관련이 있다. 실제로 재시도 비용은 다음과 같다.

  • TRANSACTION(SELECT 유저, SELECT 광물, UPDATE 유저, UPDATE 광물)

위와 같은 트랜잭션을 낙관적 락에서는 한 요청당 최악의 경우 21번 반복한다.

이 비용은 비관적 락이 SELECT 광물 시에 FOR UPDATE를 통해 Row-Level Lock을 획득하는 비용이나, 원자적 업데이트를 통해 이를 처리하는 비용보다 더 높았던 것이다.

가설 3. API를 증설하더라도 Hot Spot에서는 DB가 여전히 병목이 될 것이다.

v0.2와 v0.3 사이에 재시도 정책이 달라 직접적인 Scale-out 성능 비교에서는 제외하였다. 대신 v0.3 내부에서 VU 증가에 따라 재시도가 급증하고 TPS가 약 360에서 포화되는 현상을 통해 동일 Row 경합이 처리량을 제한하는 보조 근거로 활용하였다.

v0.2와 v0.3 낙관적 락의 평균 TPS

v0.2와 v0.3 낙관적 락의 평균 TPS

VU 증가에 따른 낙관적 락 재시도 횟수

VU 증가에 따라 급증하는 낙관적 락 재시도 횟수

비관적 락에서는 API 서버를 2대로 증설했음에도 TPS는 v0.2와 유사한 수준에서 유지(최대 80 req/s 정도의 차이)되었다. 두 API 서버가 동시에 요청을 처리하더라도 결국 동일 Row의 배타적 락을 획득해야 하므로 트랜잭션은 DB에서 직렬화된다. 따라서 API 계층의 처리 능력은 증가했지만, 최종 처리량은 동일 Row에 대한 DB의 Row-Level Lock 처리 속도에 의해 결정되었다.

v0.2와 v0.3 비관적 락의 평균 TPS

v0.2와 v0.3 비관적 락의 평균 TPS

Scale-out은 API 계층의 처리 능력을 높이는 방법이지만, Hot Spot 환경에서는 모든 요청이 동일한 데이터(Row)에 집중된다.

API 1 --------
						   |
API 2 -----> 동일 Mine(Row) -----> PostgreSQL

와 같이 모든 요청이 결국 하나의 Row에 도달하기 때문에 API 인스턴스를 추가하더라도 DB의 동시성 제약은 그대로 유지된다.

특히 비관적 락은 DB에서 Row-Level Lock을 획득하기 위해 대기하고, 낙관적 락은 버전 충돌로 인해 재시도가 반복된다. 두 방식 모두 병목의 위치는 API가 아니라 DB라는 공통점을 가진다.

가설 4. API를 Scale-out 하면 단일 API 인스턴스의 Process CPU Peak 평균값은 감소할 것이다.

낙관적 락에서 API 서버를 1대에서 2대로 증설한 결과, 단일 API 인스턴스의 Process CPU Peak는 v0.2보다 크게 감소하였다.

v0.3에서는 Retry 로직이 추가되어 동일 요청이 여러 번 실행될 수 있었음에도, 두 개의 API 서버가 요청을 분산 처리하면서 개별 인스턴스가 담당하는 요청 수는 감소하였다. 그 결과 각 API 인스턴스의 CPU 사용률은 약 25~30% 수준에서 안정적으로 유지되었다.

즉, Retry로 인해 전체 시스템의 작업량은 증가했지만, API 계층을 Scale-out함으로써 개별 인스턴스의 CPU 부담은 효과적으로 분산되었다.

v0.2와 v0.3 낙관적 락의 Process CPU Peak 평균값

v0.2와 v0.3 낙관적 락의 Process CPU Peak 평균값

비관적 락에서도 동일한 결과를 확인하였다.

v0.2에서는 단일 API가 모든 요청을 처리했기 때문에 Process CPU Peak가 높게 나타났지만, v0.3에서는 요청이 두 API 서버로 분산되면서 각 인스턴스의 CPU 사용률이 절반 수준까지 감소하였다.

비록 동일 Row에 대한 DB 락으로 인해 TPS는 크게 증가하지 않았지만, API 계층 자체의 부하는 두 인스턴스로 분산되었음을 확인할 수 있었다.

v0.2와 v0.3 비관적 락의 Process CPU Peak 평균값

v0.2와 v0.3 비관적 락의 Process CPU Peak 평균값

결론

1. 핫스팟 환경에서는 API 증설만으로 TPS를 유의미하게 개선시킬 수 없다.

API를 1대에서 2대로 증설하여 애플리케이션 계층의 처리 능력을 확장하였지만, 핫스팟 환경에서는 TPS가 서버 수에 비례하여 증가하지 않았다. 낙관적 락에서는 Retry 비용이 증가하였고, 또 그에 따라 버전 충돌 또한 증가했다.

비관적 락에서는 동일 Row에 대한 Row-Level Lock으로 인해 트랜잭션이 직렬화되었다. 결과적으로 API를 증설해도 병목은 여전히 DB에 존재하였으며, Hot Spot 환경에서는 API 증설만으로 처리량 한계를 해결할 수 없음을 확인하였다.

2. 핫스팟 환경에서는 재시도를 적용한 낙관적 락은 비관적 락보다 더 늦은 응답을 반환한다.

재시도를 적용한 낙관적 락은 대부분의 버전 충돌을 성공적으로 복구하여 정합성을 유지하였다. 그러나 충돌이 발생할 때마다 새로운 트랜잭션에서 조회와 갱신을 반복하고 재시도마다의 딜레이가 적용되며 응답 시간이 크게 증가하였다. 반면 비관적 락은 DB에서 대기 시간이 발생하더라도 한 번의 트랜잭션으로 작업을 완료하므로, 핫스팟 환경에서는 평균 응답 시간과 P95, P99 모두 재시도를 적용한 낙관적 락보다 낮게 나타났다.

3. API 서버 증설은 단일 API 인스턴스의 부하를 효과적으로 분산시킨다.

API를 증설한 이후 단일 API 인스턴스의 Process CPU Peak는 v0.2 대비 유의미하게 감소하였다. 이는 Load Balancer가 요청을 여러 API 인스턴스로 분산하여 애플리케이션 계층의 연산 부하를 효과적으로 분산시켰기 때문이다. 낙관적 락에서는 재시도로 인해 전체 연산량이 증가했음에도 각 API 인스턴스의 CPU 사용률은 감소하였으며, 비관적 락에서도 동일한 경향을 확인하였다.

4. 원자적 업데이트는 항상 최선의 선택은 아니다.

이번 버전에서 원자적 업데이트는 주요 비교 대상은 아니었지만 가장 빠른 응답 속도 및 처리 속도가 기대되어 추가하였다. 하지만 결과 값을 반환하기 위한 플러시, 영속성 컨텍스트의 초기화와 결과 값 제공을 위한 재조회 비용으로 인해서 비관적 락과 비슷한 정도의 성능을 보였다. (P95, TPS)

실제로 원자적 업데이트가 Read-Modify-Write의 구간을 좁히며 동시에 데이터 정합성을 확보한 것은 사실이나, 이를 적용하며 (물론 아주 간단한 감산, 가산 함수이지만) MineEntityUserEntity의 책임에서 벗어나 객체지향성과 함께 유지보수성이 떨어진 것도 사실이다.

결론적으로 원자적 업데이트는 연산이 적용된 최신 데이터를 바로 반환할 필요가 없는 경우, 또는 비동기적 처리에 적합하겠다는 생각이 들었다.

5. API 증설은 부하를 분산했으나 ‘병목을 제거하진 못했다.’

스케일 아웃한 시스템은 단일 인스턴스의 CPU 사용률을 줄이고, 부하를 성공적으로 분산했다. 하지만 낙관적 락에서는 여전히 동시성과 관련된 오류를 발생시켰고, 비관적 락에서는 TPS 증가, P95, P99 감소를 보여주지는 못했다.

이는 애플리케이션의 처리 능력이 아닌, 동일 데이터에 대한 DB 경합이 시스템의 병목으로 남아 있었기 때문이다. 따라서 단순히 API를 증설하는 것만으로는 한계가 있으며, DB에 도달하기 전에 동일 자원에 대한 동시성을 제어하여 병목 지점을 애플리케이션 계층으로 이동시키는 전략이 필요하다고 판단했다.

댓글남기기