unix-sockets

1 개의 포스트

cloudflare

hyper HTTP 라이브러리에서 버그를 발견한 방법 (새 탭에서 열림)

Cloudflare의 Images binding을 로컬 Unix 소켓 기반 구조로 개편한 뒤, 대용량 이미지 응답이 간헐적으로 잘리는 버그가 발생했다. 응답은 HTTP 200과 정상적인 `Content-Length`를 반환했지만 실제 본문은 수백 KB만 전달되어 이미지가 부분적으로 렌더링되거나 디코딩에 실패했다. 원인은 특정 조건에서 발생하는 hyper 라이브러리의 레이스 컨디션이었으며, 최종적으로 네 줄의 코드 수정으로 해결됐다. ### Images binding과 hyper의 데이터 흐름 - Workers는 바인딩을 통해 Images 서비스에 이미지 데이터를 직접 전달하고, 변환 결과를 스트림으로 받을 수 있다. - 이미지 변환 과정은 다음과 같이 진행된다. - Workers 런타임이 소켓을 통해 Images 서비스에 요청을 보낸다. - Images 서비스가 이미지를 합성·리사이즈·트랜스코딩한다. - 변환된 전체 이미지를 메모리 블록으로 hyper에 전달한다. - hyper가 데이터를 내부 버퍼에 저장한 뒤 소켓의 송신 버퍼로 기록한다. - 소켓의 양 끝에는 커널이 관리하는 버퍼가 있다. - 수신자가 충분히 빠르면 hyper가 한 번에 모든 데이터를 보내고 소켓을 종료할 수 있다. - 수신자가 조금이라도 느리면 송신 버퍼가 가득 차고, hyper는 공간이 생길 때까지 추가 쓰기를 기다려야 한다. ### 네트워크 중계에서 로컬 Unix 소켓으로 전환 - 초기 Images binding은 Workers 런타임과 Images 사이에서 FL이라는 내부 중계 서비스를 거쳤다. - FL은 DNS 조회와 라우팅 등 전체 네트워크 처리 파이프라인을 수행했기 때문에 오버헤드가 있었다. - 2025년 12월, Cloudflare는 FL을 같은 머신에서 실행되는 내부 Worker binding으로 교체했다. - 새 구조는 네트워크 소켓 대신 Unix 소켓으로 서비스를 직접 연결했다. - 이 변경으로 다음 효과를 기대했다. - 네트워크 스택과 FL 처리 과정 제거 - Images 요청 경로 단축 - Images 팀이 독립적으로 binding을 배포하고 변경 가능 - 그러나 출시 며칠 뒤부터 대용량 이미지 응답 실패 제보가 접수됐다. ### HTTP 200이지만 잘린 응답 - 고객 사례는 두 단계의 이미지 처리 파이프라인을 중첩한 비표준 구성이었다. - 내부 파이프라인: R2의 JPEG와 PNG를 합성해 JPEG 생성 - 외부 파이프라인: 결과 이미지를 다시 압축·변환·리사이즈 - 실제 문제는 내부 transformation binding의 반환 경로에서 발생했다. - 외부 파이프라인은 내부 응답에서 다음과 같은 모순을 받았다. - 상태 코드는 `200 OK` - `Content-Length`는 수 MB로 설정 - 실제 수신 본문은 일부 데이터에 불과함 - 한 사례에서는 예상 크기 3.3MB 중 약 200KB만 전달됐다. - 상위 계층에서는 다음 오류가 발생했지만, 실제 원인이 어느 서비스에 있는지는 즉시 알기 어려웠다. ```text error reading a body from connection: end of file before message length reached ``` - 브라우저에서는 이미지 형식에 따라 일부만 표시되거나, 하단이 회색으로 남거나, 아예 깨진 이미지로 표시됐다. ### 재현과 원인 범위 좁히기 - 개발팀은 고객의 중첩 파이프라인을 재현하는 Worker를 만들었다. - 이후 외부 파이프라인과 여러 계층을 하나씩 제거해 binding 단독으로도 문제를 재현할 수 있음을 확인했다. - 배치 요청을 보내는 간단한 스크립트로 재현을 자동화했다. - 초기 실행에서는 25건 중 19건이 실패했다. - 매번 도착한 데이터가 약 200KB였는데, 이는 운영 환경의 소켓 버퍼 크기와 매우 유사했다. - 이를 통해 문제는 고객 설정이 아니라 소켓 버퍼가 가득 찬 뒤 hyper가 응답 전송과 연결 종료를 처리하는 방식과 관련 있음을 추정할 수 있었다. - 최종 원인은 특정 타이밍에서 발생하는 hyper 내부의 레이스 컨디션으로 밝혀졌고, 수정에는 네 줄의 코드만 필요했다. ### 실용적인 결론 소켓 기반 스트리밍에서는 `200 OK`나 올바른 `Content-Length`만으로 전송 성공을 판단할 수 없다. 특히 송신 버퍼가 가득 차는 상황, 느린 수신자, 데이터 기록과 연결 종료가 동시에 일어나는 경로를 반드시 테스트해야 하며, 대용량·중첩 파이프라인을 포함한 재현 테스트가 간헐적인 네트워크 버그를 찾는 데 결정적이다.