Go 에서 context canceled 가 너무 빨리 보인다면 문제는 보통 cancel 자체가 아니라 scope 와 ownership 입니다. parent context 가 실제 작업보다 먼저 끝나거나, timeout 이 실제 경로보다 너무 짧게 잡힌 경우가 많습니다.
짧게 말하면 핵심은 이것입니다. 누가 context 를 소유하고 있고, cancellation 이 어디서 시작되는지 먼저 봐야 합니다. context cancellation 은 runtime 입장에선 보통 정상 동작입니다. 진짜 질문은 올바른 context 가 올바른 작업을 소유하고 있는가입니다.
parent lifetime 과 ownership 부터 본다
많은 context canceled incident 는 사실 lifecycle bug 입니다.
알아야 할 것은 이렇습니다.
- 누가 context 를 만든다
- 누가
cancel을 호출한다 - 어떤 작업이 그 context 에 묶여 있다
- 그 작업이 정말 parent 와 함께 멈춰야 하는가
이 ownership map 이 없으면 context bug 는 랜덤한 실패처럼 보이기 쉽습니다.
early cancellation 은 실전에서 어떻게 보이나
운영 환경에서는 보통 이렇게 보입니다.
- request 가 끝나면서 background work 가 같이 죽는다
- retry 나 fan-out call 이 너무 많은 작업을 일찍 취소한다
- dependency 는 멀쩡해 보이는데 handler 에서
context canceled가 나온다 - shutdown logic 이 끝나도 되는 작업까지 너무 빨리 멈춘다
이런 incident 가 놀랍게 느껴지는 이유는 코드에서 context lifetime 이 충분히 드러나지 않기 때문입니다.
흔한 원인
1. parent context 의 수명이 너무 짧다
request-scoped context 는 팀이 생각하는 것보다 훨씬 짧은 범위만 제어하는 경우가 많습니다.
request 보다 오래 살아야 하는 작업에 r.Context() 를 그대로 쓰는 건 자주 틀립니다.
2. timeout 설정이 너무 공격적이다
configured deadline 이 실제 dependency 또는 worker path 보다 짧을 수 있습니다.
이 경우 cancellation 은 미스터리가 아니라 예측 가능한 결과입니다.
3. background work 가 잘못된 context 를 쓴다
request 하나를 넘어 살아야 하는 task 가 실수로 request cancellation 을 상속받을 수 있습니다.
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()
go runBackgroundJob(ctx) // request 가 끝나면 같이 멈출 수 있음
background work 가 request 보다 오래 살아야 한다면 r.Context() 를 그대로 물리면 안 됩니다.
4. retry 와 fan-out path 가 cancellation pressure 를 키운다
병렬 호출이 많아지면 짧은 parent deadline 하나가 너무 많은 child work 를 한꺼번에 취소할 수 있습니다.
특히 서로 다른 latency profile 을 가진 downstream call 들을 하나의 parent context 가 묶을 때 자주 아픕니다.
5. shutdown flow 의 cancellation boundary 가 불분명하다
graceful drain 되어야 할 worker 가 더 넓은 shutdown path 의 abrupt cancellation 을 상속받을 수 있습니다.
실전 점검 순서
1. 누가 context 를 만들고 누가 cancel 하는지 찾는다
가장 중요한 첫 단계입니다.
owner 를 모르면 모든 cancellation 이 임의적으로 보입니다.
2. 실제 작업 시간과 timeout / deadline 설정을 비교한다
deadline 이 정상 경로보다 짧다면 early cancellation 은 예상된 동작입니다.
3. background task 가 request-scoped context 를 상속받는지 본다
가장 아픈 Go lifecycle bug 들이 여기서 많이 드러납니다.
4. fan-out 과 retry path 에서 parent cancellation 이 너무 이른지 확인한다
짧은 parent deadline 하나가 여러 child 를 함께 과도하게 취소할 수 있습니다.
5. 멈춰야 하는 작업에만 cancellation 이 도달하는지 확인한다
마지막 질문은 cancellation boundary 가 실제 ownership 과 맞는가입니다.
ownership bug 를 찾은 뒤 무엇을 바꾸면 좋나
parent 수명이 너무 짧다면
작업을 올바른 lifecycle 을 가진 context 에 다시 묶어야 합니다.
timeout 이 너무 공격적이라면
현실적인 latency 에 맞게 조정하거나 단계별 deadline 으로 나눠야 합니다.
background work 가 request context 를 상속받는다면
request 바깥의 explicit owner 를 줘야 합니다.
fan-out 이 과도하게 취소된다면
하나의 parent deadline 이 여러 child 를 어떻게 제어하는지 다시 봐야 합니다.
shutdown 이 너무 넓게 cancel 한다면
graceful drain context 와 hard-stop context 를 분리해야 합니다.
장애 중에 던져볼 질문
이 질문이 꽤 유용합니다.
이 작업은 정말 이 parent context 가 끝날 때 같이 멈춰야 하나, 아니면 실제 job 보다 더 짧은 lifetime 을 잘못 상속받은 건가?
이 질문이 error text 만 보는 것보다 훨씬 빨리 real bug 를 드러냅니다.
FAQ
Q. background work 가 request context 를 써도 되나
그 작업이 request 와 함께 멈춰야 할 때만 그렇습니다.
Q. 가장 빠른 첫 단계는 무엇인가
context creator 를 찾고, 그 lifetime 과 제어하는 작업의 길이를 비교하는 것입니다.
Q. early cancellation 은 항상 timeout 문제인가
아닙니다. ownership 과 scope 실수도 매우 흔합니다.
Q. cancellation 이 “정상” 인데 왜 버그인가
runtime 은 정상인데 코드가 잘못된 작업을 잘못된 lifetime 에 묶었을 수 있기 때문입니다.
Read Next
- 증상이 surprising cancel path 보다 hard deadline expiry 에 가깝다면 Golang Context Deadline Exceeded 와 비교해 보세요.
- cancellation pressure 가 stuck coordination 으로 번진다면 Golang WaitGroup Stuck 을 이어서 보세요.
- background work 가 clean 하게 멈추지 않고 누적된다면 Golang Goroutine Leak 과 같이 보세요.
- 전체 Go 분기 지도는 Golang Troubleshooting Guide 에서 이어 볼 수 있습니다.
Related Posts
- Golang Context Deadline Exceeded
- Golang WaitGroup Stuck
- Golang Goroutine Leak
- Golang Troubleshooting Guide
Sources:
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.
먼저 읽어볼 가이드
검색 유입이 많은 핵심 글부터 이어서 보세요.
- 미들웨어 트러블슈팅 가이드: Redis vs RabbitMQ vs Kafka 개발자를 위한 미들웨어 트러블슈팅 허브 글입니다. Redis, RabbitMQ, Kafka 중 어떤 증상부터 먼저 봐야 하는지와 어떤 문제 패턴이 각 시스템에 가까운지 정리합니다.
- Kubernetes CrashLoopBackOff: 먼저 볼 것들 startup failure, probe, config, resource limit 관점에서 CrashLoopBackOff를 어떻게 나눠서 봐야 하는지 정리한 가이드입니다.
- Kafka consumer lag가 계속 늘 때: 트러블슈팅 가이드 Kafka consumer lag가 계속 늘어날 때 무엇부터 봐야 하는지 정리합니다. poll 주기, 처리 속도, rebalance, consumer 설정까지 실전 기준으로 다룹니다.
- Kafka Rebalancing Too Often 가이드 Kafka consumer group에서 rebalance가 너무 자주 일어날 때 membership flapping, poll timing, protocol, assignment churn을 어떤 순서로 봐야 하는지 설명하는 실전 가이드입니다.
- Docker container가 계속 재시작될 때: 먼저 확인할 것들 exit code, command failure, environment mistake, health check 관점에서 Docker restart loop를 푸는 실전 가이드입니다.
심사 대기 중에는 광고 대신 관련 가이드를 먼저 보여줍니다.