다운타임 없이 테이블 이름 바꾸기
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에게 지시합니다(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”으로 간주합니다.
표준 마이그레이션을 실행합니다(후 마이그레이션이 아님):
def up
rename_table_safely(:issues, :tickets)
end
def down
undo_rename_table_safely(:issues, :tickets)
end
중요 사항:
- 테이블 이름이 변경될 예정임을 다른 개발자에게 알립니다.
- 병합 요청에서
@gl-database
그룹에 알립니다. - 엔지니어링 주간 회고 문서에 참고사항을 추가합니다:
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
에서 테이블 정의를 반드시 제거해야 합니다.이를 위해
lib/gitlab/database.rb
에서TABLES_TO_BE_RENAMED
상수를 편집합니다:From:
TABLES_TO_BE_RENAMED = { 'issues' => 'tickets' }.freeze
To:
TABLES_TO_BE_RENAMED = {}.freeze
다운타임 없는 배포
다운타임 없이 응용 프로그램을 업그레이드하는 경우, 이전 코드를 실행 중인 응용 프로그램 인스턴스가 있을 수 있습니다. 이전 코드는 여전히 이전 데이터베이스 테이블을 참조합니다. 쿼리는 여전히 문제없이 작동합니다. 이전 호환되는 데이터베이스 뷰가 있기 때문입니다.
이전 버전의 응용 프로그램을 다시 시작하거나 데이터베이스에 다시 연결해야 하는 경우, ActiveRecord는 다시 열 정보를 가져옵니다. 이때 이전에 표시한 테이블(TABLES_TO_BE_RENAMED
)은 ActiveRecord에게 데이터베이스 테이블 정보를 가져올 때 새 데이터베이스 테이블 이름을 사용하도록 지시합니다.
새 버전의 응용 프로그램은 새 데이터베이스 테이블을 사용합니다.