Fencing Token: 분산 락이 놓치는 빈틈을 막는 안전장치

Fencing Token이란 무엇인가

분산 시스템에서 여러 서버가 동시에 하나의 자원에 접근하지 못하도록 막기 위해 분산 락(Distributed Lock)을 사용합니다. 하지만 락을 획득한 서버가 GC 정지나 네트워크 지연 때문에 오랫동안 응답하지 못하면, 락 서버는 시간 초과로 판단해 락을 풀어버리고 다른 서버에게 락을 넘겨줄 수 있습니다. 문제는 원래 락을 가졌던 서버가 나중에 깨어나 자신이 여전히 락을 쥐고 있다고 착각한 채 작업을 이어가는 경우입니다. 이렇게 되면 두 서버가 동시에 같은 자원을 건드리는 사고가 발생합니다. Fencing Token은 이런 상황을 막기 위해 락을 발급할 때마다 단조 증가하는 숫자를 함께 부여하는 방식입니다.

동작 원리와 예시

Fencing Token의 핵심은 ‘가장 최근에 발급된 토큰보다 작은 토큰은 무시한다’는 규칙입니다. 락 서버는 락을 넘겨줄 때마다 토큰 값을 1씩 증가시킵니다. 자원을 관리하는 스토리지 서버는 요청이 들어올 때마다 함께 전달된 토큰 값을 확인하고, 이전에 처리한 토큰보다 작은 값이 오면 그 요청을 거부합니다.

예를 들어 서버 A가 토큰 33을 받아 락을 획득했다가 응답 지연으로 락이 풀리고, 서버 B가 토큰 34를 받아 새로 락을 획득했다고 가정해봅시다. 이후 서버 A가 뒤늦게 깨어나 토큰 33을 들고 자원에 쓰기 요청을 보내면, 스토리지는 이미 34번 요청을 처리했기 때문에 33번 요청을 즉시 거부합니다. 마치 은행 창구에서 번호표 순서가 지나간 뒤에는 이전 번호표를 들고 와도 처리해주지 않는 것과 같은 원리입니다. 요청 코드로 표현하면 if (request.token < lastProcessedToken) reject() 정도로 단순화할 수 있습니다.

실무에서 Fencing Token을 쓰는 이유

Redis 기반 락인 Redlock이나 Zookeeper, etcd 같은 분산 코디네이터를 사용할 때도 락 자체만으로는 완벽한 안전성을 보장하지 못합니다. 락은 '누가 지금 자원에 접근할 권한이 있는가'만 알려줄 뿐, 실제로 뒤늦게 도착한 요청을 막아주지는 않기 때문입니다. Fencing Token은 자원을 실제로 다루는 백엔드(데이터베이스, 스토리지 서버 등)가 요청의 순서를 스스로 검증할 수 있게 해주는 마지막 방어선 역할을 합니다.

  • 락 서버 장애나 지연 상황에서도 오래된 요청이 최신 상태를 덮어쓰는 사고를 방지
  • 토큰 비교 로직만 추가하면 되므로 구현 비용이 크지 않음
  • 분산 락을 '신뢰'가 아니라 '검증 가능한 순서'로 다룰 수 있게 해줌

따라서 실무에서 분산 락을 설계할 때는 락 획득 여부뿐 아니라, 그 락이 실제로 유효한 순서인지 자원 쪽에서 재확인하는 Fencing Token 로직을 함께 고려하는 것이 안전합니다.

댓글 남기기