본문으로 건너뛰기

DB 마이그레이션 실패

alembic upgrade head 실패는 새 deployment 또는 업그레이드 단계에서 가장 흔한 막힘입니다. 본 페이지는 실패 메시지별로 진단 + 조치를 정리합니다.

사전 요구 사항

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_tables10_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_URLpostgresql:// 또는 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_version04_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

절대 금지

운영 DB에서 절대 하지 말 것
  • alembic_version 행을 직접 DELETE — alembic이 base로 돌아가고 다음 upgrade가 모든 마이그레이션을 다시 시도
  • 컬럼이 안 만들어졌다고 코드를 수정해서 컬럼을 안 쓰게 — 다음 deploy에서 마이그가 다시 깨짐
  • alembic downgrade base — 모든 데이터가 사라질 수 있음
  • production DB에서 --purge / --clean 옵션 사용

문제가 풀리지 않으면 항상 pg_dump 먼저 한 후 디자인 리뷰.

관련 페이지