워커
워커는 직접 돌리는 프로세스이며, 단계의 본문을 데몬에 제공합니다. 이 페이지는 워커가 말하는 프로토콜과, 각 호출이 왜 거기 있는지를 다룹니다.
워커란 무엇인가
워커는 가져갈 수 있는 단계들의 본문을 들고 있으면서 데몬에게 일을 달라고 묻습니다. 직접 작성한 코드가 도는 곳에서, 그 임포트와 커넥션 풀과 자격 증명을 가지고 돌아가며, 데몬은 그중 아무것도 보지 못합니다. 데몬은 렌더링된 입력과 앞선 단계들을 담은 리스를 워커에게 건네고, 워커는 본문을 돌린 뒤 어떻게 되었는지 보고합니다. 둘의 관계는 그게 전부입니다.
모든 SDK가 워커를 하나씩 제공합니다. 자기가 들고 있는 워크플로를 등록하고, 리스를 롱 폴링으로 기다리고, 본문이 도는 동안 하트비트를 보내고, 모든 거절을 그 이유별로 처리합니다. 아래 예제는 워크플로 페이지에 있는 것과 같은 프로그램입니다. 이것은 그 프로그램을 처리하는 쪽입니다.
본문 제공하기
// Serve step bodies for as long as this process lives. The worker registers
// the definition, long-polls for leases, heartbeats while a body runs, and
// answers every 409 by its reason.
const worker = new Worker(mcpd, { concurrency: 8 }).register(support)
void worker.serve() # Serve step bodies for as long as this process lives. serve() blocks, so it
# runs on a thread of its own when the process does other work too.
worker = Worker(mcpd, concurrency=8).register(support)
threading.Thread(target=worker.serve, daemon=True).start() // Serve step bodies for as long as this process lives: register, long-poll
// for leases, heartbeat while a body runs, answer every 409 by its reason.
let worker = Worker::builder(&mcpd).concurrency(8).register(support).build();
let serving = {
let worker = worker.clone();
tokio::spawn(async move { worker.serve().await })
}; // Serve step bodies for as long as this process lives: register, long-poll
// for leases, heartbeat while a body runs, answer every 409 by its reason.
worker := mcpd.NewWorker(m, mcpd.WorkerConcurrency(8)).Register(support)
go func() { _ = worker.Serve(ctx) }() // Serve step bodies for as long as this process lives: register, long-poll
// for leases, heartbeat while a body runs, answer every 409 by its reason.
val worker = Worker(mcpd, concurrency = 8).register(support)
launch { worker.serve() } # Serve step bodies for as long as this node lives: register, long-poll for
# leases, heartbeat while a body runs, answer every 409 by its reason.
{:ok, worker} = StepWorker.start_link(mcpd, workflows: [support], concurrency: 8)
:ok = StepWorker.serve(worker) 프로토콜
호출은 다섯이고, 이것들을 말할 수 있는 워커라면 SDK 없이 HTTP API를 상대로도 쓸 수 있습니다. 모든 SDK의 워커는 이 다섯의 클라이언트일 뿐, 그 이상은 아닙니다.
One lease
The daemon learns that a body is running from started, that the worker is alive from the heartbeats, and how the step ended from complete or fail. Miss the heartbeats and the lease is reclaimed; a later call about it is refused with a reason, never lost.
| 호출 | 하는 일 |
|---|---|
POST /v1/api/workers/register | 워커를 알리고 그가 들고 있는 정의를 저장합니다. 응답에는 프로토콜의 나머지가 의지하는 두 숫자, lease_ttl_ms와 heartbeat_ms가 들어 있습니다. |
POST /v1/api/steps/claim | 실행 가능한 단계를 최대 max개까지 내주고, 하나도 없으면 최대 wait_ms만큼 요청을 열어 둡니다. 데몬은 클레임을 많아야 자기 요청 시간 제한의 5분의 4, 기본값으로는 24초까지 붙들고, 붙들기를 멈추면 곧바로 응답합니다. 그래서 클레임은 wait_ms보다 먼저 빈 채로 돌아올 수 있습니다. 그때는 다시 클레임하십시오. 클레임을 실행 하나로 한정할 수도 있는데, 스크립트가 자기 실행을 끝까지 몰고 가는 방식이 이것입니다. |
POST /v1/api/steps/{lease}/started | 본문이 돌고 있음을 선언합니다. 언제나 본문보다 먼저입니다. 내준 것과 돌고 있는 것의 차이를 데몬에게 알려 주는 것이 이 호출입니다. |
POST /v1/api/steps/{lease}/heartbeat | 리스를 연장합니다. 워커가 살아 있다는 데몬이 가진 유일한 증거입니다. |
POST /v1/api/steps/{lease}/complete, /fail | 단계를 끝냅니다. 출력과 실행 상태에 대한 패치와 데몬이 값을 매길 사용량을 보내거나, 재시도 여부를 결정하는 분류가 붙은 오류를 보냅니다. |
리스
리스는 한 워커가 정해진 시간 동안 붙들고 있는 하나의 시도입니다. 리스에는 렌더링된 입력, id로 키가 붙은 앞선 단계들, 실행의 상태와 상관 정보, 이 단계의 재시도 전체에서 변하지 않는 키, 그리고 데몬이 연 단계 스팬의 트레이스 id와 스팬 id가 실려 있습니다. 단계 컨텍스트를 통해 자기 스팬을 기록하는 본문은 그 스팬 아래에 스팬을 놓으므로, 단계 안의 모델 호출이 실행의 트레이스에서 알맞은 깊이에 자리 잡습니다.
워커가 하트비트를 멈춘 리스는 만료됩니다. 데몬은 그 시도를 고아 상태로 표시하고,
회수 대기 시간이 지나면 at_least_once에서는 다시 큐에 넣고
at_most_once에서는 실패로 만듭니다. 멈췄다가 돌아와 회수된 리스에 대고
무언가를 올리는 워커는 무시당하지 않고, 그 사실을 통보받습니다.
네 가지 거절
데몬이 받아들일 수 없는 워커 호출은 네 이유 중 하나를 실은 409로 거절됩니다. 이 목록은 닫혀 있고, 모든 SDK의 워커가 각각을 이름으로 구분해 처리합니다. 넷을 한 분기로 뭉뚱그리는 워커는 이미 끝낸 단계를 버리거나 성공을 실패로 보고하기 때문입니다.
| 이유 | 뜻 | 워커가 하는 일 |
|---|---|---|
already_applied | 이 리스가 앞서 보낸 complete 또는 fail이 반영되었고, 그 응답이 유실되었습니다. | 성공으로 보고 더는 아무것도 올리지 않습니다. |
fenced | 더 나중의 시도가 있거나, 다른 리스가 이 리스를 끝냈습니다. | 그 일을 버리고 이 리스에 대해 더는 아무것도 올리지 않습니다. 펜싱된 하트비트는 컨텍스트를 통해 돌고 있는 본문도 취소합니다. |
run_terminal | 실행이 끝났습니다. 완료되었거나, 실패했거나, 시간이 다 되었거나, 취소되었습니다. | 멈추고, 그 실행에 대한 클레임도 멈춥니다. |
not_started | started가 오지 않은 at_most_once 시도에 대해 complete나 fail이 도착했습니다. | 워커 쪽 버그로 크게 보고합니다. SDK 워커는 이것을 만들어 낼 수 없고, 직접 만든 워커는 만들 수 있습니다. |
본문
본문은 컨텍스트와 렌더링된 입력과 앞선 단계들을 받습니다. 데몬이 간직해야 할 것을
보고하는 자리가 그 컨텍스트입니다. complete 시점에 최상위 키를 대체하는
방식으로 병합되는 실행 상태의 패치, 데몬이 스팬에 값을 매기는 것과 같은 장부로 값을
매길 소비 토큰, 그리고 리스를 잃거나 실행이 끝날 때 발화하는 취소 신호입니다. 그
신호를 자기 I/O에 넘긴 본문은 멈춰야 할 때 멈춥니다. 토큰은 스스로 합산됩니다.
본문이 컨텍스트를 통해 기록한 스팬과 그 아래의 모든 스팬은 끝날 때 자기 사용량을
단계의 사용량에 더하므로, 모델 호출을 스팬으로 기록하는 본문은 손으로 합을 낼 일이
없습니다. set_usage는 스팬을 하나도 기록하지 않는 본문을 위한 명시적
재정의입니다.
본문은 출력을 돌려주거나 예외를 던지며 끝납니다. 단계의 예산 안에서 재시도되게 하려면 SDK의 재시도 가능 오류를 던지고, 그 밖의 분류를 지정하려면 타입이 정해진 단계 실패를 던지십시오. 나머지는 모두 치명적입니다. 메시지는 데몬으로 전달되어 저널에 표시됩니다. 스택 트레이스는 전달되지 않습니다.
워커가 들고 있지 않은 정의, 버전 또는 단계에 대한 리스는 unservable로
실패하고 본문은 아예 돌지 않습니다. 그 시도는 재시도 예산을 쓰지 않고, 그것을 들고
있는 워커를 위해 큐로 돌아갑니다. 배포 중에 워커의 두 버전을 나란히 돌릴 수 있게
해 주는 것이 이 성질입니다.
규모 확장
데몬 하나를 상대로 워커를 원하는 만큼 돌리십시오. 각 워커는 자기 동시성 한도까지 가져가고, 데몬은 모든 시도를 정확히 그중 하나에게만 건네며, 어쩌다 두 워커가 함께 붙들게 된 리스는 펜싱이 승자 하나로 정리합니다. 워커의 id는 그 워커가 가져간 모든 시도에 기록되므로, 어느 프로세스가 어떤 단계를 돌렸는지 저널이 말해 줍니다. 프로세스 마다 이름을 주거나, SDK가 호스트에서 이름을 끌어내게 하십시오.
클레임은 롱 폴링이므로, 놀고 있는 워커 무리는 데몬에게 워커당 열린 요청 하나만큼의 비용이 들고 트래픽은 없습니다. 기다리는 동안 클레임은 실행이 시작되거나, 단계가 끝나거나, 마감 시각이 발화하거나, 이벤트가 대기를 재개할 때 일을 찾고, 그 밖에는 많아야 1초에 두 번 찾습니다. 바쁜 무리는 워커 자신의 동시성으로 한계가 정해지고, 한 시간이 걸리는 본문은 10초에 한 번의 하트비트 말고는 아무 비용도 들지 않습니다.
키
워커 호출에는 worker 스코프가 필요하고, 실행을 시작하고 신호를 보내고
취소하는 데에는 execute가 필요합니다. 둘 다 설정되지 않았을 때는 write
키로 되돌아가며, admin 키는 둘 다 가집니다. 단계만 처리하는 워커에게는 실행을 시작할
수 있는 키가 필요 없고, 이벤트만 발행하는 웹훅에는 단계를 가져갈 수 있는 키가 필요
없습니다. 플래그는
설정을 보십시오.
리스에는 단계의 입력과 앞선 단계들의 출력이 실려 있지만, 워커 호출은 감사 로그에 기록되지 않습니다. 워커 호출은 1분에 여러 번 클레임하는 기계의 루프이고, 클레임마다 행을 하나씩 남기면 사람이 한 읽기가 그 아래에 묻힙니다. 실행 엔드포인트를 통해 실행을 다시 읽는 일은 기록됩니다. 보안을 보십시오.
다음
- 실행을 시작하고 따라가기, 그리고 대기에 응답하기: 실행.
- 워커가 처리하는 정의: 워크플로.
- 명세에서 생성된 워커 프로토콜의 모든 엔드포인트: HTTP API, Workers.