"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 을 둘 다 atomic 하게 queued 로 되돌려. 트랜잭션 하나, 행 두 개, 전부 아니면 전무. 이 단어가 중요해. 한 행을 되돌리고 다른 행 차례가 오기 전에 크래시하는 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 은 예상 못 한 크래시 창을 다뤄.