[Project : Gold-Rush-Lab] 3. 분산 시스템에서의 Lock 전략
핫스팟 환경에서 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회 반복한다.
실험 환경
- 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()를 호출했다.
마지막으로 기존 OptimisticLockingFailureException 은 GlobalExceptionHandler 클래스가 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 락 전략별 로드밸런서 평균 오류율
결국 DB의 병목을 만들어내는 변수는 한 행에 대한 Row-Level Lock이며, Gold-Rush-Lab에서 Row에 대해 경합하는 트랜잭션의 수는 DB Connection Pool Size에 의해 결정되기 때문이다. 즉, 오류율의 증가폭을 결정하는 것은 VU가 아닌 대기 중인 트랜잭션의 수다.
가설 2. 재시도 로직이 포함된 낙관적 락은 다른 락 전략보다 평균 응답 시간과 P95 응답 시간이 증가할 것이다.
실제로 더 높은 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
VU 증가에 따라 급증하는 낙관적 락 재시도 횟수
비관적 락에서는 API 서버를 2대로 증설했음에도 TPS는 v0.2와 유사한 수준에서 유지(최대 80 req/s 정도의 차이)되었다. 두 API 서버가 동시에 요청을 처리하더라도 결국 동일 Row의 배타적 락을 획득해야 하므로 트랜잭션은 DB에서 직렬화된다. 따라서 API 계층의 처리 능력은 증가했지만, 최종 처리량은 동일 Row에 대한 DB의 Row-Level Lock 처리 속도에 의해 결정되었다.
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에서는 단일 API가 모든 요청을 처리했기 때문에 Process CPU Peak가 높게 나타났지만, v0.3에서는 요청이 두 API 서버로 분산되면서 각 인스턴스의 CPU 사용률이 절반 수준까지 감소하였다.
비록 동일 Row에 대한 DB 락으로 인해 TPS는 크게 증가하지 않았지만, API 계층 자체의 부하는 두 인스턴스로 분산되었음을 확인할 수 있었다.
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의 구간을 좁히며 동시에 데이터 정합성을 확보한 것은 사실이나, 이를 적용하며 (물론 아주 간단한 감산, 가산 함수이지만) MineEntity와 UserEntity의 책임에서 벗어나 객체지향성과 함께 유지보수성이 떨어진 것도 사실이다.
결론적으로 원자적 업데이트는 연산이 적용된 최신 데이터를 바로 반환할 필요가 없는 경우, 또는 비동기적 처리에 적합하겠다는 생각이 들었다.
5. API 증설은 부하를 분산했으나 ‘병목을 제거하진 못했다.’
스케일 아웃한 시스템은 단일 인스턴스의 CPU 사용률을 줄이고, 부하를 성공적으로 분산했다. 하지만 낙관적 락에서는 여전히 동시성과 관련된 오류를 발생시켰고, 비관적 락에서는 TPS 증가, P95, P99 감소를 보여주지는 못했다.
이는 애플리케이션의 처리 능력이 아닌, 동일 데이터에 대한 DB 경합이 시스템의 병목으로 남아 있었기 때문이다. 따라서 단순히 API를 증설하는 것만으로는 한계가 있으며, DB에 도달하기 전에 동일 자원에 대한 동시성을 제어하여 병목 지점을 애플리케이션 계층으로 이동시키는 전략이 필요하다고 판단했다.
댓글남기기