데이터베이스 부하 분산 기술 선택
MyBest는 미들웨어 방식과 애플리케이션 방식 중 애플리케이션 방식(Rails 6.0 이상 다중 데이터베이스 지원)을 선택했습니다. 이는 Rails와 Ruby에 대한 내부 엔지니어들의 숙련도를 활용하여 초기 구현 및 장기적인 유지보수 용이성을 확보하기 위함이었습니다. 조직의 케이퍼빌리티를 고려한 선택으로, 새로운 의존성 추가 없이 Rails/Ruby 환경 내에서 제어 가능한 이점을 강조했습니다.
GraphQL과 Rails 통합 시 문제점 및 해결책
- HTTP 메서드 기반 DB 분기 불가: Rails의 기본 Read/Write Splitting 기능은 HTTP GET/HEAD 요청을 Reader로, POST/PUT/DELETE/PATCH 요청을 Writer로 자동 분기합니다. 그러나 GraphQL은 모든 요청을 HTTP POST로 처리하므로 Rails의 기본 기능 활용이 불가능했습니다.
- 해결: CQRS(Command Query Responsibility Segregation) 패턴을 참고하여 GraphQL Query는 Reader, Mutation은 Writer로 명확하게 분리하는 전략을 채택했습니다.
graphql-rubygem의execute_multiplex메서드를 후킹하여, 요청 내 모든 오퍼레이션이 Query일 경우에만 Reader DB에 연결하도록 구현했습니다.
- 해결: CQRS(Command Query Responsibility Segregation) 패턴을 참고하여 GraphQL Query는 Reader, Mutation은 Writer로 명확하게 분리하는 전략을 채택했습니다.
- Query 내 데이터 업데이트 가능성: GraphQL Query는 원칙적으로 데이터를 조회해야 하지만, 실제 구현에서는 Query 내에서 데이터 업데이트(예:
find_or_create_by)가 발생할 수 있어 Read-only 보장이 어려웠습니다.- 해결: 화이트리스트 방식의 Read-Write Splitting을 도입했습니다. Read-only가 확실한 Query만 화이트리스트에 등록하고, 해당 Query만 Reader DB에 연결하도록 했습니다. 또한,
ActiveSupport::Instrumentation을 활용하여 개발 및 스테이징 환경에서sql.active_record이벤트를 구독, Query 실행 중 업데이트 SQL이 발생하면 예외를 발생시켜 부작용 있는 Query를 사전에 방지했습니다.
- 해결: 화이트리스트 방식의 Read-Write Splitting을 도입했습니다. Read-only가 확실한 Query만 화이트리스트에 등록하고, 해당 Query만 Reader DB에 연결하도록 했습니다. 또한,
무중단 릴리스 및 점진적 도입 전략
-
초기 적용: 세 가지 GraphQL 스키마(내부 관리 화면, 웹, 모바일 앱) 중 사용 시간대가 제한적인 내부 관리 화면 스키마부터 Read-Write Splitting을 적용했습니다.
-
점진적 확장: 화이트리스트 방식 덕분에 관리 화면 내에서도 특정 액션에 연결된 Query부터 소규모로 도입하여 성공 경험을 쌓았습니다. 이후 Datadog의 텔레메트리 데이터를 분석하여 총 실행 시간이 긴 Query부터 우선순위를 정해 화이트리스트에 추가하며 점진적으로 확장했습니다.
-
결과: 이러한 노력을 통해 데이터베이스 CPU 사용률이 기존 70%에서 20% 전후로 안정화되었으며, 데이터베이스 스케일 아웃 문제도 성공적으로 해결되었습니다.