데이터베이스 리뷰 지침

이 페이지는 데이터베이스 리뷰에 관한 내용입니다. 일반적인 코드 리뷰에 대한 광범위한 조언 및 모범 사례는 코드 리뷰 가이드를 참조하세요.

일반 프로세스

데이터베이스 리뷰가 필요한 경우:

  • 데이터베이스 스키마에 영향을 주거나 데이터 마이그레이션을 수행하는 변경 사항입니다. 다음 디렉토리에 있는 파일을 포함합니다:
    • db/
    • lib/gitlab/background_migration/
  • 데이터베이스 도구에 대한 변경 사항입니다. 예를 들어:
    • lib/gitlab/database/의 마이그레이션 또는 ActiveRecord 도우미
    • 로드 밸런싱
  • 명백한 범위를 벗어나는 SQL 쿼리를 생성하는 변경사항입니다. 일반적으로 복잡한 쿼리가 도입되고 데이터베이스 리뷰가 필요한지에 대한 결정은 병합 요청 작성자에게 달려 있습니다.
  • count, distinct_count, estimate_batch_distinct_countsum을 사용하는 서비스 데이터 메트릭의 변경사항입니다. 이러한 메트릭은 대규모 테이블에 걸친 복잡한 쿼리를 가질 수 있습니다. 구현 세부 정보는 분석 계측 가이드를 참조하세요.
  • ActiveRecord 객체에 대해 update, upsert, delete, update_all, upsert_all, delete_all 또는 destroy_all 메서드를 사용하는 변경사항입니다.

데이터베이스 리뷰어는 변경 내용에서 지나치게 복잡한 쿼리를 찾아 더 면밀히 검토해야 합니다. 작성자가 특정 쿼리를 리뷰하기 위해 지적하지 않고 너무 복잡한 쿼리가 없는 경우, 마이그레이션에 집중하는 것만으로 충분합니다.

필수 사항

~데이터베이스 리뷰를 요청할 때 다음 항목을 제공해야 합니다. 병합 요청 설명에 이러한 항목이 포함되지 않은 경우 리뷰는 작성자에게 재할당됩니다.

마이그레이션

새로운 마이그레이션이 도입된 경우, 데이터베이스 리뷰어는 모든 마이그레이션에 대한 Migrating(db:migrate) 및 Rolling Back (db:rollback)의 결과를 검토해야 합니다.

GitLab에는 (db:check-migrations) 파이프라인 작업에서 제공되는 자동화된 도구가 있으며, 이를 통해 CI 작업 로그에서 이러한 결과를 제공합니다. 병합 요청 설명에 이러한 결과를 제공하는 것은 필수는 아니지만 리뷰어에게 도움이 될 수 있습니다. 봇은 또한 마이그레이션이 올바르게 되돌릴 수 있는지 확인합니다.

쿼리

새로운 쿼리가 도입되거나 기존 쿼리가 업데이트된 경우, 다음을 제공해야 합니다. - 병합 요청에 포함된 각 raw SQL 쿼리에 대한 쿼리 계획 및 각 raw SQL 스니펫 뒤를 따라가는 쿼리 계획 링크. - 변경되거나 추가된 모든 쿼리에 대한 Raw SQL (ActiveRecord 쿼리에서 변환된 내용대로). - 기존 쿼리를 업데이트하는 경우, 변경 전 쿼리와 변경 후 쿼리의 Raw SQL을 제공해야 합니다.

이 정보를 제공하는 방법은 쿼리 추가 또는 수정 시 준비 사항을 참조하세요.

역할과 프로세스

병합 요청 작성자의 역할은 다음과 같습니다:

데이터베이스 리뷰어의 역할은 다음과 같습니다:

  • 필수 항목이 제공되었는지 확인하고 적절한 형식으로 되어 있는지 확인합니다. 그렇지 않으면 병합 요청을 작성자에게 재할당합니다.
  • MR에 대한 첫 번째 검토를 수행하고 작성자에게 개선 사항을 제안합니다.
  • 만족한 경우 MR에 ~"database::reviewed" 라벨을 지정하고 승인을 요청하며, 리뷰어 룰렛이 제안한 데이터베이스 메인테이너에게 리뷰를 요청합니다. 이 작업이 완료되면 리뷰어 자신을 리뷰어에서 제거합니다.

데이터베이스 메인테이너의 역할은 다음과 같습니다:

  • MR에 대한 최종 데이터베이스 리뷰를 수행합니다.
  • 리뷰어 및 MR 작성자와 추가 개선 사항이나 관련 변경 사항에 대해 논의합니다.
  • MR을 최종 승인하고 MR에 ~"database::approved" 라벨을 지정합니다.
  • 다른 승인이 보류 중이 아니거나 필요에 따라(프론트엔드, 백엔드, 문서화) 다른 관리자에게 전달하거나 MR을 병합합니다.
    • 병합하지 않는 경우, 리뷰어 자신을 리뷰어에서 제거합니다.

리뷰 작업량 분산

리뷰 작업량은 리뷰어 룰렛을 사용하여 분산됩니다 (예시). 병합 요청 작성자는 제안된 데이터베이스 리뷰어에게 리뷰를 요청해야 합니다. 해당 리뷰어가 승인하면 제안된 데이터베이스 메인테이너로 전환합니다.

리뷰어 룰렛이 데이터베이스 리뷰어 및 메인테이너를 제안하지 않은 경우, ~database 라벨을 적용했는지 확인하고 danger-review CI 작업을 재실행하거나 @gl-database에서 누군가를 선택하세요.

데이터베이스 검토를 위한 병합 요청 준비 방법

검토를 쉽고 빠르게 만들기 위해 다음 준비 사항을 고려하세요.

마이그레이션 추가시 준비

  • 문서화된 대로 db/structure.sql이 업데이트되었는지 확인하고 추가 또는 제거된 db/schema_migrations의 관련 버전 파일을 확인하세요.
  • 문서화된 대로 데이터베이스 사전이 업데이트되었는지 확인하세요. (자세한 정보)
  • change 메소드를 사용하거나 up를 사용할 때 down 메소드를 포함하여 마이그레이션을 역으로 만드세요.
    • 롤백 절차를 포함하거나 변경 사항을 롤백하는 방법을 설명하세요.
  • db:check-migrations 파이프라인 작업이 성공적으로 실행되었는지 확인하고 마이그레이션 롤백이 예상대로 작동하는지 확인하세요.
    • db:check-schema 작업이 성공적으로 실행되었고 롤백시 예상치 못한 스키마 변경이 없는지 확인하세요. 이 작업은 스키마가 변경된 경우에만 경고를 트리거할 수 있습니다.
    • 리뷰 프로세스 중에 마이그레이션을 수정할 때 앞에서 언급한 작업이 계속 성공적으로 실행되는지 확인하세요.
  • 필요한 경우 spec/migrations에 마이그레이션에 대한 테스트를 추가하세요. 자세한 내용은 GitLab에서 Rails 마이그레이션 테스트하기에서 확인하세요.
  • 모든 트랜잭션 마이그레이션에는 기본적으로 잠금 재시도가 활성화되어 있어야 합니다. 트랜잭션하지 않는 마이그레이션의 경우 사용 사례와 해결책에 대한 문서를 검토하세요.
  • 유효한 이유가 없는 경우 RuboCop 확인이 비활성화되어 있지 않도록 확인하세요.
  • 대용량 테이블에 인덱스를 추가할 때, Database Lab에서 CREATE INDEX CONCURRENTLY를 사용하여 실행을 테스트하고 실행 시간을 MR 설명에 추가하세요.
    • Database Lab의 실행 시간은 GitLab.com의 실행 시간과 크게 다를 수 있지만, Database Lab의 실행 시간이 높다면 GitLab.com에서의 실행 시간도 상당히 높다는 힌트를 얻을 수 있습니다.
    • Database Lab의 실행 시간이 10분보다 길 경우, 인덱스포스트 마이그레이션으로 이동해야 합니다. 이 경우에는 인덱스가 필요한 코드가 배포될 때 인덱스가 제대로 적용되도록 마이그레이션 및 애플리케이션 변경을 각각 별도의 릴리스로 나눌 필요가 있습니다.
  • 데이터베이스 테스팅 작업(db:gitlabcom-database-testing)을 test 단계에서 수동으로 트리거하세요.
    • 이 작업은 Database Lab의 복제본에서 마이그레이션을 실행하고 해당 MR에 결과(쿼리, 실행 시간, 크기 변경)을 게시합니다.
    • 마이그레이션 실행 시간과 경고 사항을 검토하세요.

데이터 마이그레이션 추가 시 준비

데이터 마이그레이션은 본질적으로 리스크가 있습니다. 오류 가능성을 줄이기 위해 추가 조치가 필요합니다.

MR 설명에 포함:

  • 마이그레이션이 복원되지 않는 경우, 사건 발생 시 데이터 변경을 되돌릴 수 있는 세부 정보. 예를 들어, 레코드를 삭제하는 마이그레이션의 경우 (대부분 자동으로 복원되지 않는 작업), 삭제된 레코드를 어떻게 복구할 수 있을까요.
  • 마이그레이션이 데이터를 삭제하는 경우, ~data-deletion 라벨을 적용하세요.
  • 오류의 가능성에 대한 간결한 설명; 예를 들어 “이슈가 의도치 않게 Epic에서 사라질 수 있음”.
  • 쿼리가 예상대로 작동하는 것을 나타내는 쿼리 계획에서의 관련 데이터; 수정되거나 삭제된 레코드의 대략적인 수 등.

쿼리 추가 또는 수정시 준비

Raw SQL
  • MR 설명에 원시 SQL을 작성하세요. 가능하다면 pgFormatter 또는 https://paste.depesz.com를 사용하여 보기 좋게 서식을 맞추고 일반적인 큰따옴표(예: "projects"."id")를 사용하고 스마트 따옴표(예: “projects”.“id”)를 피하세요.
  • 파라미터를 이용하여 동적으로 생성된 쿼리의 경우, 각 변형별로 하나의 원시 SQL 쿼리가 있어야 합니다.

    예를 들어, 옵션 필터를 사용할 수 있는 이슈의 파인더의 경우, 이슈 버전과 프로젝트를 연결하여 필터를 적용하는 쿼리의 버전을 모두 포함해야 합니다.

    모든 파라미터를 포함하는 쿼리 또는 다양한 유형의 쿼리마다 하나의 쿼리만 추가해야 합니다.

    예를 들어, 조인 또는 그룹화 절이 선택적인 쿼리도, 그룹화 절이 없는 버전과 더 적은 조인이 있는 버전도 포함되어야 하지만, 나머지 테이블에 적절한 필터를 유지해야 합니다.

  • 쿼리가 항상 한정과 오프셋과 함께 사용된다면, 최대 허용 한도와 0이 아닌 오프셋과 함께 항상 포함되어야 합니다.
쿼리 계획
  • 병합 요청에 포함된 각 raw SQL 쿼리의 쿼리 계획과 해당 raw SQL 조각 뒤에 따르는 쿼리 계획 링크를 제공합니다.
  • postgres.ai 챗봇에서 explain 명령을 사용하여 생성된 계획에 대한 링크를 제공합니다. explain 명령은 EXPLAIN ANALYZE를 실행합니다.
    • Database Lab에서 정확한 그림을 얻기 어렵다면, 개발 환경을 시드하고 EXPLAIN ANALYZE의 출력 대신 explain.depesz.com 또는 explain.dalibo.com을 사용하여 계획과 사용된 쿼리 둘 다에 대한 링크를 만듭니다.
  • 쿼리 계획을 제공할 때, 충분한 데이터를 포함하는지 확인합니다:
    • 충분한 데이터로 쿼리 계획을 생성하려면 다음 ID를 사용할 수 있습니다:
      • 그룹과 관련된 쿼리의 경우 gitlab-org 네임스페이스 (namespace_id = 9970)의 ID.
      • 프로젝트와 관련된 쿼리의 경우 gitlab-org/gitlab-foss (project_id = 13083) 또는 gitlab-org/gitlab (project_id = 278964) 프로젝트의 ID.
        • 프로젝트 멤버십과 관련된 쿼리의 경우 이러한 프로젝트의 project_namespace_id가 필요할 수 있습니다. 이 값은 15846663 (gitlab-org/gitlab용) 및 15846626 (gitlab-org/gitlab-foss용)입니다.
      • 사용자와 관련된 쿼리의 경우 gitlab-qa 사용자의 ID (user_id = 1614863).
        • 선택적으로 자체 user_id 또는 프로젝트나 그룹 내에서 오랜 이력을 가진 사용자의 user_id를 사용할 수 있습니다.
    • 이는 쿼리 계획에 0 레코드나 제공된 제한보다 적은 레코드를 반환해서는 안 된다는 것을 의미합니다. 쿼리가 일괄 처리에 사용된 경우 적절한 예제 일괄 처리를 식별하고 적절한 결과를 포함해야 합니다.
    • 당신의 쿼리가 GitLab.com의 새로운 기능에 속하고 따라서 프로덕션에서 데이터를 반환하지 않는 경우:
      • 쿼리를 분석하고 로컬 환경에서 계획을 제공합니다.
      • postgres.ai를 사용하여 데이터를 업데이트(exec UPDATE issues SET ...)하고 새 테이블 및 열을 생성(exec ALTER TABLE issues ADD COLUMN ...)할 수 있습니다.
    • EXPLAIN 계획 이해에서 실제 반환된 레코드 수를 찾는 방법에 대한 자세한 정보를 참조하세요.
  • 쿼리 변경의 경우, 변경하기 의 SQL 쿼리와 계획을 모두 제공하는 것이 좋습니다. 이렇게 하면 차이점을 빨리 찾을 수 있습니다.
  • 성능 향상을 나타내는 데이터를 포함하되 가능하면 벤치마크 형식으로 제공하세요.
  • 쿼리 계획을 평가할 때, 최종 쿼리를 데이터베이스에서 실행해야 합니다. finders 및 scopes로부터 반환된 ActiveRecord::Relation에서 중간 쿼리를 분석할 필요가 없습니다. PostgreSQL 쿼리 계획은 제한 및 최종 실행 전에 추가되는 다른 매개변수에 종속적입니다. 실제 실행된 쿼리를 확인하려면 log/development.log을 확인하는 것이 실제 쿼리임을 확신할 수 있는 한 가지 방법입니다.

기존 테이블에 외래 키를 추가할 때의 준비

  • 외래 키를 추가하기 에 소스 테이블에서 고아 행을 제거하는 마이그레이션을 포함하세요.
  • 더 이상 필요하지 않은 dependent: ... 인스턴스를 제거하세요.

테이블을 추가할 때의 준비

  • 테이블 열 순서 지정 지침을 기반으로 열을 정렬하세요.
  • 다른 테이블의 데이터를 가리키는 열에 대해 외래 키를 추가하고 인덱스도 추가하세요.
  • WHERE, ORDER BY, GROUP BY, JOIN 등의 문에서 사용되는 필드에 대해 인덱스를 추가하세요.
  • 새 테이블과 열이 꼭 위험한 것은 아니지만 시간이 지남에 따라 일부 접근 패턴은 본질적으로 확장하기 어려운 경우가 있습니다. 이러한 위험한 패턴을 미리 식별하기 위해 액세스 및 크기에 대한 기대를 문서화해야 합니다. MR 설명에 다음 질문에 대한 답을 포함하세요:
    • 다음 3개월, 6개월, 1년 동안 신규 테이블에 대한 예상 성장은 얼마나 되며, 이러한 가정은 무엇을 기반으로 하고 있나요?
    • 이 신규 테이블에 대해 3개월, 6개월, 1년마다 1시간 동안 예상되는 읽기 및 쓰기 횟수는 어떻게 되며, 행이 어떤 상황에서 업데이트되나요? 이러한 가정은 무엇을 기반으로 하고 있나요?
    • 예상 데이터 볼륨 및 액세스 패턴을 기반으로 신규 테이블이 GitLab.com 또는 Self-Managed 인스턴스에 가용성 위험을 야기하는지 여부를 평가했나요? 제안된 설계는 GitLab.com 및 Self-Managed 고객의 요구를 지원하는 데 확장 가능한가요?

열, 테이블, 인덱스 또는 다른 구조를 제거할 때의 준비

  • 열 삭제 지침에 따르세요.
  • 일반적으로 (강제 규칙은 아니지만) 제거된 인덱스와 외래 키를 배포 후 마이그레이션합니다.
    • 예외로는 작은 테이블에 대한 인덱스와 외래 키를 제거하는 경우입니다.
  • 복합 인덱스를 추가하는 경우, 또 다른 인덱스가 중복될 수 있으므로 동일한 마이그레이션에서 해당 인덱스를 제거하세요. 예를 들어 index(column_A, column_B, column_C)를 추가하는 경우 index(column_A, column_B)index(column_A)가 중복됩니다.

대량 업데이트 작업을 사용할 때의 준비

update, upsert, delete, update_all, upsert_all, delete_all 또는 destroy_all ActiveRecord 메소드를 사용하는 경우, 데이터를 수정하고 성능이 저하될 수 있으며, 부적절한 범위로 인해 데이터가 파괴될 수 있으므로 추가 주의가 필요합니다. 이러한 메소드들은 또한 공통 테이블 표현(CTE) 문과 호환되지 않습니다. Danger는 이러한 메소드들이 사용될 때 Merge Request Diff에 댓글을 작성할 것입니다.

쿼리를 추가하거나 수정할 때의 준비에 대한 문서를 따라가서 원시 SQL 쿼리와 쿼리 플랜을 Merge Request 설명에 추가하고 데이터베이스 리뷰를 요청하세요.

데이터베이스 리뷰 방법

  • 마이그레이션 확인
    • 관계형 모델링 및 설계 선택 사항 검토
    • 마이그레이션은 데이터베이스 마이그레이션 스타일 가이드를 따라야 합니다.
      • 예를 들어,
      • 열 순서 확인
      • 외래 키에 대한 인덱스 확인 (migration_style_guide.md#adding-foreign-key-constraints)
    • 마이그레이션이 트랜잭션 내에서 실행되거나 동시 인덱스/외래 키 헬퍼만 포함되어 있는지 확인하세요 (트랜잭션이 비활성화된 상태에서)
    • 대형 테이블에 인덱스가 추가되고 실행 시간이 데이터베이스 랩에서 1시간을 초과했다면:
      • 이를 포스트 마이그레이션에 추가했는지 확인하세요.
      • 메인테이너: Merge Request가 병합된 후, “f_upcoming_release” Slack 채널에서 릴리스 매니저에게 알립니다.
    • db/structure.sql과 일치하는지, 마이그레이션이 가역성을 지원하는지 확인하세요
    • db/schema_migrations 하위에 해당 버전 파일이 추가되거나 제거되었는지 확인하세요.
    • 쿼리 수행 시간 확인 (있는 경우): 단일 트랜잭션에서 마이그레이션에서 실행된 누적 쿼리 시간은 GitLab.com에서 편안하게 15초 내에 들어맞아야 하며 가능하면 그보다 훨씬 짧아야 합니다.
    • 열 제거의 경우, 해당 열이 이전 릴리스에서 무시되었는지 확인하세요.
  • 일괄 배경 마이그레이션을 확인하세요:
    • GitLab.com에서 실행에 대한 시간 추정치 확립하세요. 역사적인 목적으로, 병합 요청 설명에 이 추정치를 포함하는 것이 매우 권장됩니다.
    • test 스테이지에서 수동으로 데이터베이스 테스트 작업 (db:gitlabcom-database-testing)을 트리거하세요.
    • 단일 update1초 미만일 경우, 해당 쿼리는 보통 마이그레이션 (db/migrate 내부)에 직접 배치할 수 있습니다.
    • 일반적으로 백그라운드 마이그레이션은 다음과 같은 경우에 사용되지만 이에 국한되지는 않습니다:
      • 대형 테이블의 데이터 마이그레이션
      • 데이터셋의 레코드 당 다수의 SQL 쿼리 생성
    • 쿼리 검토 (예를 들어, 일괄 크기가 적당한지 확인)
    • 일반 마이그레이션보다 실행 시간이 길 수 있기 때문에, 백그라운드 마이그레이션을 포스트 마이그레이션으로 취급하는 것이 좋습니다. 즉, db/migrate 대신 db/post_migrate에 배치하세요.
  • 마이그레이션의 실행 시간 지침을 확인하세요
  • 마이그레이션이 가역성이 있고 #down 메소드를 구현했는지 확인하세요.
  • 새로운 테이블 마이그레이션 확인:
    • 명시된 접근 패턴과 볼륨이 합리적인가요? 기반이 되는 가정이 타당해 보이나요? 이러한 패턴이 안정성에 위험을 초래할까요?
    • 열이 공간을 절약할 수 있도록 순서가 지정되었나요?
    • 다른 테이블을 참조하는 외래 키가 있나요?
  • 데이터 마이그레이션 확인:
    • GitLab.com에서 실행에 대한 시간 추정치 확립하세요.
    • 타이밍에 따라 데이터 마이그레이션은 보통, 포스트 배포 또는 백그라운드 마이그레이션에 배치할 수 있습니다.
    • 데이터 마이그레이션은 가능한 경우 가역성이 있어야 하거나 해당되는 경우 되돌릴 수 있는 방법에 대한 설명이 있어야 합니다. (일반, 포스트 배포, 백그라운드 마이그레이션 모두에 해당됨)
  • 쿼리 성능
    • 지나치게 복잡한 쿼리나 작성자가 특별히 리뷰할 쿼리를 확인하세요 (있는 경우)
    • 존재하지 않는 경우, 작성자에게 SQL 쿼리 및 쿼리 플랜을 제공하도록 요청하세요
    • 주어진 쿼리에 대해서 데이터 분포에 관한 매개변수 검토
    • 쿼리 플랜 확인 및 쿼리의 개선 사항 제안 (쿼리, 스키마 변경, 인덱스 추가 등)
    • 일반적인 지침은 쿼리의 실행 시간이 100ms 미만이어야 합니다
    • N+1 문제를 피하고 쿼리 횟수를 최소화하세요.