튜토리얼
hiapi /v1/tasks 작업 중단 또는 시간 초과: 진단 및 재시도 매뉴얼
개발자를 위한 실전 매뉴얼: 멈춘 비동기 작업 디버깅, 폴링 주기 조정, 그리고 언제 재시도하고 언제 다시 제출할지 결정하기
비동기 작업이 중단되고 시간 초과되는 이유
/v1/tasks의 비동기 작업은 세 주체 간의 계약입니다: 당신의 클라이언트, HiAPI 플랫폼, 그리고 실제로 그 작업을 수행하는 컴퓨팅 리소스. 중단은 거의 항상 어느 한 계층이 다른 계층을 기다리고 있기 때문에 발생하며, 디버깅의 첫걸음은 어느 계층인지 파악하는 것입니다—각 계층의 해결법이 다르기 때문에, 무작정 재시도하면 상황만 더 나빠집니다.
머릿속에 새겨야 할 생명주기는 다음과 같습니다:
생명주기, 단계별 설명
- 제출(Submit)—클라이언트가 요청을 보내고, 플랫폼이 이를 받아 작업 식별자를 반환합니다.
- 큐 진입(Enqueue)—작업이 스케줄링됩니다. 이 단계는 고부하에서 가장 느려지기 쉽고, 당신에게 가장 잘 보이지 않는 단계입니다.
- 실행(Run)—컴퓨팅 리소스가 해당 작업을 처리합니다. 여기서의 소요 시간은 전적으로 워크로드에 달려 있으며 당신의 HTTP 클라이언트와 무관합니다.
- 완료(Complete)—종료 상태가 기록되고, 같은 작업 식별자로 결과를 읽을 수 있습니다.
“중단”은 단일 사건인 경우가 드물며, 보통 다음 중 하나에 해당합니다:
- 작업이 실제로 여전히 실행 중입니다. 긴 입력, 큰 출력 또는 혼잡한 큐가 3단계를 늘립니다. 아무것도 고장 나지 않았고, 당신이 너무 조급했을 뿐입니다.
- 작업이 큐에 들어갔지만 아직 시작되지 않았습니다. 2단계는 매우 길어질 수 있으며, 당신 쪽에서는 중단과 똑같이 보입니다.
- 클라이언트가 너무 일찍 포기했습니다. 작업이 아니라 상태 조회에 HTTP 시간 초과를 설정해서, 플랫폼은 멀쩡한데 당신의 프로세스가 중단된 경우입니다.
- 작업이 종료 상태에 들어갔지만 당신이 감지하지 못했습니다. 폴링 루프가 답을 얻기 전에 멈추거나, 크래시하거나, 종료되었습니다.
- 진짜 플랫폼 측 장애. 더 드물지만 실제로 존재하며, 플랫폼 수준 재시도를 떠올려야 하는 유일한 경우입니다.
핵심 오해
가장 흔한 자초한 시간 초과는 두 개의 시계를 혼동하는 것입니다. 상태를 조회하기 위한 HTTP 요청은 짧아야 합니다—그것은 단순한 읽기입니다. 작업은 그 자체로 훨씬 긴 마감 시간을 가집니다. 두 가지에 같은 시간 초과 값을 쓰면, 건강한 작업을 취소한 뒤 다시 재시도하여 거의 끝난 작업을 중복시키게 됩니다.
무엇이든 바꾸기 전에, 당신이 실제로 위 다섯 가지 상황 중 어디에 있는지 먼저 판정하세요. 이 매뉴얼의 나머지 내용은 모두 이 답에 달려 있습니다.
1단계: 작업 증거 수집
1분 만에 코드를 고치거나 재시도하고 싶은 충동을 참으세요. 중단은 충분한 정보를 기록한 뒤에야 사후 디버깅할 수 있는 상태입니다. 개입하기 전에 다음을 확보하세요.
작업마다 기록해야 할 것
- 제출 시 반환된 작업 식별자. 이것이 모든 것의 열쇠입니다; 이것이 없으면 추측만 가능합니다.
- 마지막으로 관찰한 상태 문자열, 그대로 기록하고 의역하지 마세요.
- 타임스탬프: 언제 제출했는지, 각 상태 조회가 언제 나갔는지, 각 조회가 언제 돌아왔는지.
- 실행한 상태 조회 횟수와 그 간격.
- 요청 페이로드의 형태—전체 내용일 필요는 없지만 재현에 충분해야 합니다: 입력 규모, 파라미터, 실행 시간에 영향을 주는 모든 것.
- 설정한 클라이언트 측 마감 시간(있는 경우), 그리고 그것이 발동했는지 여부.
같은 식별자로 수동 재조회 한 번
어떤 결론을 내리기 전에, 같은 작업 식별자로 새로운 상태 조회를 한 번 수행하세요. 두 가지 결과가 나옵니다:
- 상태가 진행되었습니다. 작업은 살아 있습니다; 당신이 겪은 것은 작업 버그가 아니라 폴링 또는 시간 초과 버그입니다. 폴링 섹션으로 넘어가세요.
- 상태가 변하지 않았고 작업이 이미 오래되었습니다. 이제 진짜로 멈춘 것으로 의심되는 작업이 생겼습니다. 해당 상태에 얼마나 오래 머물렀는지 기록하고 기대치와 비교하세요.
기록으로 남기기
시도마다 짧은 로그 한 줄을 남기세요: 식별자, 상태, 경과 시간, 내린 결정. 반복되는 중단은 거의 항상 실시간 사건이 아니라 이력 기록으로 진단됩니다. 한 시간 전에 어떤 작업이 무엇을 했는지 복원할 수 없다면, 같은 장애를 계속 디버깅하게 될 것입니다.
잊기 쉬운 증거가 하나 더 있습니다: 하루 중 시간과 워크로드 유형. 중단이 특정 입력 유형에 집중된다면, 당신이 발견한 것은 재시도할 간헐적 장애가 아니라 보고할 가치가 있는 패턴입니다.
작업 상태를 올바르게 해석하기
상태는 모든 재시도 결정의 사실 근거입니다. 이를 작은 상태 기계로 취급하고, 각 상태가 정확히 하나의 행동에 대응하게 하면, 여전히 실행 중인 작업을 결코 재시도하지 않게 됩니다.
보게 될 상태와 그 의미
- Queued / pending(대기 중/처리 대기)—접수됨, 시작 안 됨. 여기서 오래 머물면 스케줄링 압박을 의미합니다. 재시도하지 마세요; 작업은 이미 존재합니다.
- Running / processing(실행 중/처리 중)—컴퓨팅 리소스가 작업 중입니다. 아무 문제 없습니다. 기다리는 것이 올바른 행동입니다.
- Succeeded / completed(성공/완료)—종료 상태. 결과를 읽고 마무리하세요.
- Failed / error(실패/오류)—종료 상태이며, 그 오류 페이로드가 최고의 디버깅 자료입니다. 어떤 결정을 내리기 전에 먼저 읽으세요.
- Cancelled / expired(취소됨/만료됨)—정책 또는 당신의 조작으로 인한 종료 상태. 재시도 여부는 완전히 새로운 결정입니다.
- Unknown 또는 누락—식별자를 인식할 수 없음. 이것은 실패 상태가 아니라 증거 상태입니다. 식별자를 다시 읽고 올바른 것을 보고 있는지 확인하세요.
어떤 상태든 물어야 할 두 가지 질문
- 종료 상태인가? 아니라면, 유일하게 올바른 행동은 당신의 예산 안에서 계속 기다리는 것입니다.
- 종료 상태라면, 재시도 가능한가? 성공은 재시도할 수 없습니다. 실패는 재시도 가능할 수도 있고 아닐 수도 있습니다—먼저 오류를 읽으세요. 알 수 없는 식별자는 재시도가 아니라 조사가 필요합니다.
경과 시간으로 상태를 추론하지 마세요
“10분째 돌고 있으니 분명 멈춘 거야”는 진단이 아니라 추측입니다. 경과 시간은 언제 조회할지만 알려줄 뿐, 상태가 무엇인지는 결코 알려주지 않습니다. 작업은 큐에서 오랜 시간을 보낸 뒤 완벽하게 완료될 수 있습니다. 결론은 상태 문자열이 내리게 하고, 당신의 시계는 얼마나 자주 물어볼지만 결정합니다.
합리적인 폴링 주기 설계하기
대부분의 “중단”은 위장된 폴링 버그입니다. 크게 두 가지 실패 모드가 있습니다: 폴링이 너무 공격적이어서 오히려 자체 신뢰성을 해치는 경우, 그리고 폴링이 너무 순진해서 종료 상태를 영원히 감지하지 못하는 경우. 유계 주기는 이 둘을 동시에 해결합니다.
느리게 시작하고, 완만하게 백오프하기
타이트한 루프에서 폴링하지 마세요. 먼저 짧은 간격으로 빠른 작업이 빨리 결과를 내게 한 뒤, 작업이 오래될수록 간격을 늘리세요:
import time
def poll(job_id, check_status, deadline_s=600):
start = time.monotonic()
interval = 1.0
while True:
status = check_status(job_id)
if status in ("succeeded", "failed", "cancelled", "expired"):
return status
if time.monotonic() - start > deadline_s:
return "client_deadline_exceeded"
time.sleep(interval)
interval = min(interval * 1.5, 30.0) # 상한 설정
핵심 속성: 초기 간격이 빨리 끝나는 작업을 잡을 만큼 짧고, 증가 계수가 긴 작업을 저렴하게 유지하며, 한 시간을 잠들어 결과를 놓치는 일이 없게 하는 상한이 있다는 점입니다.
항상 클라이언트 측 마감 시간을 설정하세요
플랫폼이 대신 완료 신호를 보내줄 것이라고 절대 가정하지 마세요. 당신의 마감 시간—당신이 기꺼이 기다릴 최대 시간—을 설정하고, 그것을 초과하는 것을 실패가 아닌 독립적인 결과로 취급하세요. 이 구분은 중요합니다: client_deadline_exceeded 결과는 “나는 더 이상 보지 않는다”이지 “작업이 죽었다”가 아닙니다. 그에 맞게 기록하세요.
두 가지 실용 규칙
- 총 조회 횟수를 제한하세요. 최대 반복 횟수가 없는 루프는 중단이 일어나길 기다리는 것입니다. 마감 시간과 간격 상한을 기준으로 계산하세요.
- 읽기 시간 초과와 작업 마감 시간을 분리하세요. 상태를 가져오는 HTTP 호출에는 자체적인 짧은 시간 초과가 있어야 합니다. 상태 읽기가 시간 초과되면 이 읽기를 재시도하세요—절대 작업을 재시도하지 마세요.
엔드포인트를 존중하세요
폴링은 저렴하지만 무료는 아닙니다. 수백 개의 작업을 확인해야 한다면, 이를 분산시키고 같은 클라이언트를 재사용하세요. 확신이 없을 때는 더 느린 쪽이 거의 항상 안전한 방향입니다: 6분 기다릴 가치가 있는 작업은 6분 30초 기다릴 가치도 있습니다.
재시도와 다시 제출 사이에서 결정하기
이것은 정확성과 비용이 충돌하는 결정입니다. 같은 작업을 재시도하는 것과 새 작업을 제출하는 것은 상호 교환 가능하지 않으며, 잘못 선택하면 작업을 중복시키거나 건강한 작업을 방치하게 됩니다.
결정 규칙
순서대로 하나씩 처리하세요:
- 작업이 종료 상태인가? 아니라면 재시도도 다시 제출도 하지 마세요. 기다리세요. 아직 실행 중인 작업을 재시도하면 작업을 중복시킵니다.
- 종료 상태라면, 상태가 무엇을 말하는가? 성공은 당신이 끝났다는 뜻입니다. 실패에는 오류 페이로드가 있으니—먼저 읽으세요.
- 그 실패는 일시적일 수 있는가? 고부하에서의 시간 초과, 일시적 불가용, 속도 제한—이들은 모두 재시도 후보입니다.
- 그 실패는 영구적일 수 있는가? 잘못된 입력, 지원되지 않는 파라미터, 형식이 잘못된 페이로드—몇 번을 시도하든 똑같이 실패합니다. 요청을 수정하세요.
다음의 경우 같은 작업을 재시도하세요…
작업이 존재하고, 다시 나타나지 않을 수 있는 이유로 종료 상태 실패에 들어갔으며, 플랫폼의 해당 작업 시맨틱이 한 번 더 시도하는 것을 허용합니다. 식별자를 재사용하고, 이력 기록을 유지하며, 같은 일을 하는 두 번째 작업을 만들지 않습니다.
다음의 경우 새 작업으로 다시 제출하세요…
- 식별자가 알 수 없거나 더 이상 획득할 수 없음—재시도할 대상이 없습니다.
- 원래 작업이 정책에 따라 명확히 만료되어 재시도가 붙을 곳이 없습니다.
- 입력을 변경해야 합니다. 재시도는 같은 요청이고, 수정은 새 요청입니다. 재시도가 원래 요청과 다른 의미를 갖게 해서는 절대 안 됩니다.
비용의 비대칭성
실행 중인 작업에 대한 재시도 한 번은 한 결과의 작업량을 두 배로 만듭니다. 다시 제출은 명확한 새 작업—새 식별자, 새 생명주기, 새로운 비용. 둘 다 정당한 도구이며, 둘 다 신중하게 사용해야 합니다. 경험 규칙: 인프라가 불확실하면 재시도하고, 입력이 확실하면 다시 제출하세요.
재시도를 안전하게 만들기
조용히 작업을 중복시키는 재시도는 중단보다 나쁩니다. 왜냐하면 소리 없이 실패하고 당신의 비용을 발생시키기 때문입니다. 필요해지기 전에 안전성을 내장하세요.
멱등성의 관점으로 사고하세요
실행하는 모든 작업 유형에 대해 물어보세요: 두 번 실행해도 결과가 여전히 올바른가? 답이 아니라면—예를 들어 그 작업이 레코드 한 줄을 추가하거나 하류 부수 효과를 유발한다면—재시도 로직은 반드시 상태를 기준으로 문턱을 두어야 하며, 타이머를 절대 쓰면 안 됩니다. 종료 상태 실패만, 그리고 새로운 상태 조회가 그 상태를 확인한 뒤에만 재시도하세요.
시도 횟수를 제한하세요
모든 재시도 루프에는 최대 시도 횟수와 최대 총 소요 시간이 필요하며, 둘 다 명시적으로 검사해야 합니다. 무한 재시도 루프는 더 느리고 돈만 더 드는 중단일 뿐입니다. 예산이 소진되면 실패를 드러내세요—조용히 삼키지 마세요.
시도 사이에 백오프를 넣으세요
재시도 시도 자체도 간격을 두고, 실패할 때마다 늘어나야 합니다. 이미 고장 난 의존성에 즉시 재시도하는 것은 보통 또다시 실패하고, 이미 힘든 시스템에 부하를 더할 뿐입니다.
오류뿐 아니라 시도를 기록하세요
매 시도마다 기록하세요: 작업 식별자, 결정 시점의 상태, 시도 순번, 사용한 간격, 결과. 이것이 “가끔 중단돼요”와 “화요일 오후에 큰 입력을 처리할 때 중단돼요”의 차이입니다. 후자는 고칠 수 있고, 전자는 고칠 수 없습니다.
진짜 실패를 절대 가리지 마세요
작업이 세 번 실패했는데 당신의 래퍼가 포괄적인 오류만 반환한다면, 이유를 설명하는 오류 페이로드를 버린 것입니다. 마지막 상태와 오류 세부 정보를 호출자까지 보존하세요. 재시도는 복구 메커니즘이지 은폐 메커니즘이 아닙니다—근본 문제가 드러날 기회를 줄이는 것이 아니라 늘려야 합니다.
반복되는 중단을 위한 사후 점검 체크리스트
중단이 한 번 발생하면, 현재 작업만 고치면 됩니다. 두 번 발생하면, 이 체크리스트를 실행하세요—그렇지 않으면 영원히 재시도 비용만 치르게 됩니다.
체크리스트
- 사건을 분류하세요. 관찰을 멈춘 실행 중 작업인가, 진짜 멈춘 작업인가, 클라이언트 시간 초과인가, 읽지 못한 상태인가? 이것을 명확히 말할 수 있으면 절반은 해결된 것입니다.
- 폴링 계산을 점검하세요. 간격 상한, 마감 시간, 시도 상한이 실제로 작동하는가? 무한 루프가 있는가?
- 시간 초과가 분리되어 있는지 확인하세요. 상태 읽기 시간 초과가 작업 마감 시간과 혼동된 적이 있는가?
- 재시도 결정을 재검토하세요. 비종료 상태 작업에 재시도한 적이 있는가? 실행 중인 작업을 다시 제출해 중복시킨 적이 있는가?
- 실패 페이로드를 확인하세요. 오류가 보존되었는가, 래퍼에 삼켜졌는가?
- 입력 패턴을 찾으세요. 실패가 페이로드 크기, 파라미터 또는 워크로드 유형별로 군집되는가?
- 기대치를 재검토하세요. 작업이 마감 시간을 초과한 것이 실제로 그만큼 큰 작업이기 때문인가? 그렇다면 버그는 마감 시간에 있습니다.
발견을 기본 설정으로 전환하기
위 항목 각각은 당신의 클라이언트에서 기본 관행으로 전환되어야 합니다: 유계 폴러, 독립적인 마감 시간 결과, 상태 기반 재시도 정책, 그리고 작업 식별자를 키로 한 구조화 로그. 이것을 갖추면 다음 중단은 오후 하나를 잡아먹는 일이 아니라 로그의 한 줄이 됩니다.
비동기 작업은 계약이며, 그 상태를 존중하는 한 계약은 성립합니다. 여전히 실행 중인 것과 영원히 완료될 수 없는 것을 구분하고, 유계 주기로 기다리며, 안전하게 재시도할 수 있는 것만 재시도하세요—그러면 중단은 장애가 아니라 사소한 성가심으로 남습니다.
관련 글
모든 아티클 보기 →-
튜토리얼
HiAPI API로 hailuo-2.3-fast/image-to-video 호출하는 방법: curl과 Python 실전
빠르고 저렴한 이미지-투-비디오 등급의 첫 실행 요청 — 모델 ID부터 MP4 다운로드까지
-
튜토리얼
HiAPI API로 hailuo-2.3/text-to-video를 호출하는 방법: curl, Python 및 실행 가능한 요청 예시
-
튜토리얼
HiAPI에서 Kandinsky 6.0 Pro 텍스트-투-비디오 사용하기: curl 및 Python 튜토리얼
요청 우선 실전 데모: 프롬프트에서 비디오 클립까지의 호출 구성, 요청 제출, 완료 상태 폴링 및 결과 다운로드