alembic revision의 의미

2024년 11월 20일 수요일

Today I Learned

날짜

2024년 11월 20일 수요일

내용

Alembic revision

FastAPI에선 Postgresql 의 마이그레이션을 위해 alembic을 사용한다. Django 처럼 프레임워크에서 제공하는 ORM이 없다보니 SQLAlchemy 를 사용하는데, 이 SQLAlchemy에서 제공하는 도구다.

alembic revision --autogenerate 라는 명령어로 데이터베이스 변경 내역을 스크립트로 만들고, alembic upgrade head 명령어로 입력한다.

네이버 상품 상세페이지에 추가되는 이미지에 이제는 인스타그램 게시글도 추가되도록 만들었다. 오늘이 실서버 배포날이라 실서버에서 마이그레이션을 적용하는데 이런 오류가 떴다.

1
2
3
4
File "/usr/local/lib/python3.12/site-packages/alembic/script/revision.py", line 245, in _revision_map
    down_revision = map_[downrev]
                    ~~~~^^^^^^^^^
KeyError: 'eea2e21515bc'

이게 무슨소리인고 찾아보니 이전 스크립트가 없다는 의미다. 이해를 위해 revision에 대해 알아보자.

alembic에서 revision은 데이터베이스의 변경사항을 기록하는 스크립트를 의미한다. 각각의 스크립트는 고유 아이디를 가진다. 따라서 명령어 alembic revision --autogenerate 는 “니가 알아서 현재 데이터베이스의 변경사항이 담긴 스크립트를 만들어”라는 명령어다.

스크립트 내용을 보면 대충 다음과 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
"""empty message

Revision ID: 698d8ef1ddae
Revises: e55b292f173a
Create Date: 2024-11-20 02:24:01.386718

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = '698d8ef1ddae'
down_revision: Union[str, None] = 'e55b292f173a'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.alter_column('instagram_account', 'access_token_expires_at',
               existing_type=postgresql.TIMESTAMP(),
               type_=sa.DateTime(timezone=True),
               existing_nullable=True)
    op.alter_column('instagram_comment', 'timestamp',
               existing_type=postgresql.TIMESTAMP(),
               type_=sa.DateTime(timezone=True),
               existing_nullable=True)
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.alter_column('shopify_product_variant', 'updated_at',
               existing_type=sa.DateTime(timezone=True),
               type_=postgresql.TIMESTAMP(),
               existing_nullable=True)
    op.alter_column('shopify_product_variant', 'created_at',
               existing_type=sa.DateTime(timezone=True),
               type_=postgresql.TIMESTAMP(),
               existing_nullable=True)
    # ### end Alembic commands ###

upgrade에는 이번에 생성된 revision에서 변경되는 내용들이 담기고 downgade 는 이전 revision 에서 변경된 내용 들이 나온다. 위에 보면 이번에 생성한 revision이 ‘698d8ef1dda’ 라는 고윳값을 가지고 있고, 이전에 적용된 것은 ‘e55b292f173a’ 다. 마이그레이션 작업이 시간순서대로 히스토리를 가지고 있는 셈이다.

따라서 사용자는 현재 생성한 스크립트대로 마이그레이션을 진행할 수도, 이전으로 돌아갈 수 도 있다. 이 스크립트 중 가장 최근 것은 데이터베이스에도 저장된다.

1

저렇게 테이블중에 alembic_version이 생성된다. 여기에는 가장 최근에 적용된 스크립트의 고윳값이 저장되어 있다.

2

이 스크립트는 내가 지정한 디렉토리에 생성된다. 저 데이터베이스에 있는 스크립트가 이 디렉토리에도 존재해야 한다. 그 외의 스크립트는 삭제해도 무방하다.

다시 나의 상황으로 돌아와 나는 이전 스크립트를 삭제해버렸다. 그것도 실서버에서… 데이터베이스에는 이전 reivision의 고윳값이 지정되어있으니 스크립트를 생성할 때 썼는데, 실제 스크립트 파일은 없으니 오류가 났던 것이다. 다행히 현재 데이터베이스에 문제가 있진 않아 롤백할 상황은 아니다. 어쩔 수없이 revision 히스토리를 새로 생성해야 했다.

alembic revision --autogenerate -m "Recreate migration chain" 명령어를 입력하면 새롭게 히스토리가 시작된다. down_revision은 None으로 뜬다. 스크립트 함부로 삭제하지 말자.

회고

어우 실서버 살떨려.