다운타임 없이 테이블 이름 바꾸기
GitLab에 내장된 데이터베이스 헬퍼 메서드를 사용하면 다운타임 없이 데이터베이스 테이블 이름을 변경할 수 있습니다.
이 기술은 다음 단계로 데이터베이스 뷰를 기반으로 합니다:
- 데이터베이스 테이블 이름을 변경합니다.
- 이전 테이블 이름을 사용하여 새로운 테이블 이름을 가리키는 데이터베이스 뷰를 만듭니다.
- ActiveRecord의 스키마 캐시에 대한 우회 방법을 추가합니다.
예를 들어, issues
테이블 이름을 tickets
로 변경한다고 가정합니다. 다음을 실행하세요:
BEGIN;
ALTER TABLE issues RENAME TO tickets;
CREATE VIEW issues AS SELECT * FROM tickets;
COMMIT;
데이터베이스 뷰는 기본 테이블 스키마(기본값, null이 아닌 제약 조건, 인덱스 등)를 노출하지 않으므로 애플리케이션을 업데이트하여 새로운 테이블 이름을 사용하기 위한 추가 단계가 필요합니다. ActiveRecord는 이 데이터를 많이 사용하므로, 예를 들어 새로운 모델을 초기화하는 데 필요합니다.
이 제한을 우회하기 위해 ActiveRecord에 새로운 테이블 이름을 사용하여 다른 테이블에서 이 정보를 가져오도록 지시해야 합니다.
마이그레이션 전략 분석
릴리스 N.M: ActiveRecord 모델의 테이블 표시
현재 릴리스를 “릴리스 N.M”으로 간주합니다.
이 릴리스에서 데이터베이스 테이블을 등록하여 ActiveRecord가 새로운 테이블 이름(있는 경우)을 사용하여 데이터베이스 테이블 정보를 가져오도록 지시합니다. 그렇지 않으면 이전 테이블 이름으로 되돌아갑니다. 이는 제로 다운타임 배포 중 오류를 피하기 위해 필요합니다.
-
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”으로 간주합니다.
표준 마이그레이션을 실행합니다(후 마이그레이션이 아님):
def up
rename_table_safely(:issues, :tickets)
end
def down
undo_rename_table_safely(:issues, :tickets)
end
중요 사항:
- 다른 개발자들에게 테이블 이름이 변경될 것임을 알려주세요.
- 병합 요청에서
@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.table_name
변수를 설정하여 수행할 수 있습니다.
이 지점에서 우리는 쿼리에서 이전 데이터베이스 테이블 이름을 사용하고 있는 애플리케이션이 없습니다.
-
후 마이그레이션을 통해 데이터베이스 뷰를 제거합니다:
def up finalize_table_rename(:issues, :tickets) end def down undo_finalize_table_rename(:issues, :tickets) end
-
추가로
TABLES_TO_BE_RENAMED
에서 테이블 정의 must 제거해야 합니다.이를 위해
lib/gitlab/database.rb
에서TABLES_TO_BE_RENAMED
상수를 편집합니다:이전:
TABLES_TO_BE_RENAMED = { 'issues' => 'tickets' }.freeze
이후:
TABLES_TO_BE_RENAMED = {}.freeze
무중단 배포
애플리케이션이 다운타임 없이 업그레이드될 때, 구버전 코드가 실행되고 있는 애플리케이션 인스턴스가 있을 수 있습니다. 구버전 코드는 여전히 구 데이터베이스 테이블을 참조합니다. 쿼리는 여전히 문제 없이 작동합니다. 왜냐하면 하위 호환 가능한 데이터베이스 뷰가 설정되어 있기 때문입니다.
구버전 애플리케이션을 다시 시작하거나 데이터베이스에 다시 연결해야 하는 경우, ActiveRecord는 다시 열 정보(column information)를 가져옵니다. 이때, 우리 이전에 마크한 테이블(TABLES_TO_BE_RENAMED
)은 ActiveRecord에게 데이터베이스 테이블 정보를 가져올 때 새 데이터베이스 테이블 이름을 사용하도록 지시합니다.
새 버전의 애플리케이션은 새 데이터베이스 테이블을 사용합니다.