자동화 slecs

정산 배치 OOM·이중보정·추적 누락을 한꺼번에 개선

목차

페이지네이션 없이 돌리던 대조 배치를 손봄

거래 대조 보정 배치가 한 번에 전체 거래를 메모리에 올리는 구조였음. 데이터 적을 땐 문제 없었는데 파트너 거래가 누적되면서 OOM 위험이 보이기 시작함. 페이지 단위로 끊어서 처리하도록 바꿈.

  • 페이지 크기는 일단 1000건
  • 마지막 처리 ID 기반 커서 페이지네이션
  • offset 방식은 뒤로 갈수록 누적 스캔 비용이 커져서 제외
while (hasMore) {
  rows = fetch(lastId, pageSize)
  if (rows.empty) break
  reconcile(rows)
  lastId = rows.last.id
}

처음엔 offset/limit 으로 짰다가 데이터 늘어날수록 느려진다는 걸 체감하고 커서 방식으로 갈아엎음. 인덱스도 그대로 타니 응답시간도 안정적.

잔액 동기화 이중보정 막기

잔액 동기화 배치가 같은 거래를 두 번 보정하는 케이스가 보고됨. 원인은 단순했음 — 보정 대상이 락 없이 조회되고, 이전 배치가 처리 중인 사이 다음 배치가 같은 행을 또 집어감.

시도 결과
select for update 락 경합 심함
처리 플래그 컬럼 단순하지만 race 여전
syncKey + sysId 조합 채택

syncKey 에 sysId 를 붙여서 어떤 인스턴스가 보정했는지 식별 가능하게 함. 이미 처리된 syncKey 면 skip. 같은 거래가 두 번 들어와도 두 번째는 무시됨. 멱등성 키로 푸니까 락보다 운영이 편해짐.

sysId 를 굳이 붙인 이유

원래 syncKey 만 있었는데, 멀티 인스턴스에서 같은 키로 동시에 들어오면 어느 쪽이 먼저인지 추적이 안 됐음. sysId 붙이고 나서 로그에서 "어느 인스턴스가 처리했나" 가 한 줄에 보임.

  • 보정 결과 레코드에 sysId 같이 적재
  • 결제 API 응답에도 디버깅용으로 sysId 노출
  • 관리자 도구에서 sysId 별 필터로 인스턴스별 처리량 즉시 확인

회고

  • 페이지네이션은 처음부터 커서로 짰어야 했음. offset 으로 시작하면 결국 다시 갈아엎게 됨
  • 동시성 이슈는 락으로 막기보다 멱등성 키로 푸는 게 운영상 편함
  • 식별자 한 칸 추가했을 뿐인데 장애 추적 시간이 확 줄어듦. 작은 칼럼이 큰 일을 함

다음.

댓글 0

첫 댓글 달아줘.