# 운영 적용 런북: 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에 마이그레이션을 적용하기 전 전체 스냅샷을 백업한다. ```bash # 예: pg_dump 사용 pg_dump -h -U -d -F c -f backup_pre_bat021_$(date +%Y%m%d%H%M%S).dump ``` --- ## 2단계: Step1 alembic upgrade (daemon_id NULL 컬럼 추가) ```bash export TIMESCALEDB_HOST= export TIMESCALEDB_PORT= export TIMESCALEDB_NAME= export TIMESCALEDB_USER= export TIMESCALEDB_PASSWORD= export PORTFOLIOHUB_DB_ENABLED=true alembic upgrade 20260628_0026 ``` 예상 출력: ``` INFO [alembic.runtime.migration] Running upgrade -> 20260628_0026, broker_credentials: daemon_id TEXT NULL 컬럼 추가 (REQ-047 Step1) ``` ### 검증 ```sql -- 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를 설정한다. ```bash # 사용자별로 실행 (daemon_id는 해당 user의 daemon id) pfhub credential backfill-daemon-id --user --daemon-id # --dry-run으로 먼저 확인 pfhub credential backfill-daemon-id --user --daemon-id --dry-run ``` ### 검증: NULL 0건 확인 ```sql -- 이 쿼리가 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 변경이다. **사장님 결재 후에만 실행한다.** ```bash 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 완료 확인 ```sql -- 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단계: 동작 확인 ```bash # pfhub CLI로 인증정보 조회/등록 테스트 pfhub credential list --user --daemon-id # 서비스 헬스체크 curl http:///health ``` --- ## 롤백 절차 ### Step2 롤백 (alembic downgrade) Step2 downgrade는 (user_id, broker) 중복이 없는 경우에만 가능하다. ```bash # Step2 → Step1 상태로 복원 alembic downgrade 20260628_0026 ``` **경고**: 같은 user의 여러 daemon이 동일 broker를 사용하는 경우 downgrade가 RuntimeError로 거부된다. 이 경우 수동 데이터 정리 후 downgrade하거나 백업에서 복원한다. ### Step1 롤백 ```bash # Step1 → 이전 상태로 복원 (daemon_id 컬럼 제거) alembic downgrade 20260605_0025 ``` ### DB 복원 (최후 수단) ```bash pg_restore -h -U -d -c backup_pre_bat021_.dump ``` --- ## 주의사항 - Step2 이전에 BAT-021 코드를 배포하면 `ON CONFLICT (daemon_id, broker)` 절이 구 스키마와 맞지 않아 오류 발생. - 배포 순서: Step1 → backfill → Step2 → 코드 배포 순서를 엄수한다. - `ensure_schema()`는 `IF NOT EXISTS`를 사용하므로 Step2 적용 후 실행 시 테이블이 이미 존재해 스킵됨 (신규 인스턴스 초기화 시에만 적용됨).