Locust로 부하 테스트를 진행했을 때, 1000명의 users, 3 workers, 300 ramp-up인 상황에서 RPS는 여전히 300 정도에 머물렀다. 다른 방법이 없는지 더 찾아보다가, 이전 RedissonClient로 락을 걸고 해제하는 과정을 학습하면서 배웠던 Lua Script가 생각났다.
Redis에서 Lua 실행하기(Redis Docs)
Redis는 사용자들이 서버에서 직접 Lua 스크립트를 업로드하고 실행할 수 있도록 허용한다. 스크립트는 서버에서 실행되기 때문에, 스크립트로 데이터를 읽고 쓰는 것은 매우 효율적이다. Redis는 스크립트의 원자적 실행을 보장하며, 스크립트가 실행되는 동안 모든 서버의 활동은 중단된다.(단일 스레드 실행)
Lua 스크립트 작업은 다음과 같은 특징을 지닌다.
여기서 첫 번째와 두 번째의 특징에 착안하여, 기존의 Redis 분산락 기능을 제거하고, Redis에서 실행될 모든 기능을 스크립트화하여 한번에 실행해보려고 한다.
String code = redisTemplate.execute(
issueScript,
List.of(issueRequestKey, issueRequestQueueKey),
String.valueOf(userId),
String.valueOf(totalIssueQuantity),
objectMapper.writeValueAsString(couponIssueRequest)
);
// issueScript
private RedisScript<String> issueRequestScript() {
String script = """
if redis.call('SISMEMBER', KEYS[1], ARGV[1]) == 1 then
return '2'
end
if tonumber(ARGV[2]) > redis.call('SCARD', KEYS[1]) then
redis.call('SADD', KEYS[1], ARGV[1])
redis.call('RPUSH', KEYS[2], ARGV[3])
return '1'
end
return '3'
""";
return RedisScript.of(script, String.class);
}
위와 같이 직접 Lua Script를 작성하였고, 이 스크립트를 redisTemplate을 통해 직접 실행하는 것으로 코드를 수정하였다.


현재까지 시도한 방법 중 RPS가 가장 높은 것은 300 전후였다. 그것이 4000대까지 올라왔으니 RPS가 거의 1400% 증가했다고 평가해야하며, 평균 응답 시간의 경우도 가장 빠른 것이 3.6초였으니 거의 1/20로 줄었다고 평가할 수 있다.
Redis 사용량은 30% 정도로 증가하였으나, 아직 충분히 여유가 있다.
caffeine 의존성 추가implementation("com.github.ben-manes.caffeine:caffeine:3.1.8")
LocalCacheManager를 Bean으로 등록@Bean
public CacheManager localCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(Duration.of(10, ChronoUnit.SECONDS))
.maximumSize(1000));
return cacheManager;
}
@Cacheable(cacheNames = "coupon", cacheManager = "localCacheManager")
public CouponRedisEntity getCouponLocalCache(long couponId) {
return proxy().getCouponCache(couponId);
}
@CachePut(cacheNames = "coupon", cacheManager = "localCacheManager")
public CouponRedisEntity putCouponLocalCache(long couponId) {
return proxy().putCouponCache(couponId);
}
public CouponCacheService proxy() {
return (CouponCacheService) AopContext.currentProxy();
}

로컬 캐시 사용으로 RPS는 4283에서 5311로 증가했고, 평균 응답 속도도 조금 더 줄었다.