돌아오지 않은 답
워커가 클립을 Ember에 보냈는데 연결이 끊겼어. Ember가 받았을까? 워커 쪽에선 알 길이 없어. 요청이 가는 길에 사라졌을 수도 있고, 도착해서 유료 렌더까지 시작했는데 답만 돌아오다 사라졌을 수도 있어. 두 장군 문제의 축소판이야. 아무리 기다려도 풀리지 않아.
이미지라면 Avatars는 조심스럽고 느리게 답해. 작업을 uncertain으로 두고, 누가 다시 시도하기 전에 Ember 기록에서 작업 id를 찾아 대조해. 그건 트랙 4에서 따라가 봤지. 클립은 더 나은 답을 받아. Ember의 Pro Video는 호출하는 쪽이 정한 멱등 키를 받거든. Ember의 기록 장부는 request_id를 유니크 제약이 걸린 칸에 저장해. Ember의 create()는 계획도 지출도 하기 전에 그 키부터 찾아봐. 같은 키가 오면 이미 만든 클립을 돌려줘.
시도에 이름 붙이기
clip_request_id()의 논리는 한 줄이야. 키는 avatars-, 작업 id, 시도 번호 순서야. 시도 번호는 작업의 시도 기록 길이에 1을 더한 값이고. 무작위인 건 하나도 없고, 작업 밖에 따로 저장하는 것도 없어. 작업 레코드만 있으면 누구든 키를 다시 계산할 수 있어. 방금 다시 켜진 워커한테 딱 필요한 성질이지.
키는 시도 기록이 늘어날 때만 바뀌어. 기록이 느는 곳은 두 군데야. Ember가 제출을 받아주면 워커가 받아준 클립 id를 덧붙여. 아빠가 작업을 다시 시도하면 retry()가 방금 끝난 시도를 상태와 오류까지 덧붙여. 그래서 Ember가 거절한 첫 제출은 avatars-<task>-1로 나가고, 그 뒤 재시도는 -2로 나가. Ember가 받아준 뒤 벤더가 실패한 클립은 둘 다 기록돼 있어서 재시도가 -3으로 나가. 번호가 시도 횟수를 정확히 세는 건 아니야. 사람이 다시 돈을 내기로 한 순간엔 새 키가 되고, 나머지 모든 순간엔 그대로면 그걸로 충분해.
입력 먼저, 요청은 하나
뭘 만들기도 전에 run()은 요청한 소울이 이 작업의 피사체를 그려도 되는지 피파에게 다시 물어. 검토와 제출 사이에 어떤 피사체가 보호 대상이 될 수도 있으니까. 레퍼런스는 하나씩 Ember의 비디오 입력 저장소(POST /api-video/inputs)로 올라가. 돌아올 땐 역할이 붙은 입력 id가 돼. 본문엔 키, 확정된 생성기, 프롬프트, 확정된 설정, 그 입력들이 들어가. 검토 때 이미 받은 동의라서 confirm: true도 붙어. source_metadata엔 배치, 작업, 시도 번호, 등장하는 소울들이 적혀. 워커는 시각과 함께 submitting을 먼저 기록하고, 그 뒤에야 보내.
답은 셋, 그중 하나는 다시야
| 돌아온 것 | 이미지 작업 | 클립 작업 |
|---|---|---|
| 4xx 거절 | failed, Submission refused | failed, Submission refused, 청구는 없어 |
| 전송 오류나 5xx | uncertain, 다시 하기 전에 대조 | queued로 돌아가. 단계 문구는 Ember가 안 닿아 다시 시도한다는 뜻의 Ember unreachable; will retry야. 10초 쉬고, 키는 그대로 |
| 202 | 잡 id와 함께 running | 클립 id와 함께 running, 시도도 기록 |
| 제출 중에 워커가 죽었을 때 | 재시작하면 uncertain | 재시작하면 queued |
핵심은 가운데 두 줄이야. 키가 있으면 모르는 결과가 더는 위험하지 않아. 같은 키로 다시 보내면 도착 못 한 클립을 만들거나 이미 만든 클립을 돌려줘. 어느 쪽이든 값을 치르는 클립은 딱 하나야. 다음 차례에 레퍼런스를 또 올리긴 해. 그건 Ember 디스크를 조금 쓸 뿐 크레딧은 안 써.
4xx는 일부러 다르게 다뤄. 거절은 청구 전에 Ember나 벤더가 안 된다고 말한 거야. 예를 들면 레퍼런스 이미지를 주면 8초 길이만 받는 Veo 모델이 있어. 거절된 요청을 다시 보내 봤자 또 거절이야. 그래서 작업은 벤더 자신의 말을 단 채 failed로 멈추고, 새 시도는 사람이 정해.
재시작 때 하는 정리
워커는 켜지자마자 파일 잠금부터 잡아. 데이터베이스 주인을 워커 하나로 못 박는 거야. 그러고 나서 지난 워커가 죽을 때 한창 진행 중이던 작업을 거둬들여. preparing에 걸린 작업은 큐로 돌아가. 대개는 아직 아무것도 안 보낸 작업이야. 클립엔 예외가 있어. 앞서 낸 제출을 Ember가 받아줬는데 답이 사라진 클립은 같은 키를 단 채 queued로 돌아갔다가, 다음 차례에 다시 preparing으로 들어와. 그래도 큐로 돌리는 건 안전해. 시도 기록이 안 늘었으니 다음에 보낼 때도 키가 같고, Ember는 이미 만든 클립을 돌려주거든. submitting에 걸린 작업은 종류에 따라 갈라. 이미지는 uncertain으로 가. 지난 제출이 Ember에 닿았을 수도 있는데 물어볼 키가 없거든. 클립은 바로 queued로 돌아가. 옆 주석이 이유를 대. 요청 id가 Ember에서 제출을 멱등하게 만든다고.
그걸 증명하는 테스트
test_a_lost_answer_or_a_restart_never_pays_twice는 위험한 길을 차례로 걸어.
- Ember가 503으로 답해. 작업은 큐로 돌아가고, 시도 기록은 비어 있고, Ember는 아무것도 안 만들었어.
- 테스트가 작업을 억지로
submitting에 두고 새 워커를 켜. 클립은 큐로 돌아갔다가 나가서 끝나. Ember가 받은 요청 id는avatars-<task>-1딱 하나고, Ember가 가진 클립도 하나야. - 두 번째 요청은 8초 길이 얘기를 단 422로 거절돼. 작업은 그 말을 단 채
failed야. - 명시적으로 다시 시도하면, Ember가 보는 요청은
-2로 끝나.