C.W.K.
Stream
Lesson 02 of 05 · published

만료 lease reclaim

~11 min · reclaim, atomicity, reconciliation, orphan-state

Level 0Empty Shelf
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"claim 이 건드린 건 뭐든, reclaim 이 되돌려야 해 — 전부, atomic 하게."

heartbeat 가 멈춘다

worker 가 전사 중에 죽어: 정전, 크래시, 누가 기계를 재시작. heartbeat 가 멈춰. lease 가 다 돼. 이제 control plane 이 치워야 하고, 여기가 미묘하고 잔인한 버그가 사는 곳이야.

뻔한 정리는: lease 만료 → job 을 queued 로 되돌려 다른 worker 가 claim 하게. Recall 은 정확히 그렇게 하고, 새 claim 이 허락되기 전에 해서, stale lease 가 절대 이중 claim 안 되게. 근데 'job 을 queued 로 되돌리기' 는 이야기의 절반일 뿐이고, 빠진 절반이 무는 놈이야.

claim 이 둘을 건드렸으니, reclaim 도 그래야 해

claim 이 뭘 했는지 기억해: job 을 queued 에서 leased 로 옮겼고, 그리고 그 job 의 batch item 을 queued 에서 transcribing 으로 옮겼어. 두 행이 바뀌었어. 그래서 reclaim 이 job 만 리셋하면, batch item 은 영영 'transcribing' 이라 말하며 남아 — 아무도 전사 안 하는데. queue 는 기술적으로 맞고(job 이 돌아왔고, 다른 worker 가 가져감) batch 의 진행 뷰는 영구히 거짓말해: 영원히 진행 중인 item, 허구에서 계산된 ETA, 살아 있어 보이는데 아닌 batch 를 노려보는 operator.

그래서 Recall 의 reclaim 은 job 이랑 그 batch item 을 둘 다 queued 로 atomic 하게 되돌려 — 트랜잭션 하나, 두 행, 전부 아니면 전무. 그 단어가 중요해: 한 행을 리셋하고 다른 것 전에 크래시하는 reclaim 은 자기가 치우던 바로 그 orphan 을 다시 만들어. 일반 규칙은 대칭이야: claim 이 변형하는 상태는 뭐든, reclaim 이 단일 atomic 단계로 복원해야 해. 부분 reclaim 은 더 작은 고침이 아냐 — 정리의 옷을 입은 새 불일치야.

그리고 틈새로 크래시한 걸 치유해

atomic reclaim 이 있어도 틈 하나가 남아: 'job 완료' 와 'batch item 갱신' 사이 좁은 창에 떨어지는 크래시. job 은 진짜 끝났는데, 그 item 은 여전히 transcribing 으로 읽혀 — 그리고 그걸 고칠 lease 만료가 없어, job 이 더는 leased 가 아니니까. 그래서 Recall 은 두 번째 안전망을 더해: 시작 시랑 job claim 시, reconcile 해 — batch item 이 중간에 남은 완료 job 을 찾아 수리해. 이게 reconciliation-loop 본능이야: 모든 전이가 완벽히 짝지어졌다고 믿는 대신, 주기적으로 현실을 기록이 주장하는 것과 비교하고 차이를 치유해. lease 는 예상한 크래시를 다루고; reconciliation 은 예상 못 한 크래시 창을 다뤄.

Code

두 행을 atomic 하게 reclaim, 그다음 크래시 창을 치유·python
# claim 이 두 가지를 변형했다:
#   job:  queued -> leased
#   item: queued -> transcribing

# 그러니 reclaim 은 둘 다, 한 트랜잭션에서 복원해야 한다.
with db.transaction():                 # 전부 아니면 전무
    job.status = 'queued'
    job.lease_token = None
    item.status = 'queued'             # <-- 이걸 잊으면 item 이
                                       #     worker 도 없이 영영
                                       #     'transcribing' 이라 함

# reclaim 은 새 claim 이 허락되기 전에 돌아서, 만료된
# lease 가 절대 이중 claim 되지 않는다.

# 'job 완료' 와 'item 갱신' 사이 크래시 창의 안전망:
# 시작 시와 claim 마다 reconcile.
for job in db.completed_jobs_with_item_in_transcribing():
    repair(job.batch_item)   # 현실 vs 기록 -> 틈 치유

External links

Exercise

네 시스템의 claim/lock/checkout 을 찾아 그게 변형하는 모든 행이나 field 를 나열해 — 주 record, 그리고 딴 데의 status, counter, 진행 field. 이제 정리 경로를 쓰고 확인해: 그 전부를, 단일 트랜잭션에서 복원해? 그다음 네 atomicity 가 못 덮는 크래시 창을 찾고, 시작 시 그걸 치유할 reconciliation pass 를 스케치해.
Hint
두 질문이 버그를 찾아: (1) claim 이 쓰는 전부를 나열하고, 해제 경로가 그 전부를 atomic 하게 되돌리는지 확인해 — 흔한 누락은 다른 테이블의 진행/status 행이야. (2) 같은 트랜잭션에 없는 두 전이 사이에서 프로세스가 죽으면 어떻게 될지 물어. 아무도 안 알아챌 거면, 현실을 기록과 비교하는 reconciliation pass 가 필요해.

Progress

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

댓글 0

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

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