pile·
백엔드·cloudflare-blogCloudflare Blog·

hyper HTTP 라이브러리의 버그를 발견한 방법

Cloudflare의 Images 서비스를 Unix 소켓 기반 아키텍처로 재구성한 후, 대용량 이미지 응답이 중간에 잘리는 버그가 발생했다. 14.8MB 응답에서 219KB만 전달되고 HTTP 200 OK는 정상 반환되어 애플리케이션 레벨에서 탐지가 불가능했다. 원인은 hyper 라이브러리의 dispatch 루프에서 flush 완료 여부를 확인하지 않고 연결을 종료하는 경쟁 조건이었으며, strace로 커널 호출 순서를 추적해 root cause를 특정했다. 최종 수정은 upstream PR #4018로 hyper 레포에 병합됐다.

핵심 포인트
  • 대용량 응답(14.8MB)이 219KB만 전송되고 나머지는 손실, HTTP 200 OK는 정상 반환되어 애플리케이션 레벨 로그/추적에서는 오류가 없었다.
  • 원인은 hyper dispatch.rs의 poll_flush(cx) 결과를 무시하는 코드 — flush가 Pending이어도 연결을 종료해 내부 버퍼 데이터가 유실됐다.
  • 소켓 버퍼가 가득 차는 상황(새 아키텍처에서 reader가 약간 느려짐)이 생기면서 기존에 숨어있던 경쟁 조건이 노출됐다.
  • strace로 커널 수준 syscall 추적 — 실패 요청은 sendto 1회(219KB) 직후 shutdown, 정상 요청은 sendto 여러 번 후 shutdown임을 확인했다.
  • 최종 수정: poll_shutdown에서 poll_flush를 먼저 완료한 후 소켓을 종료하도록 변경, upstream PR #4018로 병합됐다.
상세 정리
  • 발단: December 2025 아키텍처 변경 — FL 중간 서비스를 Unix 소켓 기반 내부 바인딩으로 교체했고, 새 reader가 이전보다 약간 느려졌다.
  • 증상: 25개 테스트 요청 중 19개가 응답 잘림, 타임아웃이나 5xx 없이 200 OK 반환으로 애플리케이션 레벨 로그/추적에서 오류가 없었다.
  • 초기 가설 — 타임아웃: 요청 기간과 무관하게 버그 재현, 기각됐다.
  • 초기 가설 — hyper 버전 문제: 0.14, 1.7, 1.8 모두 동일 버그 확인, 버전 업그레이드로 해결 불가였다.
  • 로컬 재현 불가: 로컬 환경에서는 reader가 충분히 빠르게 소켓을 비워 버그가 노출되지 않았다.
  • strace 투입: 커널 수준 syscall을 추적해 실패 요청에서 sendto 1회(219KB) 직후 즉시 shutdown이 호출됨을 확인했다.
  • 코드 분석: dispatch.rs의 let _ = self.poll_flush(cx)? 구문이 Poll::Pending 반환값을 _ 로 무시하고 있었다.
  • flush가 Pending을 반환하면 내부 버퍼에 아직 데이터가 남아있는 상태인데, 이를 무시하고 연결을 종료해 나머지 데이터가 유실됐다.
  • 소켓 버퍼가 가득 찬 상황(느린 reader)에서만 flush가 Pending을 반환하므로 로컬에서는 재현되지 않았다.
  • 초기 수정: dispatch 루프에서 flush_result가 Pending이면 Poll::Pending을 반환하도록 변경했다.
  • 최종 수정(upstream): poll_shutdown에서 먼저 poll_flush를 완료하는 방식으로 변경 — keepalive 연결 처리와의 상호작용을 고려한 더 적절한 구조였다.
  • upstream PR #4018이 hyperium/hyper에 병합됐고, Cloudflare 내부 포크에 즉시 적용해 Images 서비스를 안정화했다.
왜 읽나Rust async I/O 라이브러리의 숨겨진 경쟁 조건을 strace로 추적한 실전 디버깅 케이스로, 대용량 스트리밍 응답이나 async Rust I/O를 다루는 백엔드 엔지니어에게 유용한 레퍼런스다.
cloudflare-blog
Cloudflare Blog 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 백엔드·twilio-engTwilio Engineering·

    Programmable Messaging에서 Verify API로 마이그레이션하기

    Twilio의 Programmable Messaging API로 자체 OTP 솔루션을 운영하던 서비스가 Verify API로 전환하는 방법을 코드 예시와 함께 설명한다. Verify는 OTP 전송·검증을 위한 전용 API로, 전화번호 구매, 토큰 생성, DB 저장·만료 관리를 내부에서 처리해 개발자가 직접 구현할 코드를 크게 줄인다.

    요약 이어보기
    #authentication#twilio#sms+2
  2. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 2부

    포스타입 백엔드 엔지니어가 벡터 기반 개인화 추천 시스템을 실제 운영하며 맞닥뜨린 성능 장애와 용량 문제를 해결한 과정을 담은 2부다. 수백만 개의 벡터 KNN 검색이 피크 시간대에 전체 Elasticsearch 검색 성능을 흔드는 문제부터 클러스터 OOM 사태까지, 쿼리 최적화와 인프라 분리 두 가지 경로로 근본 해결에 이른다.

    요약 이어보기
    #elasticsearch#vector-search#recommendation-system+2
  3. 백엔드·포스타입포스타입·

    포스타입이 개인화 추천을 하는 방법 1부

    포스타입이 태그 기반 추천의 한계를 극복하고 벡터 임베딩 기반 개인화 추천 시스템을 구축한 과정을 담은 1부다. 유사한 콘텐츠가 다른 용어를 쓰거나 동일한 태그가 전혀 다른 톤의 콘텐츠를 가리키는 문제를 임베딩 벡터로 해결하고, OpenSearch의 HNSW ANN 검색으로 수백만 벡터를 실시간 검색하는 시스템을 구축해 구매율 15% 향상을 달성했다.

    요약 이어보기
    #opensearch#vector-search#recommendation-system+2