-
[Cloud Run][운영] concurrency를 낮춘 뒤에도 latency가 남을 때 connection pooling과 downstream retry를 어떤 순서로 먼저 줄이나기타개발지식/풀스택개발 2026. 7. 12. 20:20
IT 리서치 노트
[Cloud Run][운영] concurrency를 낮춘 뒤에도 latency가 남을 때 connection pooling과 downstream retry를 어떤 순서로 먼저 줄이나
Cloud Run에서 concurrency를 낮췄는데도 latency가 계속 남으면 많은 팀이 다시 concurrency나 request timeout부터 만진다. 하지만 2026년 7월 12일 기준 Google Cloud 문서를 다시 보면, outbound connection 재사용과 dependency-level logging, connection pool timeout, retry interval은 서로 다른 층이다. 이 글은 concurrency를 이미 낮춘 뒤에도 느린 요청이 남을 때 connection pooling과 downstream retry를 어떤 순서로 먼저 줄이는 편이 실무적으로 맞는지 정리한 것이다.
1. 개요
결론부터 말하면 concurrency를 낮춘 뒤에도 latency가 남는다면 retry보다 connection pooling을 먼저 본다. 먼저 요청당 새 연결을 계속 여는지, pool warm-up 때문에 첫 요청만 느린지, pool 획득 대기가 쌓이는지 확인한다. 그다음 explicit downstream logging으로 외부 API나 DB 자체가 느린지 본 뒤, 마지막에 retry backoff와 횟수를 줄이는 편이 맞다.
retry는 실패를 증폭시키는지 보는 마지막 축이지, 남은 latency의 첫 원인 가설이 아니다. 이미 handler runtime과 downstream I/O 분리 글이 큰 갈래를 나눴다면, 이번 글은 downstream I/O 안에서 pool과 retry의 우선순위를 더 좁히는 후속편이다. 여기서 한 단계 더 나가 pooling을 정리한 뒤에도 남는 spike를 egress path와 Cloud NAT 포트 압박 기준으로 가르고 싶다면 Direct VPC egress와 Cloud NAT 포트 고갈 분기 글이 다음 갈래다.
2. 어디서 실제로 막히는가
현장에서 자주 생기는 착시는 세 가지다. 첫째, concurrency를 낮춘 뒤 latency가 조금 줄어든 것을 보고 남은 지연도 같은 원인이라고 가정한다. 둘째, 외부 API나 DB 호출이 느린데도 애플리케이션 retry가 원인이라고 먼저 본다. 셋째, 연결 생성 비용과 pool 획득 대기를 로그에서 따로 남기지 않아 retry와 pool 문제를 같은 숫자로 읽는다.
Cloud Run troubleshooting 문서는 다른 서비스 요청이 latency 원인일 수 있으니 개별 요청 로그와 trace를 남기라고 하고, networking best practices는 connection pooling과 connection reuse를 쓰지 않으면 latency와 connection refused가 늘 수 있다고 말한다. Cloud SQL connection management는 pool timeout, acquire timeout, create timeout, retry interval을 अलग개 값으로 조정하는 예시를 보여 준다. 이 셋을 같이 읽으면 'retry를 줄일까, pool을 늘릴까'보다 먼저 '지금 지연이 어느 단계에서 생기는가'를 나눠야 한다는 점이 분명해진다.
- 증상: concurrency를 낮춰도 p95 latency가 크게 남는다.
- 실패: retry와 connection pool 문제를 같은 요청 시간으로 본다.
- 막힘: downstream 호출 시간을 explicit log로 남기지 않는다.
- 누락: pool 획득 시간과 새 연결 생성 시간을 따로 보지 않는다.
관찰 먼저 볼 값 의미 첫 요청만 느리다 idle eviction, warm-up pool 초기화 비용일 수 있다 모든 요청이 비슷하게 느리다 downstream response time 원지연이 pooling 밖에 있을 수 있다 실패 후 갑자기 느려진다 retry backoff, create timeout 재시도가 지연을 키울 수 있다 3. 실무에서 적용하는 순서
가장 실용적인 순서는 다섯 단계다. 먼저 요청 로그에 pool acquire time, downstream call duration, retry attempt를 분리해 남긴다. 두 번째로 connection pooling과 connection reuse가 실제로 켜져 있는지 본다. 세 번째로 첫 요청 지연이 크다면 idle eviction과 최소 연결 유지 전략을 본다. 네 번째로 downstream 원지연이 큰지 trace와 explicit log로 확인한다. 마지막으로 이 앞 단계가 정리된 뒤에만 retry 횟수와 backoff를 줄인다.
- pool acquire time과 downstream duration을 따로 남긴다.
- connection reuse와 pool 설정부터 확인한다.
- 첫 요청만 느리면 idle eviction과 warm-up을 본다.
- 원지연이 확인되면 downstream 호출 경로를 줄인다.
- 마지막에 retry 횟수와 backoff를 조정한다.
이 순서가 좋은 이유는 retry가 항상 증상 뒤에 있기 때문이다. 연결을 계속 새로 열면 성공 요청도 느리고, retry는 그 위에 추가 지연만 얹는다. 반대로 pool은 괜찮고 downstream API 자체가 느리다면 retry를 줄여도 근본 지연은 줄지 않는다. 따라서 retry는 pool과 원지연이 분리된 뒤에만 의미가 생긴다.
특히 Cloud SQL 같은 DB가 섞인 경우에는 pool max, min, acquire timeout, create timeout, idle timeout을 따로 적어 둬야 한다. 외부 HTTP API라면 connection reuse 여부와 upstream timeout, retry 조건을 같이 본다. 둘 다 공통점은 '새 연결 생성'과 '실패 후 재시도'를 한 값으로 보지 않는 것이다.
- pool 문제와 retry 문제를 서로 다른 로그 칸으로 분리한다.
- downstream 원지연이 있는지 trace와 explicit log로 먼저 확인한다.
- retry는 마지막에 줄여야 의미가 있다.
4. 공식 문서와 예시 화면으로 확인하기
첫 자료는 Cloud Run networking best practices의 connection pooling 구간이다. Google은 outbound connection을 매 요청마다 다시 여는 대신 재사용하라고 설명하고, 그렇지 않으면 latency와 connection refused가 늘 수 있다고 적는다. 즉 concurrency를 낮춘 뒤에도 latency가 남는다면, 요청당 연결 생성 비용부터 먼저 분리해야 한다.
이 문장이 중요한 이유는 retry보다 앞선다는 점이다. 연결을 계속 새로 여는 구조에서는 retry를 줄여도 첫 연결 비용이 그대로 남고, 오히려 실패 시 재연결이 누적되며 꼬임이 커질 수 있다.
두 번째 자료는 Cloud Run troubleshooting overview의 latency 분기다. 문서는 느린 요청의 원인을 보려면 Cloud Trace나 Cloud Logging으로 개별 요청을 식별하고, 다른 서비스로 나가는 의존성 요청은 explicit logging으로 따로 남기라고 안내한다.
즉 connection pooling 문제인지, downstream API나 DB 응답이 원래 느린지, retry가 그 지연을 증폭하는지부터 로그 축을 나눠야 한다. 이 분리가 없으면 concurrency 조정과 timeout 상향만 반복하게 된다.
세 번째 자료는 Cloud Run Java tips의 connection pool 항목이다. 이 문서는 low QPS에서는 idle connection eviction과 첫 요청 지연도 함께 고려하라고 설명한다. 즉 pool을 만든다고 끝나는 것이 아니라 pool lifetime과 idle timeout이 latency 모양을 바꾼다.
이 점이 retry와 연결된다. 첫 요청이 느린 이유가 pool warm-up이라면 retry 정책을 줄이기 전에 idle eviction과 최소 연결 유지 전략을 먼저 손보는 편이 맞다.
네 번째 자료는 Cloud SQL manage connections의 connection pools 설명이다. 여기서는 pool이 연결을 캐시해 latency와 성능을 개선한다고 적고, pool timeout, acquire timeout, create timeout, retry interval 같은 값을 별도로 조정하는 예시를 제공한다.
이 예시는 Cloud Run latency triage에도 그대로 유용하다. retry가 느린 것인지, 새 연결 획득이 느린 것인지, pool 자체가 꽉 차서 기다리는 것인지가 서로 다른 숫자라는 뜻이기 때문이다.
실무에서는 latency 잔존을 세 갈래로 나눈 표가 가장 빨리 먹힌다. 요청 시작 직후 느린지, 외부 응답 대기 중 느린지, 실패 후 재시도 때문에 늘어나는지로 먼저 자르면 조정 순서가 짧아진다.
이 표를 기준으로 보면 retry 축은 마지막이다. 이미 handler runtime과 downstream I/O 분리 글이 큰 분기를 다뤘다면, 이번 글은 downstream I/O 안에서 pooling과 retry 순서를 더 좁히는 후속편이다.
마지막 자료는 pool 획득 시간과 retry 간격을 따로 적어 두는 설정 예시다. 팀 문서에 이런 최소값이 있으면 latency가 남을 때 무엇을 먼저 줄일지 논쟁이 짧아진다.
이렇게 해 두면 pool이 비어 대기한 것인지, 새 연결 생성이 느린 것인지, retry backoff가 긴 것인지 로그 해석이 분리된다. 또 startup latency와 request timeout 글, single-thread latency 글과도 자연스럽게 이어진다.
5. 주의사항과 리스크
첫 번째 리스크는 pool이 비는 상황과 새 연결 생성 지연을 구분하지 않는 것이다. 두 번째 리스크는 downstream API 자체가 느린데 retry만 줄여 해결됐다고 착각하는 것이다. 세 번째 리스크는 request timeout을 올려 pool 문제를 가리는 것이다.
운영 전에 확인할 때는 최소한
pool_acquire_ms,pool_create_ms,downstream_call_ms,retry_attempt,retry_backoff_ms다섯 칸을 같은 로그 블록에 남겨 두는 편이 좋다. 이 값이 없으면 latency가 pool warm-up인지 retry amplification인지 다시 추정만 하게 된다.- pool이 없는데 retry만 줄여도 지연은 계속 남을 수 있다.
- 원지연이 큰 downstream에는 concurrency 재조정보다 호출 구조 수정이 먼저다.
- timeout 상향은 원인 분리 이후에만 한다.
6. 결론
Cloud Run에서 concurrency를 낮춘 뒤에도 latency가 남는다면 connection pooling을 먼저 보고 retry는 마지막에 본다. 새 연결 생성, pool 획득 대기, downstream 원지연, retry 증폭은 서로 다른 단계이기 때문이다. 이 순서를 고정해 두면 같은 느림이라도 무엇을 먼저 줄여야 하는지 훨씬 빨리 정해진다. 그리고 pooling 다음 단계의 원인이 애플리케이션 밖 네트워크 경로에 있는지 보고 싶다면 Direct VPC egress와 Cloud NAT 포트 압박 글로 이어 보면 된다.
- 먼저 pool과 connection reuse를 확인한다.
- 그다음 downstream 원지연을 trace로 본다.
- retry는 마지막에 줄인다.
7. 참고 링크
'기타개발지식 > 풀스택개발' 카테고리의 다른 글