리더가 둘이었다 · 타이머가 같으면 아무도 이기지 못한다 · 실습
리더를 뽑는 규칙을 직접 쓴다
목표
세 개의 프로세스가 서로 표를 주고받아 리더 하나를 뽑는 과정을 직접 구현합니다.
끝나면 임기(term), 투표, 과반, 그리고 타임아웃을 왜 무작위로 두는지를 손으로 겪은 상태가 됩니다.
왜 중요한가
합의 알고리즘이 어려운 이유는 수식이 아니라 경계입니다. 임기가 같을 때와 클 때,
아직 투표하지 않았을 때와 이미 했을 때, 표가 절반일 때와 절반을 넘었을 때 — 이 경계를
한 칸씩 잘못 잡으면 시스템은 평소에 멀쩡히 돌다가 네트워크가 흔들리는 날 두 개의 리더를
만들어 냅니다. 그래서 여기서는 배선(HTTP 서버·타이머·재전송)을 미리 드리고 **판단하는
함수만** 여러분이 씁니다. 판단이 곧 알고리즘이고, 나머지는 배관입니다.
마지막 단계는 Raft 논문이 무작위 타임아웃을 도입한 이유를 실측으로 보여 줍니다.
세 노드의 타이머를 똑같이 맞춰 두면 셋이 동시에 후보가 되어 각자 자기에게 한 표씩 주고,
아무도 과반에 닿지 못한 채 임기만 끝없이 올라갑니다.
단계
1. /root/raft/cluster.json 에 세 노드의 id 와 포트를 적습니다.
2. /opt/lab/raft/node.py 를 /root/raft/node.py 로 복사하고 하나를 띄워 /status 를 확인합니다.
3. /root/raft/raftrules.py 에 election_timeout_ms(node_id, fixed_ms) 를 씁니다.
4. 같은 파일에 on_timeout(state) 를 더합니다.
5. 같은 파일에 on_request_vote(state, req) 를 더합니다.
6. 같은 파일에 on_append_entries(state, req) 를 더합니다.
7. 같은 파일에 has_majority(votes, total) 을 더하고, 세 노드를 띄워 리더가 뽑히는 것을 봅니다.
8. 세 노드를 --timeout-ms 900 으로 띄우고 결과를 /root/raft/split-vote.json 에 적습니다.
참고
- 노드를 띄우는 법:
cd /root/raft && python3 node.py --id 1 --port 5001 --peers 2:5002,3:5003 & - 상태 보기:
curl -s http://127.0.0.1:5001/status - 모두 내리기:
pkill -f 'node.py --id' - 배선은 규칙 파일에 있는 함수만 부릅니다. 아직 안 쓴 함수 때문에 프로세스가 죽지는 않습니다.
- 흔한 실수 1: 낡은 임기의 요청을 받고 내 임기를 그쪽으로 내려 버리는 것. 임기는 올라가기만 합니다.
- 흔한 실수 2: 과반을
votes >= total / 2로 쓰는 것. 4노드에서 2표가 과반이 되어 리더가 둘 생깁니다.
단계 8개
- 세 노드의 자리표
- 배선을 제자리에 놓고 띄워 본다
- 선거 타임아웃
- 타임아웃이 나면 후보가 된다
- 한 임기에 한 표
- 심장박동을 받는 쪽
- 과반을 세고 리더가 된다
- 타이머가 같으면 아무도 이기지 못한다