재시도에 옵션을 넘겨도 저장된 옵션이 없으면 소용없다
AI Commons의 영상 재시도는 같은 슬라이드와 보이스로 다시 생성한다. 하지만 옵션으로 빈 객체를 보내면 음성 모델과 stability 같은 값이 기본값으로 돌아갈 수 있다. 변경은 이전 VideoJob.tracksJson에서 옵션을 찾아 큐에 넘기고, 새 재시도 행에도 같은 옵션을 저장했다.
두 테스트는 옵션 객체가 든 행과 null 행 뒤에 옵션이 있는 이력을 준비했다. 큐 인자와 새 작업의 옵션 저장은 확인했지만, 워커가 그 컬럼을 나중에 어떻게 쓰는지는 포함하지 않았다. 이 변경만으로 옵션 승계가 완성됐다고 볼 수 없었던 이유다.
진행률 갱신이 생성 옵션을 통째로 교체했다
실제 createProgressUpdater는 tracksJson에 stage, currentStep, totalPages, timestamp 객체를 대입했다. 생성 시 저장한 옵션과 진행 갱신이 같은 컬럼을 공유했다. 워커를 거친 행을 재시도가 읽으면 옵션 대신 진행 정보가 들어온다.
- 생성 요청
tracksJson에 음성 설정과 pageGap을 저장한다.
- 워커의 진행 갱신
같은 컬럼을 진행 정보 객체로 바꿔 옵션을 지운다.
- 재시도
값이 있는 첫 행을 고르면 진행 정보를 옵션으로 넘길 수 있다.
입력 fixture가 이미 원하는 옵션 모양이면 재시도 함수만 검사해서는 이 충돌을 발견할 수 없다. 원인은 JSON 필드의 파싱이 아니라 두 쓰기 경로가 같은 저장 위치를 사용한 데 있었다.
진행 정보는 다른 컬럼에 쓰고 과거 행은 구분해서 읽는다
후속 변경은 새 진행 정보를 metaJson으로 옮겼다. 재시도는 최신 작업부터 확인하되 stage나 timestamp가 문자열이면 과거 진행 정보로 보고 건너뛴다. 아래는 실제 판별식이다.
return typeof obj.stage === "string" || typeof obj.timestamp === "string"; 옵션 키의 목록으로 판별하면 새 옵션을 추가할 때마다 복구 규칙도 바꿔야 한다. 진행 정보의 표지를 사용하면 그 결합을 줄일 수 있다. 다만 미래 옵션이 같은 이름을 쓰면 다시 충돌하므로 이 이름의 구분은 계속 유지해야 한다.
| 데이터 | 새 작업 | 과거 작업 |
|---|---|---|
| 생성 옵션 | tracksJson 유지 | 진행 모양을 제외하고 남은 옵션 탐색 |
| 진행 정보 | metaJson | 화면에서 tracksJson fallback |
잡 DTO는 두 컬럼을 모두 반환하고 화면은 metaJson ?? tracksJson으로 진행 정보를 고른다. 변경 당시 진행 중이던 과거 행도 표시할 수 있게 한 호환 경로다. 이미 덮어써서 사라진 옵션을 복원하는 마이그레이션은 아니다. 옵션이 남은 행이 없으면 빈 옵션으로 재큐잉한다.
쓰기 결과를 닮은 fixture가 필요한 경계다
후속 테스트는 워커의 실제 진행 모양만 남은 행에서 빈 옵션을 쓰는 경우, 진행 행을 건너뛰고 이전 옵션 행을 선택하는 경우를 추가했다. 진행 updater가 tracksJson을 쓰지 않는지, DTO가 metaJson을 반환하는지, 화면이 새 위치와 과거 위치를 모두 읽는지도 확인한다.
이 검사는 저장 위치와 호환 분기의 계약을 다룬다. 실제 TTS 음색의 동일성이나 지워진 원옵션 복구까지 증명하지는 않는다. 시도와 최종 실패의 상태 계약이 재시도 가능 여부를 정한다면, 여기서는 재시도가 어떤 입력을 이어받는지와 그 입력을 다른 쓰기가 보존하는지를 함께 봐야 했다.