본문 바로가기
C.W.K.
Stream
Lesson 01 of 05 · published

lease token + heartbeat

~11 min · lease, token, heartbeat, fencing

Level 0Empty Shelf
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"worker 는 job 을 소유하지 않아. 타이머 걸고, 임대가 아직 유효함을 증명하는 token 을 들고 빌려."

문제: 지금 이 job 을 누가 소유해?

긴 job 이 든 queue 하나에서 worker 여럿이 당겨 가. worker 가 둘 이상이 되는 순간 위험한 질문이 나타나. worker 둘이 같은 job 을 잡는 건 뭐가 막지? 다들 죽은 줄 알았던 worker 가 깨어나서 남한테 재배정된 job 을 짓밟는 건? 소유권이 흐릿하면 worker 둘이 같은 영상을 전사하거나, 좀비 worker 가 남이 이미 다시 돌리고 있는 job 의 결과를 제출해. '지금 이 job 은 이 worker 것이고, 다른 누구 것도 아니다' 를 확실하게 말할 방법이 필요한 거지.

atomic 하게 claim, token 으로 증명

Recall 의 답은 lease 야. job claim 은 atomic DB 트랜잭션 하나 안에서 일어나. job 이 queued 에서 leased 로 옮겨 가고, claim 한 worker 가 랜덤 lease token 을 받아. 그 token 이 소유의 증거고. 그때부터 worker 가 그 job 에 하는 모든 행위, 그러니까 heartbeat 도 실패 보고도 결과 제출도 그 token 을 내밀어야 해. control plane 이 확인해서 owner 나 token 이나 status 가 안 맞으면 거부하고. job 을 claim 한 적 없는 두 번째 worker 한테는 token 이 없고, lease 가 만료된 worker 가 든 token 은 control plane 이 더는 인정 안 해. 소유권이 신뢰의 문제가 아니라 증명의 문제가 되는 거야.

분산 락에서 말하는 fencing token 아이디어야. 그냥 '쥐고 있다' 고 여기는 락은 본인이 아직 갖고 있다고 믿는 동안 조용히 잃을 수 있거든. 그래서 안전한 패턴은 리소스 쪽이 write 할 때마다 검증하는 token 을 요구하는 거야. 멈췄다 다시 깨어난 worker 는 아직 자기가 job 을 소유한다고 믿을 수 있어. 근데 token 이 stale 이라 write 가 튕겨 나가지. 상태를 바꾸는 지점마다 확인되는 token 이 lease 를 희망 섞인 관례가 아니라 진짜 안전장치로 만들어.

heartbeat 가 lease 를 살려둬

lease 에는 일부러 시간 제한을 뒀어. Recall 은 2시간 창을 쓰지. 사라진 worker 가 job 을 영영 쥐고 있지 못하게. 근데 진짜 전사 작업에는 길게 block 되는 구간이 있잖아. 오디오 준비, provider 응답 대기. 작업 도중에 lease 가 만료되지 않게 worker 가 heartbeat 를 보내. 유료 호출 직전에, chunk 를 하나 끝낼 때마다, 그리고 백그라운드 heartbeater 를 통해 길게 block 되는 작업 중에도. heartbeat 가 갈 때마다 lease 가 갱신되고. heartbeat 가 온다는 건 '나 살아 있고 아직 일하는 중' 이라는 뜻이고, 안 온다는 게 control plane 이 job 을 되찾을 때 쓰는 신호야(다음 레슨). 그래서 lease 는 살아 있는 거야. token 으로 쥐고, heartbeat 로 온기를 유지하고, worker 가 자기가 아직 거기 있다는 걸 더는 증명 못 하는 순간 놓는 거지. 스스로 놓든 timeout 으로 놓든.

Code

트랜잭션에서 claim, 그다음 token 으로 소유 증명·python
# Claim: atomic 전이 + 이 worker 에 묶인 랜덤 token.
with db.transaction():
    job = db.claim_next_queued(worker_id)   # queued -> leased
    token = random_token()
    job.lease_token = token
    job.lease_expires_at = now_plus(hours=2)

# 이후 모든 행위가 token 을 제시해야 함. 불일치 -> 거부.
control_plane.heartbeat(job.id, worker_id, token)   # lease 갱신
control_plane.submit_result(job.id, worker_id, token, result)

# 현재 token 없는 stale/두 번째 worker 는 job 을 변형 못 한다.
# 소유권은 한 번 가정되는 게 아니라 행위마다 증명된다.

External links

Exercise

네 일에서 worker 나 cron job 이나 프로세스가 task 를 '가져다가' 작업하는 데를 찾아봐. queue 소비자, 예약 job, lock 파일 같은 거. 그리고 물어봐. 그게 둘이 돌거나, 하나가 멈췄다 다시 깨어나면, 둘 다 같은 task 를 건드릴 수 있어? lease 를 설계해. atomic claim, worker 에 묶인 token, 그리고 상태를 바꾸는 모든 행위에서 그 token 검증하기. stale 한 소유자의 write 가 이제 어디서 거부되는지 적어봐.
Hint
취약한 지점은 결과를 쓰는 순간 다시 증명되지 않는 모든 'claim' 이야. worker 가 claim 하고, lease 기간을 넘겨 멈춰 있고, 그 사이 job 이 재배정되고, 그러다 깨어나서 제출해버릴 수 있거든. 재배정된 작업을 오염시키면서. 답은 상태를 바꿀 때마다 확인되는 token, 그리고 살아 있는 동안 소유를 갱신하는 heartbeat 야. 소유권은 claim 하는 순간뿐 아니라 write 할 때마다 증명할 수 있어야 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.