보관소 둘, 그리고 넘겨주기
Avatars가 요청한 이미지는 전부 Ember에서 태어나. Ember가 원본과 Pro 기록 행을 먼저 저장해. 아키텍처 문서가 Ember를 생성물의 1차 저장소라고 부르는 이유야. Avatars는 그 뒤에 단단한 사본을 하나 더 들고 있어. Ember가 기록을 정리하더라도 아빠가 고른 아바타가 사라지는 일은 없게 하려는 거야. 같은 바이트를 보관소 둘이 들고 있으면 질문이 하나 생겨. 첫 번째 보관소는 언제 손을 놔도 될까?
Ember는 보호 표시로 답해. Avatars는 잡마다 purpose: "exploration"과 source_app: "cwkAvatars"를 달아. Ember 문서는 탐색 결과를 쓰는 쪽을 두고 짧게 설명해. 새 탐색 결과는 보호된 채로 시작해. 쓰는 쪽이 실제 바이트를 단단하게 복사하고, PATCH /api/v1/explorations/{id}/protection에 {"protected": false, "durable_copy": true}를 보내 확인해 줄 때까지 그 보호가 유지돼. 제출할 때 메타데이터로 복사하겠다고 약속해도 풀리는 건 없어. Ember의 앱별 만료는 누가 그 앱에 켜기 전까진 꺼져 있어. 보존 정리와 수동 정리도 이름을 댄 앱의 이미지 중에서 보호가 풀린 것만 골라.
그러니까 Avatars 쪽 규칙은 순서의 문제야. 호출 위의 주석이 그대로 말해. 원본 바이트와 메타데이터가 둘 다 Avatars에 단단히 자리 잡은 뒤에야 보호를 푼다고.
finish() 안의 순서
끝난 잡이 Avatars 자산이 되는 곳이 finish()야. 한 단계씩 보면 이래.
- 이 작업이 이미 만든 자산이 있는지 찾아. 있으면 바이트는 이미 안전하고, 기껏해야 Ember 기록 id만 채워 넣어. 두 번째 호출이 다시 받아오거나 다시 저장하는 일은 없어.
- 없으면 바이트를 받아와. 킷의 클라이언트로 Ember의 이미지 파일 경로에서 가져와.
- 원본을 저장해.
save_asset은 바이트를 이미지로 열어서 진짜 이미지인지 확인하고, SHA-256으로 해시를 떠서 그 해시를 이름으로 써. 새로 만들 때만 열리는 임시 파일에 쓰고, 비우고,fsync하고, 원자적인os.replace로 제자리에 옮겨. 같은 이름의 파일이 이미 있으면 해시가 여전히 맞아야 해. 이어서 피사체, 레퍼런스, 작업 id, 배치 id, 공급자, 모델, 프롬프트를 담은 자산 행이 SQLite에 들어가. - 이제야 놓아줘. 킷의
release가 PATCH를 보내고, 200일 때만True를 돌려줘. - 결과를 적어. 작업은
done이 되고, Ember가 기록 id를 끝내 안 줬다면history_missing이 돼. 놓아준 결과는 작업에protection_released로 남아.
원본을 왜 해시 주소로 한 번만 쓰는지는 트랙 2에서 다뤄. 여기서 볼 건 4단계의 자리야.
확인 실패는 안전한 실패야
단계마다 죽으면 뭐가 남는지 봐. 3단계가 끝나기 전에 엔진이 죽으면, Ember엔 보호된 이미지가 있고 Avatars엔 아무것도 없어. 작업은 Ember의 답을 저장한 채 copying이니까, 다음 시작 때 아무것도 새로 만들지 않고 다시 복사해. 3과 4 사이에서 죽으면 양쪽 다 이미지를 들고 있고, Ember 쪽은 보호된 채로 있어. 작업이 여전히 copying이라 다음 시작 때 finish()가 다시 돌고, 저장된 자산을 찾은 다음 그때 놓아달라고 해. PATCH 자체가 실패하면 release가 전송 오류를 잡고 False를 돌려줘. 절대 예외를 던지지 않으니까, 네트워크가 흔들려도 저장된 이미지가 실패한 작업으로 바뀌진 않아. 근데 그 False는 확인을 못 받았다는 뜻이지, 아직 보호돼 있다는 뜻이 아니야. Ember가 PATCH를 이미 적용했는데 답이 오다가 연결이 끊기면, 보호는 벌써 풀렸는데도 False가 와. 킷의 독스트링도 호출 위 주석도 확인이 실패하면 보호된 채로 남는다고 적어 놨지만, 그건 흔한 경우일 뿐 보장은 아니야. 여기선 그래도 손해가 없어. 4단계에 오기 전에 Avatars가 이미 단단한 사본을 들고 있거든.
순서를 뒤집으면 같은 실패가 전혀 다르게 보여. 먼저 놓아주고 복사 전에 죽으면, 하나뿐인 원본이 Ember 정리 대상이 되는데 Avatars엔 아무것도 없어. 지금 순서라면 무슨 실패가 나든 원본은 Avatars에 있거나, Ember에서 아직 보호돼 있거나, 둘 다야. 치르는 값은 기껏해야 Ember에 남는 사본 하나야. 반대 순서의 최악은 누군가 돈을 내고 만든 이미지를 잃는 거고.
Avatars엔 실패한 놓아주기를 정해진 때에 다시 시도하는 장치가 없어. 표시는 작업에 남고, 이미지는 아마 Ember에서 보호된 채로 있을 거야. 그 작업의 finish()가 다음에 돌 때(기록 복구 같은 때) 다시 요청할 뿐이야.
history_missing: 바이트는 안전하고, 기록은 아니야
Ember는 이미지를 저장해 놓고도 Pro 기록 행 쓰기에 실패할 수 있어. 그럴 땐 잡을 실패시키는 대신 이미지 옆에 history_error를 붙여 알려줘. Avatars는 원본을 그대로 지키고, 작업을 history_missing으로 표시해. 단계 문구는 Original saved; history needs repair야. 원본은 저장됐고 기록은 고쳐야 한다는 뜻이야. 작업은 Ember의 오류 문장도 같이 들고 있어.
이 상태가 막는 건 딱 하나, 발행이야. 고른 이미지 중에 Ember 기록 id가 없는 생성 이미지가 있으면 세트 발행이 거절돼. 생성 이미지를 발행하려면 Ember 기록부터 복구하라는 거야. 복구는 대조를 거쳐(앞 레슨). 이 작업 id를 단 기록 행이 Ember에 생기면 검색이 그걸 찾고, finish()가 다시 돌아. 이번엔 자산이 이미 있으니 기록 id만 적어. 테스트는 finish()를 두 번 불러. 처음엔 기록 행 없이, 두 번째엔 기록 행과 함께. 그리고 Ember 이미지 파일을 딱 한 번만 받아왔는지 확인해. 아키텍처는 이 경우를 이렇게 정리해. 기록 삽입에 실패한 보관 이미지는 고칠 수 있는 메타데이터 작업일 뿐, 생성을 한 번 더 청구해도 된다는 허가가 아니라고.