해결책
SoundCloud의 백엔드는 손수 만든 REST/JSON 인터페이스를 갖춘 마이크로서비스 위에서 돌아갔다. 디버깅은 쉬웠지만, HTTP 메서드, JSON 필드 이름, 타임스탬프 형식, 오류 형태 등 모든 엔드포인트의 관례를 그것을 만든 팀이 각자 정해야 했다. 내부 가이드라인만으로는 역부족이었다. 한 클라이언트가 snake_case를 기대하는 서버에 camelCase 필드를 보내 장애를 일으킨 적이 있었고, 어떤 엔드포인트를 이해하려면 계약서가 아니라 애플리케이션 코드를 읽어야 했다.
SoundCloud는 Twitch의 Twirp RPC 프로토콜을 Scala/Finagle로 구현한 Twinagle을 만들었다. 이를 통해 엔지니어들은 API를 protobuf로 한 번 정의하고 그로부터 서버와 클라이언트 코드를 생성할 수 있었다. 그들은 트래픽이 많은 트랙 인가 서비스를 가장 먼저 마이그레이션했다. 곧바로 전환하는 대신, 비율 기반 플래그 뒤에서 REST와 Twirp를 나란히 실행했다. 낮은 롤아웃 비율에서는 두 엔드포인트를 모두 호출해 비교하되 REST 응답만 반환했기 때문에, 불일치는 사용자에게 보이는 버그가 아니라 로그로 기록된 불일치로 드러났다.
달성한 것
새 엔드포인트로 트래픽을 미러링하고, 비교(diff)한 뒤, 전환하다
하나의 protobuf 계약에서 서버와 클라이언트 코드를 생성함으로써, 팀들이 각자 JSON 필드 명명과 HTTP 관례를 만들어내면서 생기던 부류의 버그가 사라졌다.
전환하기 전에 작은 트래픽 비율로 두 엔드포인트를 병렬 실행함으로써, 팀은 사용자 누구도 잘못된 응답을 보지 않은 채 응답 불일치를 잡아낼 수 있었다.
단일 클라이언트의 고트래픽 서비스부터 시작함으로써 첫 마이그레이션의 통합 범위를 작게 유지하면서도 실제 부하에서 Twinagle을 입증할 수 있었다.
기존 비즈니스 로직은 그대로 두고 새 코드를 얇은 직렬화 계층에 국한시킴으로써 새 버그가 숨을 수 있는 곳을 제한했다.
작동하는 시스템을 폐기하기 전에, 대체 시스템이 모든 요청에 병렬로 응답하게 하고 그것을 기존 시스템과 비교(diff)하라. 그러면 기존 시스템이 여전히 응답을 제공하는 동안 버그가 드러난다.
그 후
섀도 비교 롤아웃은 여러 치명적인 버그가 사용자에게 도달하기 전에 잡아냈고, SoundCloud는 트랙 인가 서비스를 첫 시도에 장애도 버그도 없이 Twirp 트래픽 100%로 끌어올렸다. 두 서비스 간 대역폭은 JSON에서 바이너리 protobuf로 전환하면서 40% 이상 줄었고, 팀은 해당 서비스의 모든 자체 JSON 직렬화 코드를 삭제했다. SoundCloud는 이어서 다른 서비스들도 Twinagle로 전환했고, 앞으로 모든 신규 서비스의 기본값으로 삼았다.
근거 확인하기
출처
- Announcing Twinagle: Twirp and Protobuf for Finagle SoundCloud Engineering · developers.soundcloud.com ·