현재 테스트 중입니다: 완료되면 GitHub 코드를 오픈할 예정입니다.

복제 명령

모든 락 상태 변경은 아래 명령 중 하나로 AppendEntries의 로그 엔트리에 실려 복제되고, 과반 커밋 후 모든 노드가 같은 순서로 적용합니다.

명령 (리더가 제안)

명령필드의미
Grantkey, lease_ms, owner, granted_at_ms키 획득. owner는 상관 ID이며 권한이 아닙니다. token은 명령에 없고, 적용 시점에 각 노드의 상태머신이 결정적으로 부여합니다
Releasekey, token현재 보유 token과 정확히 일치할 때만 키 반납
Expirekey, token리더 주도 lease 만료. token이 일치할 때만 제거 — 늦게 도착하거나 중복된 만료가 그 사이 새로 발급된 락을 지우지 못함(멱등)
Controlcontrol requestbootstrap 소모, 새 NodeId 발급, learner admission·준비·교체 완료·중단을 모든 voter에 같은 순서로 복제

적용 결과 (상태머신 → 리더)

결과의미
Granted { token }획득 성공 — 발급된 펜싱 토큰
GrantRejected이미 보유 중인 키에 Grant가 도착(방어적 — 정상 흐름에서는 발생하지 않음)
Released반납됨
NotFound반납하려는 키가 없었음
Expired { existed }만료 처리됨 — existed는 실제로 제거됐는지
BootstrapRecordedone-shot bootstrap 자격이 복제 상태에 소모됨
ReplacementPrepared / AdmissionAccepted새 NodeId·incarnation·주소에 묶인 교체가 준비됨 / join 자격이 소모됨
ControlApplied / ControlRejected교체 상태 전이 적용 / 불변 조건 불일치로 거부
Noop아무 일 없음

lease의 남은 시간은 각 노드가 로컬 시계로 추적하지만, 실제 제거는 항상 커밋된 Expire 명령으로만 일어납니다 — 노드 간 시계 차이가 락 상태를 갈라놓지 못하는 이유입니다. Grant 결과가 client writer에 전달되기 전에 연결 처리 경로가 사라졌다면 리더는 발급된 exact token으로 Release를 제안합니다. 응답 전송 여부가 불명확한 경우에는 token을 추측하거나 같은 owner로 다시 acquire하지 않으며, 클라이언트에는 Indeterminate가 되고 락은 lease 안에 만료됩니다.

Control은 일반 client 포트에서 직접 보내는 명령이 아닙니다. 별도 cluster control 인증을 통과한 leader가 유효성을 검증한 뒤 Raft log에 제안합니다. 종료된 NodeId·incarnation은 복제된 retired/issued 집합에 남아 재사용을 거부합니다.

흐름 — 만료(Expire)의 복제

리더
팔로워 1
팔로워 2
로컬 시계로 lease 만료 감지
AppendEntries · Expire (key, token)
AppendEntries · Expire (key, token)
OK
과반 커밋 → 모든 노드가 같은 순서로 적용
token 불일치면 무시 — 새로 발급된 락 보호
합의 RPC (Raft)

만료조차 리더의 제안 → 과반 커밋을 거치는 하나의 명령입니다. token 조건 덕분에 커밋이 늦어지는 사이 같은 키가 새로 발급됐더라도, 뒤늦게 적용된 Expire가 새 락을 지우지 못합니다.

용어

  • 상태머신(state machine) — 커밋된 명령을 순서대로 적용해 현재 상태(여기서는 락 테이블)를 만들어 내는 부분. 모든 노드가 같은 명령을 같은 순서로 적용하므로 항상 같은 상태에 도달합니다.
  • 커밋(commit) — 과반 노드가 받아 적어 "확정"된 상태. 커밋된 명령만 상태머신에 적용됩니다.
  • 멱등(idempotent) — 같은 명령이 실수로 두 번 적용돼도 결과가 달라지지 않는 성질.