GraphQL BatchLoader
GitLab에서 GraphQL 쿼리 실행 시 N+1 SQL 쿼리를 방지하기 위한 batch-loader Ruby gem 사용 방법을 설명합니다.
GitLab은 batch-loader Ruby gem을 사용하여 N+1 SQL 쿼리를 최적화하고 방지합니다. GraphQL 쿼리 트리의 특성상 배칭(batching) 기회가 발생합니다. 서로 연결되지 않은 노드들이 동일한 데이터를 필요로 할 수 있지만, 각 노드가 서로의 존재를 알 수는 없기 때문입니다. 언제 사용해야 하나요? # GraphQL 쿼리 실행 중에는 DB 요청을 가능한 한 배칭 처리해야 합니다. 뮤테이션 은 직렬로 실행되므로 배칭이 필요하지 않습니다. 데이터베이스 쿼리를 실행해야 하고, 두 개의 유사한(반드시 동일하지 않아도 되는) 쿼리를 결합할 수 있다면 batch-loader 사용을 고려하세요. 새 엔드포인트를 구현할 때는 SQL 쿼리 수를 최소화하는 것을 목표로 해야 합니다. 안정성과 확장성을 위해 N+1 성능 문제가 발생하지 않도록 반드시 확인해야 합니다. 구현 # 배치 로딩은 입력 Qα, Qβ, ... Qω 에 대한 일련의 쿼리를 Q[α, β, ... ω] 에 대한 단일 쿼리로 결합할 수 있을 때 유용합니다. ID로 조회하는 것이 대표적인 예시인데, 사용자 이름으로 두 명의 사용자를 찾는 것이 한 명을 찾는 것만큼 저렴하게 처리될 수 있습니다. 하지만 실제 사례는 더 복잡할 수 있습니다. 배치 로딩은 결과 집합에 서로 다른 정렬 순서, 그룹화, 집계 또는 기타 결합 불가능한 특성이 있는 경우에는 적합하지 않습니다. 코드에서 batch-loader를 사용하는 방법은 두 가지입니다. 간단한 ID 조회의 경우 ::Gitlab::Graphql::Loaders::BatchModelLoader.new(model, id).find 를 사용하세요. 더 복잡한 경우에는 batch API를 직접 사용할 수 있습니다. 예를 들어, username 으로 User 를 로드하려면 다음과 같이 배칭을 추가할 수 있습니다: class UserResolver < BaseResolver type UserType, null: true argument :username, ::GraphQL::Types::String, required: true def resolve(**args) BatchLoader::GraphQL.for(username).batch do |usernames, loader| User.by_username(usernames).each do |user| loader.call(user.username, user) end end end end username 은 조회하려는 사용자 이름입니다. 하나의 이름이거나 여러 이름일 수 있습니다. loader.call 은 결과를 입력 키에 매핑하는 데 사용됩니다(여기서 user는 해당 username에 매핑됩니다). BatchLoader::GraphQL 은 지연 객체(데이터를 가져오는 지연된 promise)를 반환합니다. BatchLoading 메커니즘 사용 방법을 설명하는 예시 머지 리퀘스트 를 참조하세요. BatchModelLoader # ID 조회의 경우 BatchModelLoader 사용