GitHub Freno: 협력적 고가용성 스로틀러 서비스
Freno는 애플리케이션이 데이터베이스(MySQL 등)에 데이터를 쓰기 전에 요청을 중계하여 과부하를 막아주는 협력적 스로틀러입니다. 대량의 업데이트나 스키마 변경 작업 시 발생할 수 있는 데이터베이스 복제 지연(Replication Lag) 문제를 방지하여 서비스 안정성을 높여줍니다. Raft 합의 알고리즘을 기반으로 고가용성을 보장하며, 클라이언트는 HTTP 요청을 통해 안전하게 쓰기 작업을 진행할 수 있습니다.
Freno는 협력적이며 고가용성을 갖춘 스로틀러(throttler) 서비스입니다. 클라이언트는 리소스에 대한 쓰기 작업을 제어(throttle)하기 위해 freno를 사용합니다. 현재 구현된 버전은 여러 MySQL 클러스터의 복제(Replication) 상태를 기반으로 쓰기 작업을 제어할 수 있습니다. 복제 지연(lag)이 미리 정의된 임계치를 초과하면 freno는 협력적인 클라이언트의 쓰기 작업을 제한합니다. freno는 서버 인벤토리의 변경 사항에 동적으로 적응하며, 사용자가 직접 특정 앱의 쓰기를 강제로 제한하도록 제어할 수도 있습니다. freno는 고가용성을 제공하며 리더십을 결정하고 구성원 노드 간에 사용자 이벤트를 전달하기 위해 Raft 합의 프로토콜을 사용합니다.
협력적 스로틀러인 freno는 백엔드 저장소(현재는 MySQL만 지원)에서 데이터를 수집하며, "백엔드 저장소에 쓰기를 해도 되는가?"라는 질문에 답할 수 있는 로직을 갖추고 있습니다. 클라이언트(애플리케이션, 스크립트, 작업)는 freno에 이를 문의해야 합니다. freno는 클라이언트와 백엔드 저장소 사이의 프록시(Proxy)가 아닙니다. 단순히 저장소를 관찰하고 "쓰기 가능" 또는 "쓰기 중단" 상태를 알려줄 뿐입니다. 클라이언트는 freno에 문의하고 그 권고 사항을 존중해야 합니다.
저장소 및 앱 freno는 데이터 저장소별로 데이터를 수집합니다. 예를 들어 MySQL 클러스터를 탐색할 때 클러스터별로 복제 지연을 독립적으로 수집합니다. 백엔드 저장소의 메트릭은 자동으로 수집되며 절대적인 사실로 간주됩니다. freno는 '앱(app)'으로 식별되는 클라이언트에 서비스를 제공합니다. freno는 협력적 방식이므로 앱이 스스로를 식별할 것이라고 신뢰합니다. 앱은 관리될 수 있습니다. 즉, freno에게 특정 앱의 작업을 강제로 제한하도록 지시할 수 있습니다. 이는 우선순위가 높은 다른 앱들이 작업을 완료할 수 있도록 하기 위함입니다. freno는 단순히 무엇을 제한할지에 대한 지시만 받을 뿐, 자체적인 스케줄링이나 우선순위 지정 로직은 가지고 있지 않습니다.
MySQL freno는 원래 MySQL 제어에 대한 통합적이고 자체 적응 가능한 솔루션을 제공하도록 설계되었습니다. 즉, 복제 지연을 낮게 유지하면서 쓰기를 제어하는 역할입니다. freno는 미리 정의된 MySQL 클러스터 목록으로 구성됩니다. 여기에는 자격 증명, 지연(또는 기타) 검사 쿼리, 예상 임계치가 포함될 수 있습니다. 각 클러스터에 대해 freno는 데이터를 탐색하고 수집할 서버를 알아야 합니다. 각 클러스터별로 다음 목록을 제공할 수 있습니다:
- 정적 목록: 호스트 이름[:포트]가 하드코딩된 목록
- 동적 목록: 호스트는 자유롭게 추가되거나 삭제될 수 있으며, 제어 방식은 이러한 변경에 맞춰 동적으로 적응합니다. 지원되는 동적 옵션은 다음과 같습니다. HAProxy를 통해 제공: freno에 HAProxy URL 및 백엔드/풀 이름을 제공하면, freno는 주기적으로 해당 풀에서 활성화된 서버 목록을 파싱하고 동적으로 탐색을 적용합니다.
사용 사례 freno는 대량 작업, 예를 들어 대규모 데이터 로딩/아카이빙, 스키마 마이그레이션, 대규모 업데이트에 매우 유용합니다. 이러한 작업들은 일반적으로 수천에서 수백만 건의 행(Row)을 거치며 MySQL 복제 지연과 같은 원치 않는 부작용을 초래할 수 있습니다. 이러한 작업을 작은 하위 작업(예: 한 번에 100개 행)으로 나누고, 각 하위 작업을 적용하기 전에 freno에 문의함으로써 데이터베이스와 이를 사용하는 애플리케이션에 악영향을 주지 않고 동일한 결과를 얻을 수 있습니다. freno는 복제본의 유효성을 추론하기 위한 실제 지연 측정에도 사용될 수 있으며, 이는 마스터 노드에서 직접 읽어올 때 발생하는 '쓰기 후 읽기(Write-then-read)' 문제를 완화하는 데 도움을 줍니다.
HTTP freno는 HTTP를 통해 요청을 제공합니다. 가장 중요한 요청은 "이 앱이 이 저장소에 쓰기를 해도 되는가?"를 묻는 확인 요청입니다. freno는 HEAD 요청을 권장하며(GET도 허용되지만 오버헤드가 더 큽니다), 다음과 같은 상태 코드로 응답합니다:
- 200 (OK): 애플리케이션이 데이터 저장소에 쓸 수 있습니다.
- 404 (Not Found): 알 수 없는 메트릭 이름입니다.
- 417 (Expectation Failed): 요청한 애플리케이션은 쓰기가 명시적으로 금지되었습니다.
- 429 (Too Many Requests): 쓰지 마십시오. 저장소 상태가 예상 임계치를 충족하지 못함을 나타내는 정상적인 상태입니다.
- 500 (Internal Server Error): 내부 오류입니다. 쓰지 마십시오.
클라이언트 클라이언트는 일반적으로 HEAD를 통해 /check/... 요청을 보냅니다. 클라이언트는 초당 수많은 요청을 보낼 수 있습니다. freno는 리소스 소모가 적습니다. 초당 수백 번 freno를 호출해도 문제가 없습니다. (물론 하드웨어와 리소스에 따라 다릅니다.)