# REQ-017: service-token rotate unique index 보강 - Status: Implemented - Type: Migration - Priority: High - Owner: Justin Ko ## Intent - 현행 `idx_auth_tokens_service_name_unique` partial index가 `WHERE token_type='service'` 조건만 적용해 revoked row와 새 row가 같은 name으로 unique 충돌을 일으켜 `pfhub service-token rotate`가 실 DB에서 insert 실패하는 결함을 정상화한다(REL-006 verification 실패 기록). - index 조건에 `AND revoked_at IS NULL`을 추가해 active service token name만 unique 유지하도록 보강한다. ## Requirement - Alembic migration 신규 작성: 기존 `idx_auth_tokens_service_name_unique`를 drop하고 `(name)` partial index를 `WHERE token_type='service' AND revoked_at IS NULL` 조건으로 재생성한다. - migration은 `CREATE INDEX CONCURRENTLY` 적용으로 운영 lock 영향을 최소화한다(autocommit 외부 트랜잭션 필요 — Alembic op.execute로 직접 SQL 실행). - `app/repositories/postgres.py:161-167` `ensure_schema()`의 `idx_auth_tokens_service_name_unique` 정의도 동일 조건으로 갱신한다. - 기존 active service token 행 데이터에는 변경 없음. 데이터 backfill 불요. - rotate 동작은 기존 코드(`app/services/auth_service.py:rotate_service_token`, `app/repositories/postgres.py:1502-1549`)를 변경하지 않으며, index 보강만으로 정상 작동해야 한다. ## Acceptance Criteria 1. Given 신규 Alembic revision 적용 후, When `pfhub service-token issue NAME --user U --no-expiry` → `pfhub service-token rotate NAME --force` 시퀀스 실행, Then 두 명령 모두 unique 위반 없이 성공한다(실 DB `docker-compose.local-test.yml` 환경). 2. Given AC-1 시퀀스 실행 후, When `SELECT name, token_type, revoked_at FROM auth_tokens WHERE name = ''`, Then revoked row 1건 + active row 1건이 존재한다. 3. Given AC-1 rotate 결과, When 새 token의 `runtime_id`와 `daemon_id`를 확인, Then runtime_id는 갱신, daemon_id는 유지(rotate invariant 보존). 4. Given `ensure_schema()` 단독 실행, When 새 partial index 조건이 적용, Then 동일 조건(`WHERE token_type='service' AND revoked_at IS NULL`)이 PostgreSQL에 반영된다. 5. Given migration이 운영 환경에서 적용, When CREATE INDEX CONCURRENTLY가 실행, Then auth_tokens write 작업이 long-running lock 없이 진행된다(staging 환경 사전 검증). ## Impacted Area - Docs: - `docs/data-model.md` — auth_tokens 인덱스 변경 반영 (REQ-019에서 처리) - Modules: - `migrations/versions/` — 신규 Alembic revision (예: `20260520_NNNN_service_token_unique_index_fix.py`). `op.execute("DROP INDEX IF EXISTS ...")` + `op.execute("CREATE UNIQUE INDEX CONCURRENTLY ... WHERE token_type='service' AND revoked_at IS NULL")` 패턴 - `app/repositories/postgres.py:161-167` — `ensure_schema()`의 idx 정의 조건 갱신 - Tests: - 신규 `tests/test_service_token_rotate_integration.py` (실 DB 의존 마킹) — AC-1~3 검증 - 기존 `tests/test_daemon_registry.py::test_rotate_*` — 신규 index 조건에서 통과 확인 ## Notes - 이 결함은 REL-006 verification에 "rotate insert 막힘" 검증 실패로 이미 기록됨([temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md:74](temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md#L74)). - CREATE INDEX CONCURRENTLY는 transactional block 밖에서 실행 필요. Alembic revision에서 `autocommit_block`을 사용하거나 `op.execute()`로 직접 SQL 실행해야 한다. - 기존 운영 환경에 revoked service token이 있다면 새 partial index 생성 시점에 자동 적용되며, 별도 데이터 정리 불요. - Source: dcy-004 FR-2, dcy-004 NFR-2/NFR-3/NFR-6, dcy-004 OQ-2 결정(partial 조건 추가). ## Change Log ### CHG-20260521-01 - Date: 2026-05-21 - Author: Justin Ko #### Change Summary - Status: Approved → Implemented #### Intent - BAT-006 confirm-batch-verification 완료. idx_auth_tokens_service_name_unique partial index 조건 `AND revoked_at IS NULL` 보강 Alembic revision + ensure_schema() 갱신 구현. AC-1~3/5(실 DB issue/rotate 시퀀스 및 staging lock 검증)는 실 DB 의존 — release 배포 시 최종 확인 예정. #### Acceptance Criteria Delta - No change #### Impacted Area Delta - No change #### Delivery Trace Delta - Batches: - BAT-006 - Releases: - REL-001 #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: implemented - Follow-up Action: REL-001 배포 시 실 DB에서 issue → rotate 시퀀스로 AC-1~3 최종 확인 ### CHG-20260520-02 - Date: 2026-05-20 - Author: Justin Ko #### Change Summary - Status: Proposed → Approved #### Intent - 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC 구체성 확인 완료, blocker 없음 #### Acceptance Criteria Delta - No change #### Impacted Area Delta - No change #### Delivery Trace Delta - Batches: none (미정) - Releases: none (미정) #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: promote-to-approved - Follow-up Action: batch 계획 편입 ### CHG-20260520-01 - Date: 2026-05-20 - Author: Justin Ko #### Change Summary - 최초 생성 — dcy-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-2 (rotate unique index 보강) 산출물. #### Intent - rotate 실 DB 실패 결함 해소 + R-2(migration lock) 완화 적용. #### Acceptance Criteria Delta - Added: - AC-1~5 최초 작성 #### Impacted Area Delta - Docs: docs/data-model.md (REQ-019에서 처리) - Modules: migrations/versions/, app/repositories/postgres.py — 최초 지정 - Tests: tests/test_service_token_rotate_integration.py (신규), tests/test_daemon_registry.py — 최초 지정 #### Delivery Trace Delta - Batches: none (미정) - Releases: none (미정) #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: confirm-req 후 batch 계획 편입; staging 환경에서 CONCURRENTLY migration dry-run으로 lock 시간 측정 #### Validation Plan - 실 DB(`docker-compose.local-test.yml`) 환경에서 AC-1~3 시퀀스 검증; staging 환경에서 AC-5 lock 측정