워크플로
워크플로는 장기 실행 작업의 상태를 영속화하여 충돌이나 재시작 후에도 실행을 복구할 수 있게 합니다. 결제, 주문 이행, 여러 단계 승인 같은 프로세스에 적합합니다.
워크플로를 사용하는 이유
함수는 진행 중인 상태를 메모리에 보관하지만 워크플로는 실행 상태를 영속화합니다.
| 측면 | 함수 | 워크플로 |
|---|---|---|
| 상태 | 호출 로컬 | 영속 기록에서 재구성 |
| 워커 충돌 | 진행 중인 호출 실패 | 기록된 이력에서 재생 |
| 실행 시간 | 수초~수분 | 수시간~수개월 |
| 애플리케이션 실패 | 호출자에게 반환 | 제공자 정책에 따라 종료 또는 재시도 |
워크플로 작동 방식
워크플로 코드는 일반 Lua 코드처럼 보입니다.
local funcs = require("funcs")
local time = require("time")
local result, err = funcs.call("app.api:charge_card", payment)
if err then return nil, err end
time.sleep("24h")
local status, err = funcs.call("app.api:check_status", result.id)
if err then return nil, err end
if status == "failed" then
local _, refund_err = funcs.call("app.api:refund", result.id)
if refund_err then return nil, refund_err end
end
return status
워크플로 엔진은 호출을 가로채 결과를 기록합니다. 충돌 후에는 기록된 이력에서 실행을 재생합니다.
워크플로 안에서 각 funcs.call() 대상은 Temporal 액티비티로 실행됩니다. 대상 function.* 엔트리는 meta.temporal.activity.worker를 통해 워커에 등록되어야 합니다. 등록되지 않은 엔트리는 워크플로에서 사용할 수 없습니다. process.* 액티비티 대상에는 Temporal 워커가 사용하는 함수 레지스트리에 등록되도록 meta.options.default_host 또는 레거시 meta.default_host도 필요합니다. 함수 액티비티 예제와 액티비티 옵션은 액티비티를 참조하세요.
funcs.call()은 기록된 액티비티를 실행하고, time.sleep()은 워크플로 타이머를 사용하며, uuid.v4()는 부작용을 기록하고, time.now()는 워크플로의 결정적 시간 참조를 읽습니다.
워크플로 패턴
사가 패턴
실패 시 보상 작업을 수행합니다.
local funcs = require("funcs")
local inventory, err = funcs.call("app.inventory:reserve", items)
if err then return nil, err end
local payment, err = funcs.call("app.payments:charge", amount)
if err then
local _, compensation_err = funcs.call("app.inventory:release", inventory.id)
return nil, compensation_err or err
end
local shipping, err = funcs.call("app.shipping:create", order)
if err then
local _, refund_err = funcs.call("app.payments:refund", payment.id)
local _, release_err = funcs.call("app.inventory:release", inventory.id)
return nil, refund_err or release_err or err
end
return {inventory = inventory, payment = payment, shipping = shipping}
신호 기다리기
승인 결정, 웹훅, 사용자 작업 같은 외부 이벤트를 기다립니다.
local funcs = require("funcs")
local _, err = funcs.call("app.approvals:submit", request)
if err then return nil, err end
local inbox = process.inbox()
local msg, open = inbox:receive() -- blocks until signal arrives
if not open then return nil, errors.new("workflow inbox closed") end
local decision, payload_err = msg:payload():data()
if payload_err then return nil, payload_err end
if decision.approved then
return funcs.call("app.orders:fulfill", request.order_id)
else
return funcs.call("app.notifications:send_rejection", request)
end
컴퓨팅 모델 선택
| 사용 사례 | 선택 |
|---|---|
| HTTP 요청 처리 | 함수 |
| 데이터 변환 | 함수 |
| 백그라운드 작업 | 프로세스 |
| 사용자 세션 상태 | 프로세스 |
| 실시간 메시징 | 프로세스 |
| 결제 처리 | 워크플로 |
| 주문 이행 | 워크플로 |
| 여러 날에 걸친 승인 | 워크플로 |
워크플로 시작
워크플로 호스트와 함께 process.spawn()을 사용합니다.
-- Spawn workflow on temporal worker
local pid, err = process.spawn("app.workflows:order_processor", "app:temporal_worker", order_data)
if err then return nil, err end
-- Send signals to workflow
local ok, err = process.send(pid, "update", {status = "approved"})
if err then return nil, err end
return ok
호출자는 같은 생성 API를 사용합니다. 호스트가 엔트리를 temporal.worker에서 실행할지 process.host에서 실행할지 결정합니다. 영속 이력과 재생은 Temporal 호스트 경로에만 적용됩니다. 일반 프로세스 호스트를 통해 실행한 워크플로 엔트리는 메모리 프로세스 의미 체계를 가지며 Temporal 내구성을 얻지 않습니다.
process.spawn()으로 자식을 생성하면 같은 제공자의 자식 워크플로가 되어 내구성 보장을 유지합니다.
실패와 감독
프로세스는 process.service를 사용해 감독되는 서비스로 실행할 수 있습니다.
# Process definition
- name: session_handler
kind: process.lua
source: file://session_handler.lua
method: main
# Supervised service wrapping the process
- name: session_manager
kind: process.service
process: app:session_handler
host: app:processes
lifecycle:
auto_start: true
restart:
max_attempts: 10
워크플로는 프로세스 감독 트리를 사용하지 않습니다. 워크플로 제공자가 영속성과 복구를 관리하고, 애플리케이션 수준 재시도는 구성된 워크플로 및 액티비티 정책을 따릅니다.
구성
동적으로 생성되는 워크플로 정의:
- name: order_processor
kind: workflow.lua
source: file://order_processor.lua
method: main
meta:
temporal:
workflow:
worker: app:temporal_worker
modules:
- funcs
- time
funcs.call()을 통해 호출되는 모든 함수 또는 프로세스도 액티비티 워커를 선언합니다. 예:
- name: charge_card
kind: function.lua
source: file://charge_card.lua
method: main
meta:
temporal:
activity:
worker: app:temporal_worker
워크플로 제공자:
- name: temporal_worker
kind: temporal.worker
client: app:temporal_client
task_queue: "orders"
lifecycle:
auto_start: true
프로덕션 워크플로 인프라는 Temporal을 참조하세요.