Python Celery worker concurrency가 낮아 보일 때: 문제 해결 가이드
마지막 업데이트

Python Celery worker concurrency가 낮아 보일 때: 문제 해결 가이드


Celery worker concurrency가 낮아 보일 때, 겉으로 보이는 문제는 worker count가 적은 것 같지만 실제로는 pool model, task 형태, prefetch 동작, broker 흐름, blocking dependency 때문에 체감 병렬성이 훨씬 낮아지는 경우가 많습니다.

짧게 말하면, 설정된 concurrency 숫자만 보지 말고 실제로 동시에 진전되는 task 수를 먼저 확인한 뒤, task duration, prefetch, blocking dependency를 봐야 합니다.


먼저 설정된 slot 수와 실제 throughput을 구분하세요

worker가 높은 concurrency를 광고하더라도 실제 throughput이 낮을 수 있습니다.

task가 길거나, 분배가 나쁘거나, downstream system에 막히거나, prefetch 때문에 특정 worker에 치우치면 설정된 숫자보다 훨씬 약하게 보입니다. 그래서 raw worker count는 표면적인 숫자일 뿐입니다.

concurrency가 낮아 보이는 흔한 이유

1. task가 예상보다 오래 block 됨

database wait, 외부 API call, filesystem 작업, CPU-heavy section이 worker slot을 오래 점유할 수 있습니다.

그러면 사용 가능한 병렬 capacity가 빠르게 사라집니다.

2. pool choice가 workload와 맞지 않음

prefork, threads, gevent, eventlet 계열은 CPU-heavy인지, I/O block이 많은지, 혼합형인지에 따라 체감 성능이 달라집니다.

workload에 맞지 않는 pool model은 설정 숫자보다 훨씬 약한 concurrency로 보이게 만듭니다.

3. prefetch가 작업 분배를 왜곡함

worker가 task를 너무 많이 먼저 잡아두면 다른 worker는 덜 쓰이는데도 전체 시스템은 병렬성이 낮아 보일 수 있습니다.

이때는 queue가 길고 분배가 끈적하게 보이는 현상이 같이 나옵니다.

4. broker나 downstream pressure가 실제 진행을 늦춤

queue는 꽉 차 있지만 worker는 실질적으로 일을 끝내기보다 다른 곳을 기다리며 시간을 보내고 있을 수 있습니다.

5. memory와 worker model tradeoff를 무시하고 있음

기술적으로 concurrency를 더 높일 수 있어도 memory duplication, process churn, downstream pressure 때문에 운영적으로는 나쁜 선택일 수 있습니다.

그래서 concurrency 조정은 memory와 dependency 동작과 함께 봐야 합니다.

실전 점검 순서

1. 설정된 concurrency와 실제 active task progress를 비교하세요

가장 먼저 “동시에 몇 개가 진짜로 전진하고 있는가”를 봐야 합니다. 종이 위에 slot이 16개여도 실제로 몇 개만 계속 진전된다면 병목은 다른 곳에 있습니다.

2. task duration과 blocking call을 측정하세요

특히 아래를 확인합니다.

  • 긴 database wait
  • 느린 external API
  • 큰 CPU-heavy section
  • task가 다른 시스템을 기다리는 구간

Celery concurrency 문제는 결국 task shape에서 시작되는 경우가 많습니다.

3. pool type과 prefetch를 같이 검토하세요

prefetch를 보지 않고 pool setting만 바꾸면 착시 같은 개선이나 더 나쁜 분배가 생길 수 있습니다.

아래 같은 baseline은 예약 분배가 문제인지 확인하는 데 도움이 됩니다.

celery -A app worker --loglevel=info --concurrency=4 --prefetch-multiplier=1

4. queue backlog와 worker utilization을 같이 보세요

backlog는 큰데 worker가 충분히 생산적이지 않다면, 단순히 worker 수 부족이 아니라 completion을 늦추는 다른 병목이 있다는 뜻입니다.

5. bottleneck이 보인 뒤에만 scale 하세요

task가 독립적이고 downstream이 충분히 버틴다면 worker 증설이 도움이 됩니다. 반대로 contention이나 memory cost만 키운다면 scaling은 오히려 악화 요인입니다.

패턴을 찾은 뒤 어떻게 바꿀지

task가 너무 느리거나 blocking이 큰 경우

task scope를 줄이고, heavy task를 분리하고, task body에서 불필요한 blocking을 줄이세요.

pool type이 workload와 맞지 않는 경우

기본값이 아니라 실제 workload shape에 맞는 pool model을 선택해야 합니다.

prefetch가 분배를 왜곡하는 경우

prefetch를 낮추거나 reservation 동작을 조정해 worker 하나가 너무 많은 작업을 선점하지 않게 해야 합니다.

worker를 늘릴수록 memory cost가 너무 커지는 경우

더 늘리기 전에 Python worker memory duplication을 같이 보세요.

빠른 체크리스트

Celery concurrency가 낮아 보일 때는 아래 순서가 가장 실용적입니다.

  1. 설정된 concurrency와 실제 active progress를 비교한다
  2. task duration과 blocking dependency를 확인한다
  3. pool type과 prefetch를 같이 검토한다
  4. queue backlog와 worker utilization을 본다
  5. bottleneck을 이해한 뒤에만 scale 한다

FAQ

Q. worker 수를 늘리면 가장 빨리 해결되나요?

그럴 때도 있지만, task가 block되거나 분배가 나쁘면 그렇지 않습니다.

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

queue backlog, active worker progress, task duration을 동시에 보는 것입니다.

Q. worker가 많은데도 queue가 긴 이유는 뭔가요?

worker가 block되었거나, task를 선점만 하고 있거나, 같은 downstream system을 기다리고 있기 때문일 수 있습니다.

Q. concurrency를 올리자 memory가 더 나빠진 이유는 뭔가요?

worker 수 증가가 process memory duplication과 dependency pressure를 함께 키웠기 때문일 수 있습니다.

Sources:

먼저 읽어볼 가이드

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