DB 마이그레이션 실패
alembic upgrade head 실패는 새 deployment 또는 업그레이드 단계에서 가장
흔한 막힘입니다. 본 페이지는 실패 메시지별로 진단 + 조치를 정리합니다.
사전 요구 사항
- TimescaleDB가 떠 있고
DATABASE_URL이 정확함 - 드라이버는 반드시
postgresql+psycopg://(psycopg v3) — 자세한 내용은 환경변수 카탈로그 참조 - 리비전 체인은 Alembic 마이그레이션 참조
Case 1: TimescaleDB 확장 부재
메시지
sqlalchemy.exc.ProgrammingError: (psycopg.errors.UndefinedFile)
extension "timescaledb" is not available
또는
extension "timescaledb" does not exist
원인
docker-compose.yml의 image가 postgres:16 (plain) 또는 다른 Postgres
이미지로 설정되어 TimescaleDB가 설치되지 않은 상태.
진단
docker compose exec timescaledb psql -U quant -d quantai -c "
SELECT extname, extversion FROM pg_extension WHERE extname = 'timescaledb';
"
# 결과가 0 row 면 부재
조치
# docker-compose.yml
timescaledb:
image: timescale/timescaledb:latest-pg16 # postgres:* 가 아니어야 함
# 볼륨 데이터를 건드리고 싶지 않다면 image만 바꾸고 재생성
docker compose up -d --force-recreate timescaledb
# 그 후 마이그 재시도
docker compose exec api alembic upgrade head
이미 데이터가 plain Postgres에 들어가 있다면, pg_dump → 새 TimescaleDB
컨테이너로 restore.
Case 2: pg_trgm / 다른 확장 권한 부족
메시지
permission denied to create extension "pg_trgm"
원인
quant 유저가 일반 권한이라 CREATE EXTENSION 권한 없음. 매니지드
Postgres (RDS / CloudSQL / Azure Database) 에서 흔함.
조치
DBA / 슈퍼유저로 1회 수동 생성 후 마이그레이션 재시도:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS timescaledb;
# 그 후
docker compose exec api alembic upgrade head
이미 만들어진 확장에 대해 마이그레이션의 CREATE EXTENSION IF NOT EXISTS
는 no-op이라 안전.
Case 3: 컬럼 / 테이블 이미 존재
메시지
column "asset_class" of relation "trades" already exists
또는
relation "ohlcv" already exists
원인
기존 BTC-only 인스턴스에서 Base.metadata.create_all로 만들어진 스키마
위에 마이그레이션을 적용하려고 함. 00_baseline이 stamp되지 않아서
alembic이 01_*부터 다시 시작.
조치
# baseline을 stamp (실제 SQL 안 돔, alembic_version 행만 추가)
docker compose exec api alembic stamp 00_baseline
# 그 위에서 head로
docker compose exec api alembic upgrade head
특정 revision까지 부분 적용된 상태라면, 어디까지 적용됐는지 확인 후 가장 가까운 revision으로 stamp:
# 이미 적용된 revision 추정 (예: 03_trading_asset_class까지)
docker compose exec api alembic stamp 03_trading_asset_class
docker compose exec api alembic upgrade head
Case 4: 멀티 head 충돌
메시지
Multiple head revisions are present, please specify a head
원인
08_broker_events_queue 이후로 의도적인 두 head 분기가 있습니다 —
10_live_gating_tables 와 10_exchange_keys_asset_class. alembic upgrade head 단수형이 아니라 heads 복수형이 필요한 일부 명령에서 발생.
조치
# upgrade는 heads (복수) 자동으로 둘 다 적용 — 별도 옵션 불요
docker compose exec api alembic upgrade head
# 만약 위가 실패하면
docker compose exec api alembic upgrade heads
# 현재 head 확인 (둘 다 있어야 정상)
docker compose exec api alembic heads
Case 5: psycopg v2/v3 드라이버 미스매치
메시지
ModuleNotFoundError: No module named 'psycopg2'
또는
sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:postgresql.psycopg2
원인
DATABASE_URL이 postgresql:// 또는 postgresql+psycopg2://로 시작.
quant-ai는 psycopg v3를 사용하므로 postgresql+psycopg:// 가 정답.
조치
# .env
DATABASE_URL=postgresql+psycopg://quant:<pass>@timescaledb:5432/quantai
docker compose restart api analysis-worker position-reconciler
Case 6: lock 충돌 (마이그 실행 중 다른 프로세스가 DDL)
메시지
canceling statement due to lock timeout
또는
database is being accessed by other users
원인
다른 alembic 프로세스 / 사용자가 동시에 DDL 실행 중. 또는 hypertable 변환이 백그라운드에서 진행 중.
조치
# 1) 진행 중인 다른 alembic 프로세스 확인
docker compose exec timescaledb psql -U quant -d quantai -c "
SELECT pid, usename, application_name, query_start, LEFT(query, 80)
FROM pg_stat_activity
WHERE state = 'active' AND pid != pg_backend_pid()
ORDER BY query_start;
"
# 2) 5분 이상 잠긴 세션이라면 종료
docker compose exec timescaledb psql -U quant -d quantai -c "
SELECT pg_terminate_backend(<PID>);
"
# 3) 마이그 재시도
docker compose exec api alembic upgrade head
Case 7: 부분 적용된 마이그레이션 → 수동 정리
시나리오
05_symbols_and_calendars 도중 컨테이너가 죽어 equity_symbols는
생성됐지만 인덱스는 없는 상태. alembic_version은 04_exchange_keys_kis.
진단
docker compose exec timescaledb psql -U quant -d quantai -c "
SELECT relname FROM pg_class WHERE relkind='r' AND relname='equity_symbols';
"
# 1 row이면 부분 생성됨
docker compose exec api alembic current
# 04_exchange_keys_kis (head) ← 마이그 메타는 04 단계
조치 (옵션 A: 수동 cleanup 후 재시도)
docker compose exec timescaledb psql -U quant -d quantai -c "
DROP TABLE IF EXISTS equity_symbols CASCADE;
"
docker compose exec api alembic upgrade head
조치 (옵션 B: stamp로 건너뛰기)
수동으로 인덱스 / FK를 다 만들었다면:
docker compose exec api alembic stamp 05_symbols_and_calendars
docker compose exec api alembic upgrade head
검증
# 1. head에 도달
docker compose exec api alembic current
# 10_live_gating_tables (head) + 10_exchange_keys_asset_class (head)
# 2. 모든 테이블 존재 확인
docker compose exec timescaledb psql -U quant -d quantai -c "\dt"
# 3. /health 200
curl -fsS http://localhost:8000/health
# 4. 헬스체크 cron (alembic at head 항목 OK)
bash infra/healthcheck.sh
절대 금지
alembic_version행을 직접 DELETE — alembic이 base로 돌아가고 다음 upgrade가 모든 마이그레이션을 다시 시도- 컬럼이 안 만들어졌다고 코드를 수정해서 컬럼을 안 쓰게 — 다음 deploy에서 마이그가 다시 깨짐
alembic downgrade base— 모든 데이터가 사라질 수 있음- production DB에서
--purge/--clean옵션 사용
문제가 풀리지 않으면 항상 pg_dump 먼저 한 후 디자인 리뷰.