Golang Worker Pool Backpressure: 작업이 계속 쌓일 때
마지막 업데이트

Golang Worker Pool Backpressure: 작업이 계속 쌓일 때


Go worker pool 에서 작업이 계속 쌓인다면, queue 는 보통 들어오는 작업 속도가 끝나는 속도보다 빠르다고 말해 주는 것입니다. 실제 문제는 느린 downstream call, 비싼 job 비용, 없는 backpressure, 혹은 overload 를 제어하지 못하고 숨기기만 하는 pool 구조인 경우가 많습니다.

짧게 말하면 핵심은 이것입니다. worker 를 더 늘리기 전에 job 유입 속도와 완료 속도를 비교해야 합니다. 커지는 queue 는 보통 scheduler 미스터리가 아니라 throughput mismatch 입니다.


queue 증가와 completion pace 부터 본다

worker 수를 만지기 전에, 시스템이 아래 중 무엇인지 알아야 합니다.

  • 처리가 너무 느린가
  • 유입이 너무 많은가
  • 같은 작업을 retry 로 반복하는가
  • downstream system 에 막히는가

이 네 가지는 모두 queue growth 를 만들지만 fix 는 다릅니다.


worker-pool backpressure 는 실전에서 어떻게 보이나

운영 환경에서는 보통 이렇게 보입니다.

  • queue depth 가 꾸준히 오른다
  • worker 는 바쁜데 throughput 은 회복되지 않는다
  • saturation 이 분명한데도 producer 는 계속 일을 넣는다
  • retry 와 requeue 가 backlog 를 더 악화시킨다
  • 운영자는 worker 수를 늘렸다가 downstream 문제를 더 키운다

그래서 queue growth 는 단순 worker 설정 문제가 아니라 시스템 신호로 봐야 합니다.


흔한 원인

1. worker 가 각 job 에 너무 오래 머문다

DB, HTTP, file I/O, CPU-heavy step 이 effective throughput 을 낮출 수 있습니다.

job 당 비용이 커지면 pool size 가 그대로여도 queue 는 커집니다.

2. queue input 에 backpressure 가 없다

시스템이 이미 포화됐는데도 producer 가 계속 작업을 밀어 넣을 수 있습니다.

즉 queue 가 overload 를 제어하는 대신 흡수하고 있는 상태입니다.

3. worker 수가 실제 workload 와 맞지 않는다

worker 가 너무 적으면 throughput 이 막히고, 너무 많아도 downstream contention 과 resource pressure 를 더 키울 수 있습니다.

worker 는 많을수록 좋은 게 아닙니다.

4. retry 와 requeue 가 queue pressure 를 곱한다

실패하는 dependency 하나 때문에 같은 job 이 반복해서 쌓일 수 있습니다.

이 경우 backlog 는 capacity 문제가 아니라 failure-amplification 문제인 경우가 많습니다.

5. 작업 분포가 하나의 느린 stage 를 숨긴다

pool 전체가 작다기보다, 특정 stage 또는 job type 하나가 훨씬 느려서 queue age 를 지배하는 경우도 있습니다.


실전 점검 순서

1. job 유입 속도와 완료 속도를 비교한다

핵심 신호입니다.

유입이 완료보다 꾸준히 크다면 queue 증가 자체는 당연하고, 이제 왜 그런지 찾아야 합니다.

2. queue depth 와 worker utilization 을 같이 본다

queue 는 큰데 worker utilization 이 낮으면 한 종류의 문제고,

queue 도 크고 worker 도 포화면 다른 종류의 문제입니다.

3. 각 job 안의 blocking downstream step 을 찾는다

아래를 보세요.

  • HTTP wait
  • DB wait
  • file 또는 network latency
  • lock 또는 channel stall

worker 대부분이 기다리는 중이라면, worker 를 늘려도 waiting 만 넓게 퍼질 수 있습니다.

4. retry, requeue, duplicate work 패턴을 점검한다

이 단계는 자주 건너뛰지만, worker 가 바빠 보이는데도 queue 가 계속 커지는 이유를 설명하는 경우가 많습니다.

5. throughput 한계가 분명해진 뒤에만 worker 수를 조정한다

실제 bottleneck 이 downstream 또는 duplicated work 라면, worker tuning 만으로는 queue 가 안 풀립니다.


예시: worker 는 살아 있는데 queue 는 계속 커지는 경우

jobs := make(chan Job, 100)

for i := 0; i < 4; i++ {
	go worker(jobs)
}

producer 가 worker 완료 속도보다 빠르게 일을 넣으면 queue depth 는 계속 커지고, backpressure 는 결국 시스템 다른 곳에서 터집니다.

핵심 질문은 “왜 worker 가 incoming work 대비 너무 느리게 끝나는가” 입니다.


bottleneck 을 찾은 뒤 무엇을 바꾸면 좋나

job 자체가 너무 느리다면

비싼 경로를 최적화하거나 job 당 비용을 줄여야 합니다.

backpressure 가 없다면

bounded queue, producer throttling, rejection behavior 를 넣어 overload 를 더 일찍 보이게 해야 합니다.

worker 수가 mis-sized 라면

실제 throughput 과 dependency behavior 를 바탕으로 조정해야 합니다.

retry 가 pressure 를 키운다면

scale 보다 retry discipline 을 먼저 고쳐야 합니다.

특정 stage 가 backlog 를 지배한다면

그 stage 를 분리하거나 pipeline 설계를 다시 봐야 합니다.


장애 중에 던져볼 질문

이 질문이 꽤 유용합니다.

queue 가 커지는 이유가 worker 가 적어서인가, job 이 느려서인가, 아니면 시스템이 안전하게 끝낼 수 있는 양보다 더 많은 work 를 계속 받아서인가?

이 구분이 보통 fix path 를 바로 드러냅니다.


FAQ

Q. worker 를 더 늘리면 항상 해결되나

아닙니다. downstream bottleneck 을 더 크게 만들 수도 있습니다.

Q. 가장 빠른 첫 단계는 무엇인가

queue growth 와 completion pace 를 동시에 측정하는 것입니다.

Q. retry 만으로도 backpressure 처럼 보일 수 있나

그렇습니다. 실패한 작업 반복만으로도 pool 이 부족해 보일 수 있습니다.

Q. queue 는 항상 bounded 여야 하나

항상은 아니지만, 무제한 축적은 overload 를 너무 늦게 보여 주는 경우가 많습니다.


Sources:

먼저 읽어볼 가이드

검색 유입이 많은 핵심 글부터 이어서 보세요.