ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Cloudflare Queues][운영] backlog는 줄었는데 oldest message age만 남을 때 backlog slope와 realtime metrics를 어떤 시간축으로 같이 기록하나
    기타개발지식/풀스택개발 2026. 8. 17. 09:16

    IT 리서치 노트

    [Cloudflare Queues][운영] backlog는 줄었는데 oldest message age만 남을 때 backlog slope와 realtime metrics를 어떤 시간축으로 같이 기록하나

    Cloudflare Queues에서 incident가 한 차례 지나간 뒤 가장 헷갈리는 순간은 backlog 그래프는 내려오는데 oldest message age만 남는 때다. 2026년 8월 16일 기준 Cloudflare 공식 문서를 다시 보면 backlog와 consumer concurrency와 message operations는 GraphQL 평균 데이터셋으로 보고, realtime backlog의 oldest message timestamp는 point-in-time 값으로 따로 본다. 이 글은 backlog slope와 oldest message age를 어떤 시간축으로 같이 기록해야 queue가 회복 중인지, 아니면 오래된 tail 메시지가 아직 붙어 있는지 빨리 구분할 수 있는지 정리한다.

    1. 개요

    결론부터 말하면 Cloudflare Queues incident 메모는 GraphQL backlog 평균과 point-in-time oldest message age를 같은 시계열로 취급하지 말아야 한다. backlog slope는 평균 queue 길이의 변화이고, oldest message timestamp는 특정 순간에 남아 있는 가장 오래된 메시지의 나이다.

    그래서 backlog가 줄어드는 그래프만 보고 회복됐다고 닫으면 안 된다. 평균은 내려오는데 특정 메시지 묶음이 오래 남아 있으면 age tail이 계속 남고, 이 경우 다음 triage는 retry 묶음이나 외부 API 정체 쪽으로 가야 한다.

    2. 어디서 실제로 막히는가

    실무에서 자주 섞이는 실패는 세 가지다. 첫째, GraphQL backlog 평균과 REST 또는 JavaScript API의 realtime oldest message 값을 같은 분 버킷으로 엮는다. 둘째, backlog messages가 줄면 곧바로 tail 메시지도 사라졌다고 가정한다. 셋째, oldest message age가 커졌다는 이유만으로 consumer concurrency를 다시 올리거나 batch size를 다시 건드린다.

    Cloudflare Metrics 문서는 backlog, consumer concurrency, message operations를 따로 보라고 하고, realtime backlog는 aggregated 평균이 아니라 point-in-time 값이라고 분명히 설명한다. JavaScript API 문서는 oldestMessageTimestamp를 독립 필드로 제공하고, pull consumers 문서는 timestamp_ms로 메시지 나이를 직접 계산할 수 있다고 적고 있다. 이 조합을 보면 age tail은 평균 queue 길이와 별개로 봐야 한다는 결론이 나온다.

    • 증상: backlog 그래프는 감소하는데 oldest message age가 계속 커진다.
    • 실패: realtime oldest message 값을 GraphQL 평균 backlog와 같은 성격의 데이터로 적는다.
    • 막힘: age tail을 봤는데도 곧바로 concurrency 조정부터 다시 시작한다.
    • 누락: backlog 평균을 캡처한 시간과 realtime oldest age를 폴링한 시각을 따로 적지 않는다.

    이미 batch size와 consumer concurrency 기본 분기 글과 upstream 429와 autoscale delay 분기 글을 지나왔다면, 이제 남는 문제는 평균 그래프와 tail age를 어떤 시간축으로 기록할지다.

    3. 실무에서 적용하는 순서

    가장 실용적인 순서는 다섯 단계다. 첫째, backlog 평균을 뽑은 GraphQL 시간 구간을 적는다. 둘째, 같은 시점 전후로 realtime oldest message timestamp를 폴링한 시각을 적는다. 셋째, oldest message age를 초 단위로 계산한다. 넷째, 같은 구간의 concurrency와 retry 또는 lag 평균을 나란히 둔다. 다섯째, backlog는 감소하지만 age tail이 남으면 그 메시지 묶음을 별도 cohort로 추적한다.

    1. GraphQL backlog 평균 구간을 먼저 고정한다.
    2. realtime oldest message 폴링 시각을 별도로 적는다.
    3. oldest age를 직접 계산한다.
    4. 동일 구간의 concurrency와 retry 평균을 옆에 둔다.
    5. age tail이 남으면 그 묶음을 별도 추적으로 넘긴다.

    실제 캡처를 볼 때는 그래프 제목, 범례, 시간축, 단위, 오른쪽 위 시각, API 응답 숫자를 따로 확인하고 기록하고 비교해야 한다. 화면에서는 backlog average 그래프를 먼저 확인하고, 바로 옆이나 아래에서 consumer concurrency를 확인하고, 별도 응답에서 oldestMessageTimestamp와 계산된 age 초 값을 읽고 저장한다. 같은 사건이라도 캡처 시각과 폴링 시각을 분리 표시하고 메모를 업데이트해야 나중에 재현, 검토, 공유가 빨라진다. 운영자는 같은 메모에서 수치를 다시 확인하고, 초 값을 계산하고, cohort를 분리하고, 다음 점검 시각을 저장하고, 팀 채널에 공유하고, 후속 triage 항목을 추가하고, 사건 메모를 다시 검토해야 한다. 한 번 더 확인하고, 다시 기록하고, 다시 비교하고, 다시 저장하고, 다시 업데이트하고, 재현 절차를 검토해야 한다. 추가로 API 응답을 조회하고, 관련 로그를 확인하고, 설정 값을 복사하고, 명령 출력과 파일 경로와 콘솔 결과를 함께 저장하면 복기가 쉬워진다.

    • 그래프 라벨을 확인하고 시간축과 단위를 기록한다.
    • 캡처 시각과 폴링 시각을 분리해 표시하고 저장한다.
    • backlog average와 oldest age와 concurrency를 같은 메모에서 비교한다.
    • tail 메시지 묶음 ID를 적고 다음 확인 시각을 업데이트한다.
    incident_window=2026-08-16T18:00:00Z..2026-08-16T18:05:00Z
    graph_backlog_avg=412
    graph_consumer_concurrency=8
    poll_at=2026-08-16T18:05:12Z
    api_oldest_message_timestamp=2026-08-16T17:58:05Z
    derived_oldest_age_seconds=427
    next_check=identify the specific message cohort still aging

    이 순서를 쓰면 'queue는 회복 중인데 몇 개의 메시지가 오래 남는 상황'과 '전체 queue가 아직 느린 상황'을 훨씬 빨리 가를 수 있다. 특히 oldest age가 커지는데 backlog 평균은 내려오고 retry 평균도 낮다면, 이미 대다수 메시지는 처리됐고 일부 lease 또는 외부 API 대기 묶음만 남았을 가능성이 크다.

    반대로 backlog 평균과 oldest age가 같이 올라가면 아직 큐 전체가 밀리는 것이다. 이때는 age tail보다는 batch size, concurrency ceiling, upstream 제한을 보는 쪽이 맞다. age tail 메모는 전체 회복 이후 남은 소수 묶음을 분리할 때 더 유용하다.

    4. 공식 문서와 예시 화면으로 확인하기

    첫 공식 화면은 Metrics 문서의 큰 축이다. Cloudflare는 backlog, consumer concurrency, message operations를 별도 데이터셋으로 보라고 안내한다.

    Cloudflare Queues는 backlog, consumer concurrency, message operations를 따로 측정하라고 문서에 명시한다.
    Cloudflare Queues는 backlog, consumer concurrency, message operations를 따로 측정하라고 문서에 명시한다.

    즉 backlog가 줄어든다는 관찰과 처리 품질이 회복됐다는 관찰은 같은 말이 아니다. oldest message age를 backlog 평균과 같은 줄에만 놓으면 남아 있는 오래된 메시지를 놓치기 쉽다.

    두 번째 자료는 Realtime backlog 구간이다. 문서는 realtime backlog가 aggregated 수치가 아니라 point-in-time 값이라고 적고 있다.

    realtime backlog는 aggregated 평균이 아니라 순간 시점 값이며, oldest message timestamp도 이 묶음에 포함된다고 Cloudflare가 설명한다.
    realtime backlog는 aggregated 평균이 아니라 순간 시점 값이며, oldest message timestamp도 이 묶음에 포함된다고 Cloudflare가 설명한다.

    그래서 GraphQL 분 단위 평균 backlog와 REST 또는 JavaScript API의 순간 oldest message 값은 같은 축으로 그대로 겹쳐 보면 안 된다. 각자 시점이 다르다는 사실부터 메모해야 한다.

    세 번째 자료는 JavaScript API의 QueueMetrics 인터페이스다. backlogCount, backlogBytes와 함께 oldestMessageTimestamp가 따로 노출된다.

    QueueMetrics는 backlogCount와 oldestMessageTimestamp를 별도 필드로 제공한다.
    QueueMetrics는 backlogCount와 oldestMessageTimestamp를 별도 필드로 제공한다.

    즉 오래된 메시지 문제를 보려면 backlog 개수만이 아니라 message age 필드를 같은 로그 묶음으로 남겨야 한다. queue 길이가 짧아도 오래된 메시지가 몇 개 버티는 상황은 따로 생길 수 있다.

    네 번째 자료는 Pull consumers 문서의 timestamp_ms 설명이다. Cloudflare는 현재 시각에서 timestamp_ms를 빼면 메시지 나이를 계산할 수 있다고 적고 있다.

    pull consumer의 timestamp_ms는 메시지 나이를 직접 계산하는 기준으로 쓸 수 있다.
    pull consumer의 timestamp_ms는 메시지 나이를 직접 계산하는 기준으로 쓸 수 있다.

    push consumer만 쓰더라도 이 사고방식이 유용하다. oldest message age를 backlog slope와 분리해 적을 때 결국 필요한 것은 평균 개수가 아니라 실제 메시지 나이다.

    실무에서는 backlog 평균과 oldest message age를 시간축부터 분리해야 한다. 순간값과 평균값을 섞으면 대기열은 회복되는데 특정 메시지 묶음만 오래 남는 상황을 잘못 읽게 된다.

    backlog slope와 oldest message age를 어떤 시간축으로 같이 적는지 정리한 표다.
    backlog slope와 oldest message age를 어떤 시간축으로 같이 적는지 정리한 표다.

    이미 upstream 429와 autoscale delay 분기 글을 먼저 갈랐다면, 이번 표는 그다음에 남는 age-tail을 backlog 평균과 분리 기록하는 단계다.

    마지막 자료는 운영 메모 예시다. 어떤 분 버킷의 평균 backlog를 봤는지와 어느 시각의 oldest_message_timestamp를 캡처했는지를 한 줄에 나눠 적는 편이 가장 실용적이다.

    backlog slope와 oldest message age를 같이 적을 때 남기면 좋은 최소 메모 예시다.
    backlog slope와 oldest message age를 같이 적을 때 남기면 좋은 최소 메모 예시다.

    이 정도만 남겨도 queue 회복 그래프와 오래된 메시지 tail을 같은 사건으로 오해하는 시간이 줄어든다. age tail은 보통 별도 retry 묶음이나 외부 API 정체와 연결된다.

    5. 주의사항과 리스크

    첫 번째 리스크는 point-in-time oldest age를 평균 backlog 그래프 위에 덧칠해 같은 종류의 데이터처럼 읽는 것이다. 두 번째 리스크는 backlog가 줄었다는 사실만으로 SLA가 회복됐다고 닫는 것이다. 세 번째 리스크는 age tail을 봤는데도 queue 전체 설정을 다시 흔드는 것이다.

    • 평균값과 순간값은 같은 축으로 그대로 합치지 않는다.
    • oldest age는 남아 있는 tail 메시지의 신호일 수 있다.
    • age tail이 남는다고 항상 consumer concurrency를 다시 올리는 것은 아니다.

    6. 결론

    Cloudflare Queues에서 backlog는 내려오는데 oldest message age만 남는다면, 평균 queue 길이와 point-in-time age를 별도 시간축으로 적는 편이 가장 빠르다. 그래야 queue 전체 지연과 소수 tail 메시지 지연을 같은 장애로 섞지 않는다.

    요약하면 backlog slope는 평균 회복 속도, oldest message age는 남아 있는 tail의 나이다. 둘을 같이 남기되 같은 값처럼 읽지 않는 것이 핵심이다.

    그 다음 단계로는 oldest message age tail이 남을 때 retry delay와 lease 재전달 증거를 다시 나누는 follow-up 글을 붙여 두면, 평균 backlog와 순간 age를 분리한 뒤 남는 tail 원인까지 한 가지 흐름으로 이어서 볼 수 있다.

    7. 참고 링크

    1. https://developers.cloudflare.com/queues/observability/metrics/
    2. https://developers.cloudflare.com/queues/configuration/javascript-apis/
    3. https://developers.cloudflare.com/queues/configuration/pull-consumers/
    4. https://developers.cloudflare.com/queues/platform/limits/
Designed by Tistory.