InfoGrab DocsInfoGrab Docs

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

요약

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다. 이 기법은 데이터베이스 뷰를 기반으로 하며 다음 단계를 따릅니다. 예를 들어 issues 테이블 이름을 tickets로 변경한다고 하면 다음과 같이 실행합니다.

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

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

  1. 데이터베이스 테이블 이름을 변경합니다.
  2. 이전 테이블 이름으로 데이터베이스 뷰를 만들어 새 테이블 이름을 가리키게 합니다.
  3. 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 용) 테이블 정보를 새 테이블 이름이 있으면 그 이름으로 가져오고 없으면 이전 테이블 이름으로 가져오도록 합니다. 이 단계는 무중단 배포 중 오류를 피하는 데 필요합니다.

  1. 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" 이라고 합니다.

  1. 포스트 마이그레이션이 아닌 일반 마이그레이션을 실행합니다.

    def up
      rename_table_safely(:issues, :tickets)
    end
    
    def down
      undo_rename_table_safely(:issues, :tickets)
    end
    
  2. (db/docs 아래의) 테이블 딕셔너리 파일의 이름을 새 이름으로 변경합니다(이 예시에서는 db/docs/tickets.yml). introduced_by_url과 milestone 속성도 업데이트합니다.

  3. 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) 확인합니다. 그런 인덱스는 별도 마이그레이션에서 수동으로 변경할 수 있으며, 이 또한 릴리스 N.M+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 변수를 설정하는 방식으로 처리합니다.

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

  1. 포스트 마이그레이션으로 데이터베이스 뷰를 제거합니다.

      def up
        finalize_table_rename(:issues, :tickets)
      end
    
      def down
        undo_finalize_table_rename(:issues, :tickets)
      end
    
  2. TABLES_TO_BE_RENAMED에서 테이블 이름을 제거해야 합니다.

    이를 위해 lib/gitlab/database.rb의 TABLES_TO_BE_RENAMED 상수를 수정합니다.

    변경 전:

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

    변경 후:

    TABLES_TO_BE_RENAMED = {}.freeze
    

무중단 배포#

다운타임 없이 애플리케이션을 업그레이드하면 이전 코드로 동작하는 인스턴스가 남아 있을 수 있습니다. 이전 코드는 여전히 이전 데이터베이스 테이블을 참조합니다. 하위 호환용 데이터베이스 뷰가 있으므로 쿼리는 문제없이 동작합니다.

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

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

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

GitLab v19.4
원문 보기

요약

GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다. 이 기법은 데이터베이스 뷰를 기반으로 하며 다음 단계를 따릅니다. 예를 들어 issues 테이블 이름을 tickets로 변경한다고 하면 다음과 같이 실행합니다.

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

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

  1. 데이터베이스 테이블 이름을 변경합니다.
  2. 이전 테이블 이름으로 데이터베이스 뷰를 만들어 새 테이블 이름을 가리키게 합니다.
  3. 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 용) 테이블 정보를 새 테이블 이름이 있으면 그 이름으로 가져오고 없으면 이전 테이블 이름으로 가져오도록 합니다. 이 단계는 무중단 배포 중 오류를 피하는 데 필요합니다.

  1. 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" 이라고 합니다.

  1. 포스트 마이그레이션이 아닌 일반 마이그레이션을 실행합니다.

    def up
      rename_table_safely(:issues, :tickets)
    end
    
    def down
      undo_rename_table_safely(:issues, :tickets)
    end
    
  2. (db/docs 아래의) 테이블 딕셔너리 파일의 이름을 새 이름으로 변경합니다(이 예시에서는 db/docs/tickets.yml). introduced_by_url과 milestone 속성도 업데이트합니다.

  3. 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) 확인합니다. 그런 인덱스는 별도 마이그레이션에서 수동으로 변경할 수 있으며, 이 또한 릴리스 N.M+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 변수를 설정하는 방식으로 처리합니다.

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

  1. 포스트 마이그레이션으로 데이터베이스 뷰를 제거합니다.

      def up
        finalize_table_rename(:issues, :tickets)
      end
    
      def down
        undo_finalize_table_rename(:issues, :tickets)
      end
    
  2. TABLES_TO_BE_RENAMED에서 테이블 이름을 제거해야 합니다.

    이를 위해 lib/gitlab/database.rb의 TABLES_TO_BE_RENAMED 상수를 수정합니다.

    변경 전:

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

    변경 후:

    TABLES_TO_BE_RENAMED = {}.freeze
    

무중단 배포#

다운타임 없이 애플리케이션을 업그레이드하면 이전 코드로 동작하는 인스턴스가 남아 있을 수 있습니다. 이전 코드는 여전히 이전 데이터베이스 테이블을 참조합니다. 하위 호환용 데이터베이스 뷰가 있으므로 쿼리는 문제없이 동작합니다.

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

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