본문 바로가기
C.W.K.
Stream
Lesson 03 of 04 · published

요청은 긴 일을 소유 안 해

~10 min · requests, workers, async, durability

Level 0Empty Shelf
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"몇 시간짜리 일을 소유한 요청은 노트북 닫아서 죽일 수 있는 job 이야."

솔깃한 실수

'이 아카이브 전사해줘' 를 만드는 뻔한 방법은 그 일을 직접 하는 요청 핸들러야. 브라우저가 endpoint 를 부르면 핸들러가 영상을 하나씩 돌면서 전사하고, 아카이브가 끝나면 return 하고. 데모에선 돌아가. 프로덕션에선 재앙이고. 요청만큼 부서지기 쉬운 소유자가 없거든. 요청의 수명은 TCP 연결에, 브라우저 탭에, 호텔 Wi-Fi 신호에, 노트북 뚜껑에 묶여 있어. 그중 아무거나 끝나는 순간 몇 시간짜리 job 이 반쯤 된 채로 죽어. 깔끔하게 재개할 방법도 없이.

더 깊은 문제는 timeout 이 아니라 소유권이야. 요청이 일을 소유하면 그 일은 요청이 살아 있는 동안만 존재해. 그런데 요청은 애초에 짧게 살라고 설계된 거잖아. 버려지라고 만든 그릇에 몇 시간짜리 가치를 담은 셈이야.

계획, enqueue, 관찰 — 절대 소유 안 함

Recall 은 그걸 뒤집어. 요청은 전사를 절대 안 돌려. 값싸고 빠른 세 가지 중 하나만 하고 바로 return 해.

  • 계획(Plan) — run 이 뭘 할지(어떤 영상을 몇 개) 아무것도 안 쓰고 계산해.
  • enqueue — durable batch 와 그 queued job 을 DB 에 만들고 batch id 를 돌려줘. 밀리초면 끝나.
  • 관찰(Observe) — 이미 돌고 있는 batch 의 현재 상태와 진행과 ETA 를 읽어.

실제 몇 시간짜리 전사는 persistent worker 가 소유해. 운영체제의 서비스 매니저가 살려두는, 어떤 요청과도 무관한 프로세스야. queued job 을 claim 해서 누가 보든 말든 갈아. 그래서 operator 노트북 연결을 끊든 status 명령을 죽이든 batch 엔 아무 일도 안 생겨. 애초에 일이 요청에 있었던 적이 없거든. 요청은 durable 한 의도만 적어두고 자리를 떴을 뿐이야.

테스트: 노트북을 닫아

제대로 했는지 보는 한 줄짜리 테스트가 있어. 일을 시작하고 노트북을 닫아 봐. 계속 돌아? 돌면 그 일은 persistent worker 가 소유하는 durable queue 에 있는 거야. 안 돌면 어떤 요청이 몰래 job 을 인질로 잡고 있는 거고, 네트워크가 한 번만 흔들려도 몇 시간이 날아가. Recall 은 설계상 이 테스트를 통과해. 요청이 하는 일은 뭐가 일어나야 하는지를 durably 기록하는 것 하나고, 그걸 실제로 일어나게 하는 건 모든 요청보다 오래 사는 worker 니까.

Code

요청은 enqueue 하고 return, 시간은 worker 가 소유·python
# 틀림: 요청이 몇 시간짜리 일을 소유
@router.post('/archive/transcribe')
async def transcribe_archive(req):
    for video in select_videos(req):
        do_transcription(video)   # 몇 시간 block
    return {'status': 'done'}     # 탭 닫으면 전부 죽음

# 맞음: 요청은 의도를 쓰고 밀리초에 return
@router.post('/archive/start')
async def start_batch(req):
    batch = create_batch(req)             # durable 행
    enqueue_jobs(batch)                    # queued, DB 가 소유
    return {'batch_id': batch.id}          # 즉시 return

# 요청이 아니라, OS 서비스 매니저가 살려두는 persistent worker 가
# 그 job 들을 claim 하고 몇 시간을 돌린다.

External links

Exercise

네 일에서 느린 걸 inline 으로 처리하는 endpoint 를 찾아봐(없으면 상상해도 돼). 리포트 생성, 업로드 처리, 긴 export 같은 거. 거기다 노트북 닫기 테스트를 돌려. 호출자가 중간에 끊기면 일이 사라져? 그러면 endpoint 는 durable 한 의도 record 만 만들고 핸들을 돌려주게, 느린 부분은 따로 있는 persistent worker 가 하게 다시 설계해. 핸들이 뭔지, 호출자가 진행을 어떻게 확인하는지도 적어보고.
Hint
이 패턴에는 움직이는 부분이 셋 있어. durable 한 job/batch 행을 쓰고 id 를 빠르게 돌려주는 endpoint, 요청 바깥에 살면서 그 행을 claim 하는 worker, 호출자가 id 로 poll 하는 status endpoint. 셋을 다 못 대겠으면 느린 일이 아직 요청 안에 갇혀 있는 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.