크론 두 번 실행 방지, Postgres advisory lock으로 잠그기
서버 2대에서 크론이 겹쳐 돌아 알림이 중복 발송될 때, Postgres advisory lock으로 실행을 하나로 잠그는 방법을 정리했어요.
크론이 두 번 돌면 알림도 두 번 갑니다. 보통 서버를 한 대에서 두 대로 늘린 날부터요. 혹시 이런 적 없나요?
크론은 왜 두 번 돌까요
스케줄러는 "언제 실행할지"만 알아요. 다른 프로세스가 같은 작업을 이미 하고 있는지는 모르죠. 그래서 서버가 2대면 같은 크론이 2번 돌고, 배포 중에 구 프로세스와 새 프로세스가 1초만 겹쳐도 알림과 메일이 중복 발송돼요.
테이블에 "실행 중" 플래그를 저장하는 방식도 써 봤는데, 프로세스가 죽으면 플래그가 남아서 다음 실행까지 막아 버리는 문제가 있었어요.
advisory lock이 담백한 해결책이에요
Postgres에는 테이블이 아니라 숫자 키를 잠그는 advisory lock이 있어요. 키는 64비트 정수 1개, 또는 32비트 정수 2개 조합이에요. 작업마다 고유 숫자를 정해 두면, 그 키를 잡은 커넥션이 하나뿐이라는 걸 DB가 보장해 줘요.
크론에서는 try 버전이 핵심이에요. pg_try_advisory_lock은 기다리지 않고 즉시 true나 false를 돌려주거든요. 못 잡으면 누군가 이미 하고 있다는 뜻이니 조용히 종료하면 돼요.
-- 잡으면 true, 이미 잡혀 있으면 false
SELECT pg_try_advisory_lock(20260816);
-- 작업이 끝나면 해제
SELECT pg_advisory_unlock(20260816);직접 적용해 봤더니 쿼리 2줄에 분기 1개, 10분이면 끝났어요. 프로세스가 죽어도 커넥션이 끊기면 락이 자동으로 풀리니까, 플래그 방식처럼 청소 로직을 따로 만들 필요가 없다는 점이 제일 마음에 들었어요.
두 가지만 조심하세요
하나는 커넥션 풀이에요. 세션 락은 커넥션이 살아 있는 동안 유지되는데, 풀에서 커넥션을 돌려쓰면 잡은 쪽과 해제하는 쪽이 달라질 수 있어요. 트랜잭션이 끝날 때 자동 해제되는 pg_advisory_xact_lock을 쓰면 이 문제를 피할 수 있어요.
또 하나는 키 충돌이에요. 서로 다른 작업이 같은 숫자를 쓰면 엉뚱한 작업끼리 서로를 막아요. 키 목록은 상수 파일 한 곳에 모아 두세요.
검증도 간단해요. 같은 스크립트를 터미널 2개로 동시에 돌려 보니 한쪽만 true를 받았어요. 테스트해 보면 5분도 안 걸리니 눈으로 꼭 확인해 보세요.
작은 잠금 하나가 신뢰를 지켜요
라이프케어로그 시리즈를 혼자 운영하면서 느낀 건, 중복 알림 한 번이 기능 열 개보다 신뢰를 크게 깎는다는 거예요. 플랜비(plan-b) 같은 작은 도구를 만들 때도 화려한 기능보다 이런 기본기를 먼저 챙기게 되더라고요. 돌고 있는 크론이 하나라도 있다면, 오늘 잠금부터 걸어 보세요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.