Skip to content

[refactor][queue-service] enqueue/delete 로직 원자성 개선 (Lua Script 통합) #11

Description

@coderabbitai

📌 개요

현재 enqueuedelete 가 다단계 처리되어 race condition 윈도우가 존재한다.

enqueue

  • 1단계: setIfAbsent (역인덱스 락)
  • 2단계: MULTI/EXEC (Sorted Set + Hash)
  • 1단계 성공 후 2단계 실패 시 orphan 역인덱스 가능 (TTL 로 자동 정리되지만 일시적 불일치)

delete

  • Java 단 compare-and-delete 적용 (현재):
  String current = redisTemplate.opsForValue().get(userProgramKey);
  if (tokenIdStr.equals(current)) {
      redisTemplate.delete(userProgramKey);
  }
  • GET 과 DEL 이 별도 명령 → 사이의 race 윈도우 (1~2ms) 존재
  • 그 사이 역인덱스 변경 시 다른 토큰의 역인덱스를 잘못 삭제 가능 (Critical)

🎯 목표

  • enqueue 의 진짜 원자성 (중복 검사 + 저장 한 호출)
  • delete 의 race 윈도우 완전 제거
  • 네트워크 왕복 횟수 ↓ (성능 개선 부수 효과)

🧩 리팩토링 범위

  • 클래스 / 패키지 구조 개선
  • 메서드 분리 / 네이밍 개선 (Lua Script 정의 분리)
  • 중복 로직 제거 (다단계 → 단일 호출)
  • 트랜잭션 / 예외 처리 정리 (원자성 보장)

🏗️ 변경 설계

변경 전 구조

enqueue

setIfAbsent (1단계) → MULTI/EXEC (2단계)
→ 1단계 성공 + 2단계 실패 시 orphan 역인덱스

delete

GET → 검사 → DEL (Java 코드)
→ GET 과 DEL 사이 race 가능

변경 후 구조 — Lua Script

enqueue.lua

local userProgramKey = KEYS[1]
local programKey = KEYS[2]
local tokenKey = KEYS[3]
local tokenId = ARGV[1]
local score = ARGV[2]
local ttl = tonumber(ARGV[3])

if redis.call('EXISTS', userProgramKey) == 1 then
    return {err = "DUPLICATE"}
end

redis.call('SET', userProgramKey, tokenId, 'EX', ttl)
redis.call('ZADD', programKey, score, tokenId)
redis.call('HMSET', tokenKey, ...)
redis.call('EXPIRE', tokenKey, ttl)

return {ok = "OK"}

delete.lua (compare-and-delete)

local current = redis.call('GET', KEYS[1])
if current == ARGV[1] then
    redis.call('DEL', KEYS[1])
end
redis.call('ZREM', KEYS[2], ARGV[1])
redis.call('DEL', KEYS[3])

영향 받는 기능

  • enqueue() — Lua Script 로 통합
  • delete() — Lua Script 로 통합
  • 기존 setIfAbsent + MULTI/EXEC 로직 제거

⚠️ 주의 사항

  • 기능 동작은 기존과 동일 (원자성 강화만)
  • API 스펙 변경 없음
  • DuplicateTokenException 던지는 방식 동일 (Lua return 값으로 판별)
  • Lua Script 파일은 src/main/resources/scripts/ 등에 분리 관리

🧪 검증 방법

  • 기존 테스트 통과 여부 확인
  • 동시성 테스트 추가 (concurrent enqueue, race delete)
  • Lua Script 단위 테스트 (Testcontainers Redis 활용)

🔗 참고

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions