Alembic 마이그레이션
quant-ai의 DB 스키마는 Alembic으로 관리합니다. 마이그레이션은 멱등(idempotent) 하게 작성되어 있어 같은 head로 두 번 upgrade해도 안전합니다.
사전 요구 사항
- TimescaleDB가 떠 있고
DATABASE_URL이 정확함 — 드라이버는 반드시postgresql+psycopg://(psycopg v3) 사용 api컨테이너 안에서 alembic 실행
# 컨테이너 안에서
docker compose exec api alembic current
# 호스트에서 한 줄로
docker compose exec -T api alembic current
리비전 체인
00_baseline (no-op, 기존 BTC 스키마 stamp 용)
└─ 01_asset_class_enum (asset_class enum 추가)
└─ 02_market_asset_class (ohlcv 등 market 테이블에 asset_class)
└─ 03_trading_asset_class (trades / backtest_results)
└─ 04_exchange_keys_kis (KIS 컬럼: app_key, app_secret, account_no)
└─ 05_symbols_and_calendars (equity_symbols + market_calendars)
└─ 06_fx_rates (FX 환율 하이퍼테이블)
└─ 07_analysis_reports (analysis_reports + analysis_traces)
└─ 08_broker_events_queue (broker_order_events + queue)
├─ 09_positions_table (positions hypertable)
│ └─ 10_live_gating_tables (live_confirm_tokens, user_settings)
└─ 09_backtest_asset_class (backtest_results.asset_class)
└─ 10_exchange_keys_asset_class (exchange_keys.asset_class)
08_broker_events_queue에서 두 개의 09_* 브랜치로 분기합니다. 둘 다
선형 후속(10_live_gating_tables, 10_exchange_keys_asset_class)을 가지며
alembic upgrade head는 두 head를 모두 적용합니다. 분기는 의도적이며
alembic heads로 항상 두 줄을 확인하세요.
적용 (upgrade)
새 인스턴스
# 1) 컨테이너 부팅
docker compose up -d timescaledb redis api
# 2) 모든 마이그레이션을 head로
docker compose exec -T api alembic upgrade head
# 3) 검증
docker compose exec -T api alembic current
# 10_live_gating_tables (head) 또는
# 10_exchange_keys_asset_class (head)
기존 BTC-only 인스턴스 (Base.metadata.create_all로 만들어진 DB)
00_baseline은 의도적으로 비어 있어 기존 테이블을 다시 만들지 않습니다.
한 번만 stamp해서 기준점을 잡습니다.
# 1) 베이스라인을 stamp (실제 SQL은 실행되지 않음, alembic_version 행만 추가)
docker compose exec -T api alembic stamp 00_baseline
# 2) 그 위에서 head까지 upgrade
docker compose exec -T api alembic upgrade head
부분 적용
특정 revision만 올리고 멈추기:
docker compose exec -T api alembic upgrade 05_symbols_and_calendars
상대 경로로 한 단계만:
docker compose exec -T api alembic upgrade +1
롤백 (downgrade)
08_broker_events_queue 이전으로 되돌리면 큐 잔여 분석 작업과 broker
audit 행이 함께 사라집니다. 특히 09_positions_table을 다운그레이드하면
하이퍼테이블의 압축 정책이 손실됩니다. 프로덕션 다운그레이드 전 반드시
pg_dump를 먼저 수행하세요.
# 한 단계 다운
docker compose exec -T api alembic downgrade -1
# 특정 revision으로 다운
docker compose exec -T api alembic downgrade 04_exchange_keys_kis
# 전부 되돌림 (위험)
docker compose exec -T api alembic downgrade base
운영 시나리오: 무중단 배포 + 마이그레이션
scripts/deploy_azure.sh는 다음 순서를 따릅니다.
1. ACR로 새 이미지 push
2. VM에서 docker compose pull
3. docker compose run --rm api alembic upgrade head ← 여기서 스키마 갱신
4. bash scripts/rolling_restart.sh ← 컨테이너 교체
5. /health 200 확인 (실패 시 --rollback)
이로써 새 이미지가 기동될 때는 이미 새 스키마가 적용되어 있어, 시작 실패가 거의 없습니다. 이는 모든 마이그레이션이 add-only이고 컬럼/테이블 삭제를 별도 revision으로 미루는 컨벤션 덕분입니다 — 구 이미지가 잠깐 새 스키마와 공존해도 호환됩니다.
검증
# 1. 현재 head
docker compose exec -T api alembic current
# 출력에 (head) 가 있어야 함
# 2. 모든 head 표시 (분기 확인)
docker compose exec -T api alembic heads
# 멀티 head가 정상 — 두 줄이 떠도 OK
# 3. 히스토리
docker compose exec -T api alembic history --verbose
트러블슈팅
| 증상 | 원인 / 조치 |
|---|---|
relation "ohlcv" already exists | 기존 BTC 인스턴스에서 stamp 누락 → alembic stamp 00_baseline 후 다시 upgrade |
extension "timescaledb" does not exist | TimescaleDB 이미지가 아닌 plain Postgres에 붙음. docker-compose.yml의 image를 timescale/timescaledb:latest-pg16으로 |
permission denied to create extension "pg_trgm" | 슈퍼유저로 실행해야 함. quant 유저는 default DB owner이지만 일부 클라우드 Postgres는 별도 권한 필요. RDS/CloudSQL 등 매니지드 DB라면 운영자가 1회 CREATE EXTENSION 수동 실행 |
column "asset_class" of relation "trades" already exists | 이미 부분 적용된 상태. alembic stamp <revision>으로 메타테이블만 동기화 후 head 시도 |
Multiple head revisions are present | 정상. alembic heads로 둘 다 확인하고 그대로 진행 |
자세한 케이스 분석은 DB 마이그레이션 실패를 참고하세요.