InfoGrab DocsInfoGrab Docs

다운타임 없이 데이터베이스 테이블 이름 변경

요약

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다. 이 기법은 데이터베이스 뷰를 기반으로 하며, 다음 단계를 따릅니다: 데이터베이스 테이블 이름을 변경합니다.

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다.

이 기법은 데이터베이스 뷰를 기반으로 하며, 다음 단계를 따릅니다:

  • 데이터베이스 테이블 이름을 변경합니다.

  • 새 테이블 이름을 가리키는 이전 테이블 이름으로 데이터베이스 뷰를 생성합니다.

  • ActiveRecord의 스키마 캐시에 대한 해결 방법을 추가합니다.

예를 들어, issues 테이블 이름을 tickets로 변경하는 경우 다음을 실행합니다:

BEGIN;
  ALTER TABLE issues RENAME TO tickets;
  CREATE VIEW issues AS SELECT * FROM tickets;
COMMIT;

데이터베이스 뷰는 기본 테이블 스키마(기본값, not null 제약 조건, 인덱스)를 노출하지 않으므로, 애플리케이션이 새 테이블 이름을 사용하도록 추가 단계가 필요합니다. ActiveRecord는 예를 들어 새 모델을 초기화할 때 이 데이터에 크게 의존합니다.

이 제한을 해결하려면 ActiveRecord에게 새 테이블 이름을 사용하여 다른 테이블에서 이 정보를 가져오도록 지시해야 합니다.

마이그레이션 전략 분석#

릴리즈 N.M: ActiveRecord 모델의 테이블 표시#

현재 릴리즈를 "릴리즈 N.M"이라고 가정합니다.

이 릴리즈에서는 데이터베이스 테이블을 등록하여 ActiveRecord가 새 테이블 이름(존재하는 경우)을 사용하여 데이터베이스 테이블 정보(SchemaCache)를 가져오도록 지시합니다. 그렇지 않으면 이전 테이블 이름으로 폴백합니다. 이는 다운타임이 없는 배포 중 오류를 방지하기 위해 필요합니다.

  • lib/gitlab/database.rb에서 TABLES_TO_BE_RENAMED 상수를 편집합니다:
TABLES_TO_BE_RENAMED = {
  'issues' => 'tickets'
}.freeze

이 릴리즈(N.M)에서는 tickets 데이터베이스 테이블이 아직 존재하지 않습니다. 이 단계는 릴리즈 N.M+1에서의 실제 테이블 이름 변경을 준비하는 것입니다.

릴리즈 N.M+1: 데이터베이스 테이블 이름 변경#

다음 릴리즈를 "릴리즈 N.M+1"이라고 가정합니다.

  • 표준 마이그레이션(포스트 마이그레이션이 아닌)을 실행합니다:
def up
  rename_table_safely(:issues, :tickets)
end

def down
  undo_rename_table_safely(:issues, :tickets)
end
  • 테이블의 사전 파일(db/docs 아래)을 새 이름으로 변경합니다(이 예시에서는 db/docs/tickets.yml). introduced_by_urlmilestone 속성을 업데이트합니다.

  • 임시 뷰(이전 테이블 이름이 있는)에 대한 항목을 db/docs/deleted_views에 생성합니다. 이는 동일한 머지 리퀘스트의 포스트 배포 마이그레이션에서 finalize_table_rename에 의해 뷰가 삭제되기 때문입니다.

중요 사항:

  • 다른 개발자에게 테이블 이름이 변경될 것임을 알립니다.

머지 리퀘스트에서 @gl-database 그룹에 멘션합니다.

  • Engineering Week-in-Review 문서에 다음 내용을 추가합니다: table_name이 N.M에서 이름이 변경될 예정입니다. 릴리즈 N.M 및 N.M+1에서는 이 테이블에 대한 수정이 허용되지 않습니다.

  • 헬퍼 메서드는 테이블 이름 변경을 위해 Rails의 표준 rename_table 헬퍼를 사용합니다.

  • 헬퍼는 시퀀스와 인덱스의 이름을 변경합니다. 인덱스 이름 지정 시 표준 Rails 규칙에서 벗어나는 경우가 있으므로, 모든 인덱스가 올바르게 이름이 변경되지 않을 가능성이 있습니다. 로컬에서 마이그레이션을 실행한 후 일관성 없이 명명된 인덱스가 있는지 확인합니다(db/structure.sql). 이러한 인덱스는 별도의 마이그레이션에서 수동으로 이름을 변경할 수 있으며, 릴리즈 M.N+1의 일부가 될 수도 있습니다.

  • 외래 키 칼럼에 여전히 이전 테이블 이름이 포함될 수 있습니다. 작은 테이블의 경우 표준 칼럼 이름 변경 프로세스를 따르세요.

  • 트리거를 사용하는 데이터베이스 테이블의 이름을 변경하지 마세요.

  • 테이블 수정(칼럼 추가 또는 제거)은 이름 변경 프로세스 중에 허용되지 않습니다. 이름 변경 마이그레이션이 시작되기 전에(또는 다음 릴리즈에서) 테이블에 대한 모든 변경이 완료되었는지 확인합니다.

  • 인덱스 이름이 변경될 수 있으므로, 모델이 unique_by: index_name 옵션과 함께 벌크 삽입 (예: insert_all, upsert_all)을 사용하지 않는지 확인합니다. 이러한 메서드를 사용하는 동안 인덱스 이름을 변경하면 기능이 중단될 수 있습니다.

  • 복합 기본 키가 있는 테이블의 경우: 데이터베이스 뷰는 복합 기본 키 메타데이터를 보존하지 않습니다. 이름 변경 마이그레이션을 배포하기 전에 모델에서 self.primary_key를 명시적으로 설정합니다:

  class ModelName < ApplicationRecord
    self.primary_key = [:column1, :column2, :column3]
  end
  • 새 데이터베이스 테이블을 가리키도록 모델 코드를 수정합니다. 모델 이름을 직접 변경하거나 self.table_name 변수를 설정하여 수행합니다.

이 시점에서 애플리케이션은 쿼리에서 이전 데이터베이스 테이블 이름을 사용하지 않습니다.

  • 포스트 마이그레이션을 통해 데이터베이스 뷰를 제거합니다:
  def up
    finalize_table_rename(:issues, :tickets)
  end

  def down
    undo_finalize_table_rename(:issues, :tickets)
  end
  • 테이블 이름은 TABLES_TO_BE_RENAMED에서 반드시 제거되어야 합니다.

이를 위해 lib/gitlab/database.rbTABLES_TO_BE_RENAMED 상수를 편집합니다:

변경 전:

TABLES_TO_BE_RENAMED = {
  'issues' => 'tickets'
}.freeze

변경 후:

TABLES_TO_BE_RENAMED = {}.freeze

다운타임 없는 배포#

애플리케이션이 다운타임 없이 업그레이드되면, 이전 코드를 실행하는 애플리케이션 인스턴스가 존재할 수 있습니다. 이전 코드는 여전히 이전 데이터베이스 테이블을 참조합니다. 이전 버전과 호환되는 데이터베이스 뷰가 제자리에 있기 때문에 쿼리는 문제없이 작동합니다.

이전 버전의 애플리케이션을 재시작하거나 데이터베이스에 재연결해야 하는 경우, ActiveRecord가 칼럼 정보를 다시 가져옵니다. 이 시점에서 이전에 표시된 테이블(TABLES_TO_BE_RENAMED)이 ActiveRecord에게 데이터베이스 테이블 정보를 가져올 때 새 데이터베이스 테이블 이름을 사용하도록 지시합니다.

새 버전의 애플리케이션은 새 데이터베이스 테이블을 사용합니다.

다운타임 없이 데이터베이스 테이블 이름 변경

GitLab v19.1
원문 보기
요약

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다. 이 기법은 데이터베이스 뷰를 기반으로 하며, 다음 단계를 따릅니다: 데이터베이스 테이블 이름을 변경합니다.

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다.

이 기법은 데이터베이스 뷰를 기반으로 하며, 다음 단계를 따릅니다:

  • 데이터베이스 테이블 이름을 변경합니다.

  • 새 테이블 이름을 가리키는 이전 테이블 이름으로 데이터베이스 뷰를 생성합니다.

  • ActiveRecord의 스키마 캐시에 대한 해결 방법을 추가합니다.

예를 들어, issues 테이블 이름을 tickets로 변경하는 경우 다음을 실행합니다:

BEGIN;
  ALTER TABLE issues RENAME TO tickets;
  CREATE VIEW issues AS SELECT * FROM tickets;
COMMIT;

데이터베이스 뷰는 기본 테이블 스키마(기본값, not null 제약 조건, 인덱스)를 노출하지 않으므로, 애플리케이션이 새 테이블 이름을 사용하도록 추가 단계가 필요합니다. ActiveRecord는 예를 들어 새 모델을 초기화할 때 이 데이터에 크게 의존합니다.

이 제한을 해결하려면 ActiveRecord에게 새 테이블 이름을 사용하여 다른 테이블에서 이 정보를 가져오도록 지시해야 합니다.

마이그레이션 전략 분석#

릴리즈 N.M: ActiveRecord 모델의 테이블 표시#

현재 릴리즈를 "릴리즈 N.M"이라고 가정합니다.

이 릴리즈에서는 데이터베이스 테이블을 등록하여 ActiveRecord가 새 테이블 이름(존재하는 경우)을 사용하여 데이터베이스 테이블 정보(SchemaCache)를 가져오도록 지시합니다. 그렇지 않으면 이전 테이블 이름으로 폴백합니다. 이는 다운타임이 없는 배포 중 오류를 방지하기 위해 필요합니다.

  • lib/gitlab/database.rb에서 TABLES_TO_BE_RENAMED 상수를 편집합니다:
TABLES_TO_BE_RENAMED = {
  'issues' => 'tickets'
}.freeze

이 릴리즈(N.M)에서는 tickets 데이터베이스 테이블이 아직 존재하지 않습니다. 이 단계는 릴리즈 N.M+1에서의 실제 테이블 이름 변경을 준비하는 것입니다.

릴리즈 N.M+1: 데이터베이스 테이블 이름 변경#

다음 릴리즈를 "릴리즈 N.M+1"이라고 가정합니다.

  • 표준 마이그레이션(포스트 마이그레이션이 아닌)을 실행합니다:
def up
  rename_table_safely(:issues, :tickets)
end

def down
  undo_rename_table_safely(:issues, :tickets)
end
  • 테이블의 사전 파일(db/docs 아래)을 새 이름으로 변경합니다(이 예시에서는 db/docs/tickets.yml). introduced_by_urlmilestone 속성을 업데이트합니다.

  • 임시 뷰(이전 테이블 이름이 있는)에 대한 항목을 db/docs/deleted_views에 생성합니다. 이는 동일한 머지 리퀘스트의 포스트 배포 마이그레이션에서 finalize_table_rename에 의해 뷰가 삭제되기 때문입니다.

중요 사항:

  • 다른 개발자에게 테이블 이름이 변경될 것임을 알립니다.

머지 리퀘스트에서 @gl-database 그룹에 멘션합니다.

  • Engineering Week-in-Review 문서에 다음 내용을 추가합니다: table_name이 N.M에서 이름이 변경될 예정입니다. 릴리즈 N.M 및 N.M+1에서는 이 테이블에 대한 수정이 허용되지 않습니다.

  • 헬퍼 메서드는 테이블 이름 변경을 위해 Rails의 표준 rename_table 헬퍼를 사용합니다.

  • 헬퍼는 시퀀스와 인덱스의 이름을 변경합니다. 인덱스 이름 지정 시 표준 Rails 규칙에서 벗어나는 경우가 있으므로, 모든 인덱스가 올바르게 이름이 변경되지 않을 가능성이 있습니다. 로컬에서 마이그레이션을 실행한 후 일관성 없이 명명된 인덱스가 있는지 확인합니다(db/structure.sql). 이러한 인덱스는 별도의 마이그레이션에서 수동으로 이름을 변경할 수 있으며, 릴리즈 M.N+1의 일부가 될 수도 있습니다.

  • 외래 키 칼럼에 여전히 이전 테이블 이름이 포함될 수 있습니다. 작은 테이블의 경우 표준 칼럼 이름 변경 프로세스를 따르세요.

  • 트리거를 사용하는 데이터베이스 테이블의 이름을 변경하지 마세요.

  • 테이블 수정(칼럼 추가 또는 제거)은 이름 변경 프로세스 중에 허용되지 않습니다. 이름 변경 마이그레이션이 시작되기 전에(또는 다음 릴리즈에서) 테이블에 대한 모든 변경이 완료되었는지 확인합니다.

  • 인덱스 이름이 변경될 수 있으므로, 모델이 unique_by: index_name 옵션과 함께 벌크 삽입 (예: insert_all, upsert_all)을 사용하지 않는지 확인합니다. 이러한 메서드를 사용하는 동안 인덱스 이름을 변경하면 기능이 중단될 수 있습니다.

  • 복합 기본 키가 있는 테이블의 경우: 데이터베이스 뷰는 복합 기본 키 메타데이터를 보존하지 않습니다. 이름 변경 마이그레이션을 배포하기 전에 모델에서 self.primary_key를 명시적으로 설정합니다:

  class ModelName < ApplicationRecord
    self.primary_key = [:column1, :column2, :column3]
  end
  • 새 데이터베이스 테이블을 가리키도록 모델 코드를 수정합니다. 모델 이름을 직접 변경하거나 self.table_name 변수를 설정하여 수행합니다.

이 시점에서 애플리케이션은 쿼리에서 이전 데이터베이스 테이블 이름을 사용하지 않습니다.

  • 포스트 마이그레이션을 통해 데이터베이스 뷰를 제거합니다:
  def up
    finalize_table_rename(:issues, :tickets)
  end

  def down
    undo_finalize_table_rename(:issues, :tickets)
  end
  • 테이블 이름은 TABLES_TO_BE_RENAMED에서 반드시 제거되어야 합니다.

이를 위해 lib/gitlab/database.rbTABLES_TO_BE_RENAMED 상수를 편집합니다:

변경 전:

TABLES_TO_BE_RENAMED = {
  'issues' => 'tickets'
}.freeze

변경 후:

TABLES_TO_BE_RENAMED = {}.freeze

다운타임 없는 배포#

애플리케이션이 다운타임 없이 업그레이드되면, 이전 코드를 실행하는 애플리케이션 인스턴스가 존재할 수 있습니다. 이전 코드는 여전히 이전 데이터베이스 테이블을 참조합니다. 이전 버전과 호환되는 데이터베이스 뷰가 제자리에 있기 때문에 쿼리는 문제없이 작동합니다.

이전 버전의 애플리케이션을 재시작하거나 데이터베이스에 재연결해야 하는 경우, ActiveRecord가 칼럼 정보를 다시 가져옵니다. 이 시점에서 이전에 표시된 테이블(TABLES_TO_BE_RENAMED)이 ActiveRecord에게 데이터베이스 테이블 정보를 가져올 때 새 데이터베이스 테이블 이름을 사용하도록 지시합니다.

새 버전의 애플리케이션은 새 데이터베이스 테이블을 사용합니다.