데이터베이스 검토 지침

이 페이지는 데이터베이스 검토에 관한 내용입니다. 일반적인 코드 검토에 대한 보다 넓은 조언 및 최상의 실천 방법에 대해서는 코드 검토 가이드를 참조하십시오.

일반적인 프로세스

데이터베이스 검토는 다음에 필요합니다:

  • 데이터베이스 스키마나 데이터 이주를 건드리는 변경 사항, db/lib/gitlab/background_migration/ 디렉터리에 있는 파일 포함
  • 데이터베이스 도구에 대한 변경 사항. 예를 들어:
    • lib/gitlab/database/의 마이그레이션 또는 ActiveRecord 도우미
    • 부하 분산
  • 분명히 넘어간 SQL 쿼리를 생성하는 변경 사항이 포함될 경우를 대비. 복잡한 쿼리가 도입되었는지 여부와 해당 쿼리가 데이터베이스 검토가 필요한지에 대한 결정은 주로 Merge Request 작성자에 달려 있습니다.
  • count, distinct_count, estimate_batch_distinct_count, sum을 사용하는 서비스 데이터 메트릭스의 변경 사항 포함 이러한 메트릭은 큰 테이블을 대상으로 복잡한 쿼리를 수행할 수 있습니다. 구현 세부 정보는 Analytics Instrumentation Guide를 참조하십시오.
  • ActiveRecord 객체에 대해 update, upsert, delete, update_all, upsert_all, delete_all 또는 destroy_all 메소드를 사용하는 변경 사항 포함

데이터베이스 리뷰어는 변경 사항에서 지나치게 복잡한 쿼리를 찾아가까이 검토하는 것을 기대합니다. 지정된 쿼리가 없거나 지나치게 복잡한 쿼리가 없다면, 마이그레이션에만 집중하는 것으로 충분합니다.

필수

다음 요소를 제공해야 합니다. 제안 ~데이터베이스 리뷰를 요청할 때, 당신의 Merge Request 설명에이 항목이 포함되지 않았다면 리뷰는 작성자에게 재할당됩니다.

마이그레이션

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

우리는 GitLab에서 제공하는 자동화 도구(db:check-migrations 파이프라인 작업)를 사용하여 이 결과를 CI 작업 로그에서 제공합니다. Merge Request 설명에서 이 결과를 제공하지 않아도 됩니다만, 그렇게 하는 것이 리뷰어에게 도움이 될 수 있습니다. 봇은 또한 마이그레이션이 올바르게 되돌릴 수 있는지 확인합니다.

쿼리

새로운 쿼리가 도입되었거나 기존 쿼리가 업데이트된 경우, 다음을 제공해야 합니다:

  • Merge Request에 포함된 각 raw SQL 쿼리의 쿼리 계획과 해당 raw SQL 스니펫 뒤에 쿼리 계획 링크
  • 변경되거나 추가된 모든 쿼리에 대한 Raw SQL (ActiveRecord 쿼리에서 변환된 것으로)
    • 기존 쿼리를 업데이트하는 경우 기존 버전과 새 버전의 쿼리의 Raw SQL 및 쿼리 계획을 함께 제공해야 합니다.

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

역할과 프로세스

Merge Request 작성자의 역할:

데이터베이스 리뷰어의 역할:

  • 필수 작업이 제대로 제공되었는지 확인합니다. 제공되지 않았다면, Merge Request을 다시 작성자에게 재할당합니다.
  • MR에 대해 첫 번째 검토를 수행하고 개선 사항을 작성자에게 제안합니다.
  • 만족한 경우, MR에 ~"database::reviewed"를 다시 라벨링하고 승인을 요청하며, 리뷰어가 제안하는 데이터베이스 유지관리자로부터 리뷰를 요청합니다. 이후에 자신을 리뷰어에서 제거합니다.

데이터베이스 유지관리자의 역할:

  • MR에 대해 최종 데이터베이스 검토를 수행합니다.
  • 데이터베이스 리뷰어 및 MR 작성자와 추가 개선 사항 또는 관련 변경 사항에 대해 논의합니다.
  • 마지막으로 MR을 승인하고, MR에 ~"database::approved"를 다시 라벨링합니다.
  • 승인 대기중인 다른 승인이 없다면 MR을 Merge하거나 필요에 따라 다른 유지관리자에게 전달합니다 (프론트엔드, 백엔드, 문서 등).
    • Merge하지 않을 경우, 자신을 리뷰어에서 제거합니다.

리뷰 작업 분산

리뷰어 룰렛을 사용하여 리뷰 작업이 분산됩니다 (예시). Merge Request 작성자는 제안된 데이터베이스 리뷰어로부터 리뷰를 요청해야 합니다. 리뷰어가 승인하면, 제안된 데이터베이스 유지관리자에게 넘깁니다.

리뷰어 룰렛에서 데이터베이스 리뷰어 및 유지관리자가 제안되지 않았다면 ~database 라벨을 적용하고 danger-review CI 작업을 다시 실행하거나, @gl-database 팀에서 누군가를 선택하십시오.

데이터베이스 검토를 위한 Merge Request 작성 방법

검토를 쉽게하고 빠르게 하기 위해 다음을 고려하세요.

마이그레이션 추가 시 준비

  • 문서화된대로 db/structure.sql을 업데이트하고 추가하거나 제거된 db/schema_migrations의 관련 버전 파일을 추가적으로 확인합니다.
  • 데이터베이스 사전이 문서화된대로 업데이트되었는지 확인합니다.
  • change 메소드를 사용하거나 up를 사용할 때 down 메소드를 포함하여 마이그레이션이 되돌릴 수 있도록 만듭니다.
    • 롤백 절차를 포함하거나 변경 사항을 롤백하는 방법을 설명합니다.
  • db:check-migrations 파이프라인 작업이 성공적으로 실행되었는지 확인하고 마이그레이션이 예상대로 작동하는지 확인합니다.
    • db:check-schema 작업이 성공적으로 실행되었는지 확인하고 롤백에 예상치 않은 스키마 변경 사항이 줄 지 않았는지 확인합니다. 이 작업은 스키마가 변경된 경우에만 경고를 트리거할 수 있습니다.
    • 리뷰 프로세스 중에 마이그레이션을 수정할 때마다 이전 작업이 계속해서 성공하는지 확인합니다.
  • 필요에 따라 spec/migrations에 마이그레이션에 대한 테스트를 추가합니다. 자세한 내용은 Testing Rails migrations at GitLab를 참조하십시오.
  • 모든 트랜잭션 마이그레이션에 대해 재시도 잠금이 기본적으로 활성화됩니다. 트랜잭션 마이그레이션이 아닌 경우에는 유효한 이유가없는 한 RuboCop 체크가 비활성화되지 않았는지 확인합니다.
  • 큰 테이블에 인덱스를 추가하는 경우, CREATE INDEX CONCURRENTLY를 사용하여 Database Lab에서 실행하고 실행 시간을 MR 설명에 추가합니다:
    • Database Lab의 실행 시간은 크게 다를 수 있지만, Database Lab의 실행 시간이 높다면 GitLab.com에서 실행 시간도 상당히 높을 수 있음을 나타낼 수 있습니다.
    • Database Lab의 실행 시간이 10분을 넘는 경우, 인덱스배포 이후 마이그레이션으로 이동해야 합니다. 이 경우 인덱스가 필요한 코드가 배포될 때 인덱스가 준비되어 있는지 확인하기 위해 마이그레이션과 응용 프로그램 변경을 별도의 배포로 나눌 수 있어야 합니다.
  • test 단계에서 데이터베이스 랩의 클론에서 마이그레이션을 실행하는 데이터베이스 테스팅 작업(db:gitlabcom-database-testing)을 수동으로 트리거합니다.
    • 이 작업은 Database Lab에서 마이그레이션을 실행하고 MR에 그 결과 (쿼리, 실행 시간, 크기 변경)을 게시합니다.
    • 마이그레이션 실행 시간 및 경고를 검토합니다.

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

데이터 마이그레이션은 본질적으로 리스크가 따릅니다. 제조 데이터의 손상이나 손실 가능성을 줄이기 위해 추가 조치가 필요합니다.

MR 설명에 포함:

  • 마이그레이션이 자체적으로 되돌릴 수 없는 경우, 사고가 발생했을 때 데이터 변경 사항을 어떻게 되돌릴 수 있는지에 대한 세부 정보. 예를 들어, 레코드를 삭제하는 마이그레이션이면(대부분의 경우 자동으로 되돌릴 수 없는 동작), 삭제된 레코드를 어떻게 복구할 수 있는지에 대한 내용을 설명합니다.
  • 데이터를 삭제하는 마이그레이션인 경우 ~data-deletion 레이블을 적용합니다.
  • 오류로 인한 사용자 경험 영향에 대한 간결한 설명; 예를 들어, “문제가 기대하지 않게 Epic에서 사라질 수 있음”.
  • 쿼리 계획서(query plans)에서 관련된 데이터를 제공하여 쿼리가 예상대로 작동하는지 나타내는 내용; 수정되거나 삭제되는 레코드의 근사치와 같은 내용이 포함됩니다.

쿼리 추가 또는 수정 시 준비

로우 SQL
  • MR 설명에 로우 SQL을 작성합니다. 가능하면 pgFormatterhttps://paste.depesz.com을 사용하여 보기 좋게 서식을 맞추고, 정규 따옴표를 사용합니다(예: "projects"."id") 및 스마트 따옴표(예: “projects”.“id”)를 피합니다.
  • 동적 매개변수를 사용하여 쿼리가 생성되는 경우, 각 변형별로 하나의 로우 SQL 쿼리가 있어야 합니다.

    예를 들어, 프로젝트에 대한 선택적 필터링을 매개변수로 사용하는 이슈를 찾는 프로그램은 이슈에 대한 쿼리 버전과 프로젝트를 조인하고 필터를 적용하는 쿼리 버전이 모두 포함되어야 합니다.

    대량의 변형이 생성될 수 있는 finders나 다른 메소드도 있습니다. 가능한 모든 생성된 쿼리를 추가할 필요는 없으며, 나머지 테이블에 적절한 필터를 유지하면서 그룹화나 조인이 적은 버전만 포함하면 됩니다.

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

기존 테이블에 외래 키 추가 준비

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

테이블 추가 시 준비

  • 테이블 열 순서에 따른 정렬 가이드라인을 기반으로 열을 정렬합니다.
  • 다른 테이블에서 데이터를 가리키는 열에 외래 키와 인덱스를 추가합니다.
  • WHERE, ORDER BY, GROUP BY, JOIN 등에 사용되는 필드에 대해 인덱스를 추가합니다.
  • 새로운 테이블은 db/fixtures/development/에 있는 파일로 씨드되어야 합니다. 이러한 fixtures는 업그레이드가 성공적으로 완료되었음을 보장하기 위해 항상 새로운 테이블을 채워야 하므로, 새로운 테이블을 항상 채워야 합니다.
  • 새로운 테이블과 열이 본질적으로 리스크를 가지진 않지만, 시간이 지남에 따라 일부 접근 패턴은 확장하기 어려울 수 있습니다. 미리 이러한 리스크 패턴을 식별하려면 액세스 및 크기에 대한 기대치를 문서화해야 합니다. 이 MR 설명에 다음 질문에 대한 답변을 포함시켜야 합니다:
    • 다음 3개월, 6개월, 1년 동안 새로운 테이블의 예상 성장은 어떻게 되나요? 이 기대치는 어떤 가정에 기반합니까?
    • 3개월, 6개월, 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 차이에 대해 코멘트를 추가합니다.

쿼리를 추가하거나 수정할 때의 준비를 참고하여 Merge Request 설명에 원시 SQL 쿼리 및 쿼리 계획을 추가하고 데이터베이스 검토를 요청하세요.

데이터베이스 검토 방법

  • 마이그레이션 확인
    • 관계 모델링 및 디자인 선택 사항 검토
    • 데이터베이스 마이그레이션을 데이터베이스 마이그레이션 스타일 가이드에 따라 확인하세요. 예시:
      • 열의 순서 확인
      • 외래 키에 대한 인덱스 확인(migration_style_guide.md#adding-foreign-key-constraints)
    • 마이그레이션이 트랜잭션에서 실행되거나 동시 인덱스/외래 키 도우미만 포함되도록 보장합니다(트랜잭션이 비활성화된 상태).
    • 큰 테이블에 인덱스가 추가되었고 실행 시간이 Database Lab에서 1시간 이상 지연된 경우:
      • 그것이 마이그레이션 이후에 추가되었는지 확인합니다.
      • 유지자: Merge Request이 Merge되면 #f_upcoming_release Slack 채널에서 릴리스 매니저에게 알려주세요.
    • db/structure.sql의 일관성 및 마이그레이션의 가역성을 확인하세요.
    • db/schema_migrations의 관련 버전 파일이 추가되거나 제거되었는지 확인하세요.
    • 쿼리 실행 시간 확인(있는 경우): 단일 트랜잭션에서 마이그레이션에 실행된 누적 쿼리 시간이 GitLab.com에서 15초 내에 편안하게 들어맞아야 합니다. 가능하면 이보다 훨씬 적은 시간이 좋습니다.
    • 열이 제거되는 경우, 해당 열이 이전 릴리스에서 무시되었는지 확인하세요.
  • 일괄 배경 마이그레이션 확인:
    • GitLab.com에서 실행에 대한 시간 예상치를 설정합니다. 기록상으로, 이 예상치를 Merge Request 설명에 포함하는 것이 매우 권장됩니다. 이것은 예상 배치 수에 대한 시간 간격입니다.
    • 매뉴얼으로 데이터베이스 테스트 작업 (db:gitlabcom-database-testing)을 test 단계에서 트리거합니다.
    • 단일 update1초 미만인 경우, 해당 쿼리는 일반 마이그레이션(내부 db/migrate)에 직접 배치될 수 있습니다.
    • 백그라운드 마이그레이션은 일반적으로 사용되지만 다음과 같은 용도로 사용됩니다(하지만 이에 국한되지는 않음):
      • 더 큰 테이블에서 데이터 마이그레이션
      • 데이터 세트에서 레코드 당 많은 SQL 쿼리 수행
    • 쿼리를 검토하세요(예: 배치 크기가 적절한지 확인하세요)
    • 일반 마이그레이션에 비해 실행 시간이 더 길 수 있기 때문에 백그라운드 마이그레이션을 포스트 마이그레이션으로 취급하는 것이 좋습니다: db/migrate 대신 db/post_migrate에 배치하세요.
  • 마이그레이션의 시간 가이드라인 확인
  • 마이그레이션이 가역적이며 #down 메서드를 구현하는지 확인
  • 새로운 테이블 마이그레이션 확인:
    • 명시된 액세스 패턴 및 볼륨이 합리적인지 확인하세요. 그 기반으로 한 가정이 타당해 보이는지, 이러한 패턴이 안정성에 위험을 가지고 있는지 확인하세요.
    • 열이 공간을 절약하기 위해 순서가 지정되어 있는지 확인하세요.
    • 다른 테이블에 대한 참조에 대한 외래 키가 있는지 확인하세요.
    • 테이블에 db/fixtures/development/에 대한 fixture가 있는지 확인하세요.
  • 데이터 마이그레이션 확인:
    • GitLab.com에서 실행에 대한 시간 예상치를 설정합니다.
    • 시간에 따라 데이터 마이그레이션은 정규, 포스트 배포, 또는 백그라운드 마이그레이션에 배치될 수 있습니다.
    • 데이터 마이그레이션이 가능한 경우 되감거나 어떻게 되감을 수 있는지에 대한 설명과 함께 이루어져야 합니다. 이는 모든 유형의 마이그레이션(정규, 포스트 배포, 백그라운드)에 적용됩니다.
  • 쿼리 성능
    • 지나치게 복잡한 쿼리 및 작성자가 특별히 검토를 요청한 쿼리를 확인하세요(있는 경우)
    • 없는 경우 작성자에게 Database Lab을 사용하여 SQL 쿼리 및 쿼리 계획을 제공하도록 요청하세요.
    • 주어진 쿼리에 대해 데이터 분포와 관련된 매개변수 확인
    • 쿼리 계획을 확인하고 쿼리에 대한 개선 제안(쿼리, 스키마 변경, 인덱스 추가 등)을 제안하세요
    • 쿼리의 일반적인 가이드라인은 100ms 실행 시간 아래여야 합니다.
    • N+1 문제를 피하고 쿼리 수를 최소화하세요.