- ensure_schema() broker_credentials: daemon_id NOT NULL, PK=(daemon_id,broker)로 갱신 - _row_to_broker_credential: Step2 컬럼 순서(daemon_id 선두)로 갱신 - upsert_broker_credential: ON CONFLICT (daemon_id,broker)로 수정, fallback try/except 제거 - list_broker_credentials: fallback 제거, SELECT 컬럼 순서 갱신 - test_bat020_broker_credential_domain.py: AC-3/AC-4 행 순서를 Step2 포맷으로 갱신 - tests/test_bat021_step2_ensure_schema_and_upsert.py: 12개 신규 테스트 - docs/runbooks/bat-021-req047-step2-migration-runbook.md: 운영 적용 런북 - docs/batches/bat-021: batch 문서(index/planning/design/implementation/verification) 실 PostgreSQL 통합검증(AC-1~7 전건 PASS): Step1 upgrade → backfill → Step2 upgrade → downgrade 시나리오 포함 NULL 사전검증(upgrade 거부) 및 downgrade 안전장치(중복 시 RuntimeError) 확인 521 passed, 0 failed (기존 509 + 신규 12)
5.7 KiB
5.7 KiB
운영 적용 런북: REQ-047 Step2 — broker_credentials PK 전환 (daemon_id, broker)
- Batch: BAT-021
- Discovery: dcy-010
- Date: 2026-06-28
- 결재 게이트: 사장님 결재 필수 (비가역 PK 변경)
전제 조건
- REQ-046 ~ REQ-051 코드(BAT-020 PR #17,
d4acfd4)가 배포돼 있어야 한다. - BAT-021 코드(ensure_schema 갱신, ON CONFLICT 수정)는 Step2 적용 완료 이후 배포한다(아래 배포 순서 준수).
- 마이그레이션 전 DB 백업이 완료돼 있어야 한다.
배포 순서 개요
백업 → Step1 upgrade → backfill CLI → NULL 0건 확인 → Step2 upgrade(결재) → 코드 배포 → 동작 확인 → 비상 시 롤백
1단계: DB 백업
운영 DB에 마이그레이션을 적용하기 전 전체 스냅샷을 백업한다.
# 예: pg_dump 사용
pg_dump -h <DB_HOST> -U <DB_USER> -d <DB_NAME> -F c -f backup_pre_bat021_$(date +%Y%m%d%H%M%S).dump
2단계: Step1 alembic upgrade (daemon_id NULL 컬럼 추가)
export TIMESCALEDB_HOST=<DB_HOST>
export TIMESCALEDB_PORT=<DB_PORT>
export TIMESCALEDB_NAME=<DB_NAME>
export TIMESCALEDB_USER=<DB_USER>
export TIMESCALEDB_PASSWORD=<DB_PASS>
export PORTFOLIOHUB_DB_ENABLED=true
alembic upgrade 20260628_0026
예상 출력:
INFO [alembic.runtime.migration] Running upgrade <prev> -> 20260628_0026, broker_credentials: daemon_id TEXT NULL 컬럼 추가 (REQ-047 Step1)
검증
-- broker_credentials에 daemon_id TEXT NULL 컬럼이 존재해야 함
\d broker_credentials
-- 또는
SELECT column_name, is_nullable FROM information_schema.columns
WHERE table_name = 'broker_credentials' AND column_name = 'daemon_id';
-- 기대: daemon_id | YES (nullable)
-- 기존 PK (user_id, broker) 유지 확인
SELECT constraint_name, constraint_type FROM information_schema.table_constraints
WHERE table_name = 'broker_credentials' AND constraint_type = 'PRIMARY KEY';
-- 기대: broker_credentials_pkey | PRIMARY KEY
-- 기존 데이터 daemon_id=NULL 보존 확인
SELECT COUNT(*) FROM broker_credentials; -- 기존 행 수 유지
3단계: backfill CLI — daemon_id 매핑
모든 기존 broker_credentials 행에 daemon_id를 설정한다.
# 사용자별로 실행 (daemon_id는 해당 user의 daemon id)
pfhub credential backfill-daemon-id --user <username> --daemon-id <daemon_slug>
# --dry-run으로 먼저 확인
pfhub credential backfill-daemon-id --user <username> --daemon-id <daemon_slug> --dry-run
검증: NULL 0건 확인
-- 이 쿼리가 0을 반환해야 Step2를 진행할 수 있다
SELECT COUNT(*) FROM broker_credentials WHERE daemon_id IS NULL;
-- 기대: 0
경고: NULL이 1건이라도 있으면 Step2 upgrade가 RuntimeError로 거부된다. 반드시 0건 확인 후 진행.
4단계: Step2 alembic upgrade (PK 전환) — 사장님 결재 필요
이 단계는 비가역 PK 변경이다. 사장님 결재 후에만 실행한다.
alembic upgrade 20260628_0027
예상 출력:
INFO [alembic.runtime.migration] Running upgrade 20260628_0026 -> 20260628_0027, broker_credentials: PK (user_id,broker) → (daemon_id,broker) 전환 (REQ-047 Step2)
검증: Step2 완료 확인
-- PK가 (daemon_id, broker)로 변경됐는지 확인
SELECT constraint_name FROM information_schema.table_constraints
WHERE table_name = 'broker_credentials' AND constraint_type = 'PRIMARY KEY';
-- 기대: broker_credentials_pkey
SELECT pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conname = 'broker_credentials_pkey';
-- 기대: PRIMARY KEY (daemon_id, broker)
-- daemon_id NOT NULL 확인
SELECT column_name, is_nullable FROM information_schema.columns
WHERE table_name = 'broker_credentials' AND column_name = 'daemon_id';
-- 기대: daemon_id | NO (not nullable)
-- user_id 컬럼 유지 확인
SELECT column_name FROM information_schema.columns
WHERE table_name = 'broker_credentials' AND column_name = 'user_id';
-- 기대: user_id 존재
5단계: 코드 배포 (BAT-021 코드 포함)
Step2 alembic upgrade가 완료된 후에 코드를 배포한다.
포함 변경:
app/repositories/postgres.py:ensure_schema()broker_credentials: daemon_id NOT NULL, PK=(daemon_id, broker)_row_to_broker_credential(): Step2 컬럼 순서 (daemon_id 선두)upsert_broker_credential(): ON CONFLICT (daemon_id, broker), fallback 제거list_broker_credentials(): fallback 제거, Step2 컬럼 순서
6단계: 동작 확인
# pfhub CLI로 인증정보 조회/등록 테스트
pfhub credential list --user <username> --daemon-id <daemon_id>
# 서비스 헬스체크
curl http://<SERVICE_HOST>/health
롤백 절차
Step2 롤백 (alembic downgrade)
Step2 downgrade는 (user_id, broker) 중복이 없는 경우에만 가능하다.
# Step2 → Step1 상태로 복원
alembic downgrade 20260628_0026
경고: 같은 user의 여러 daemon이 동일 broker를 사용하는 경우 downgrade가 RuntimeError로 거부된다. 이 경우 수동 데이터 정리 후 downgrade하거나 백업에서 복원한다.
Step1 롤백
# Step1 → 이전 상태로 복원 (daemon_id 컬럼 제거)
alembic downgrade 20260605_0025
DB 복원 (최후 수단)
pg_restore -h <DB_HOST> -U <DB_USER> -d <DB_NAME> -c backup_pre_bat021_<TIMESTAMP>.dump
주의사항
- Step2 이전에 BAT-021 코드를 배포하면
ON CONFLICT (daemon_id, broker)절이 구 스키마와 맞지 않아 오류 발생. - 배포 순서: Step1 → backfill → Step2 → 코드 배포 순서를 엄수한다.
ensure_schema()는IF NOT EXISTS를 사용하므로 Step2 적용 후 실행 시 테이블이 이미 존재해 스킵됨 (신규 인스턴스 초기화 시에만 적용됨).