Chore/#101 vacancy subscribe load test - #103
Conversation
3-Phase 구조로 SADD/SMEMBERS/TTL 각각 측정 - PHASE 1: POST /api/v1/vacancy 100회 호출로 SADD 부하 - PHASE 2: k6/experimental/redis 로 SMEMBERS 응답시간 측정 - PHASE 3: TTL/EXISTS 로 키 자동 삭제 확인
There was a problem hiding this comment.
Code Review
This pull request introduces a load testing suite for the vacancy subscription feature using k6 and a custom token generator utility. The k6 script supports multiple phases including subscription load generation, Redis SMEMBERS response time measurement, and TTL verification. The token generator creates test users and generates JWT tokens. Key feedback points out that the @Transactional annotation on the token generator test causes automatic rollbacks, which requires manual intervention and should be removed. Additionally, the Redis client should be initialized conditionally to prevent errors when testing HTTP-only phases, and performance.now() should be used instead of Date.now() to measure sub-millisecond Redis operations accurately.
| @Test | ||
| @Transactional | ||
| void generateTokens() throws IOException { |
There was a problem hiding this comment.
@Transactional 어노테이션이 테스트 메서드에 적용되어 있으면, Spring의 테스트 컨텍스트 프레임워크는 테스트 종료 후 트랜잭션을 자동으로 롤백합니다. 이로 인해 생성된 시드 유저가 DB에 반영되지 않아 k6 실행 시 USER_NOT_FOUND 에러가 발생하게 됩니다.
가이드 주석에 "테스트 실행 전에 @Transactional을 주석 처리하고 실행 후 원복하라"고 안내되어 있으나, 이는 번거롭고 실수하기 쉽습니다. @Transactional 어노테이션을 아예 제거하면 별도의 수동 작업 없이도 항상 DB에 정상적으로 커밋되므로, 수동 가이드 단계를 없애고 자동화할 수 있습니다.
@Test
void generateTokens() throws IOException {| const auth = REDIS_PASSWORD ? `:${encodeURIComponent(REDIS_PASSWORD)}@` : ''; | ||
| return `redis://${auth}${REDIS_ADDR}`; | ||
| })(); | ||
| const redisClient = new redis.Client(REDIS_URL); |
There was a problem hiding this comment.
PHASE가 subscribe일 때는 Redis 클라이언트를 전혀 사용하지 않음에도 불구하고, 스크립트 로드 시점에 new redis.Client(REDIS_URL)가 호출되어 Redis 연결을 시도합니다. 이로 인해 Redis 서버에 접근할 수 없는 환경(예: 로컬에서 백엔드 HTTP API 부하 테스트만 수행하려는 경우)에서는 스크립트 초기화 단계에서 에러가 발생하여 테스트를 실행할 수 없게 됩니다.
PHASE가 smembers 또는 ttl일 때만 redisClient를 생성하도록 조건부로 초기화하는 것이 좋습니다.
| const redisClient = new redis.Client(REDIS_URL); | |
| const redisClient = (PHASE === 'smembers' || PHASE === 'ttl') ? new redis.Client(REDIS_URL) : null; |
| const start = Date.now(); | ||
| const members = await redisClient.smembers(REDIS_KEY); | ||
| const duration = Date.now() - start; |
There was a problem hiding this comment.
Redis SMEMBERS와 같은 인메모리 조회 작업은 보통 1밀리초(ms) 미만의 매우 빠른 속도로 수행됩니다. Date.now()는 밀리초 단위의 정수만 반환하므로, 실제 소요 시간이 0.3ms인 경우 0으로 측정되어 메트릭의 정확도가 떨어질 수 있습니다.
k6가 지원하는 performance.now()를 사용하면 마이크로초(µs) 수준의 정밀한 소수점 단위 밀리초 측정이 가능하므로, 보다 정확한 응답 시간 분포(p95, p99 등)를 얻을 수 있습니다.
| const start = Date.now(); | |
| const members = await redisClient.smembers(REDIS_KEY); | |
| const duration = Date.now() - start; | |
| const start = performance.now(); | |
| const members = await redisClient.smembers(REDIS_KEY); | |
| const duration = performance.now() - start; |
- @transactional 제거: 자동 롤백으로 시드 유저 미반영 문제 해결 - Redis 클라이언트 조건부 초기화: subscribe phase 단독 실행 가능 - Date.now() -> performance.now(): sub-ms SMEMBERS 측정 정확도 개선
📢 기능 설명
필요시 실행결과 스크린샷 첨부
연결된 issue
연결된 issue를 자동을 닫기 위해 아래 {이슈넘버}를 입력해주세요.
close #{이슈넘버}
✅ 체크리스트