notes(hjjeong): cross-tenant §11.2 부분 실패 시뮬레이션 검증 (2026-04-29)

테넌트 DB 만 만져도 됨 — 메타 DB 무수정.
TEST02 의 USER_INFO 를 임시 RENAME 해서 SELECT 실패 유도 → fan-out
호출 → 즉시 롤백. 메타 DB·다른 테이블 일체 영향 없음.

결과:
- RENAME 후 /users → HTTP 200, header X-CrossTenant-Failed: TEST02
  total=9 q=2 failed=1 by={'*':8, 'TEST01':1}  (TEST02 0)
- 롤백 후 /users → total=10 failed=0 by 원복

검증된 항목:
- fail-open: 한 회사 실패해도 전체 응답 200
- 회사 격리: TEST01·메타 행 영향 없음
- companies_failed: 1 + failed_company_codes: ["TEST02"]
- 응답 헤더 X-CrossTenant-Failed: TEST02

문서 갱신:
- 설계 27.md §11.2:  + 실행로그 §9.4 링크
- 실행 28.md §9.4 신설 — 시뮬레이션 SQL + 결과 + 검증 항목 5개
- 실행 28.md §9.5 — 남은 시나리오 (§11.4 락 비획득 / §11.5 캐시 N/A)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hjjeong
2026-04-29 11:42:12 +09:00
parent 568eb14503
commit 280e25a4df
2 changed files with 32 additions and 4 deletions
@@ -424,7 +424,7 @@ const editUrl = `https://${row.subdomain}.invyone.com/admin/userMng/userMngList?
| 시나리오 | 상태 |
|---|---|
| 11.1 행복 경로 | ✅ 2회사 (TEST01 + TEST02) 머지 실증 완료 (2026-04-29). [실행로그 §9](2026-04-28-cross-tenant-execution-log.md) 참조 |
| 11.2 부분 실패 | ⏳ 미검증 — TEST02 DB pg_terminate 시뮬레이션 필요 |
| 11.2 부분 실패 | ✅ 완료 (2026-04-29) — TEST02 USER_INFO RENAME 으로 SELECT 실패 유도, fail-open + `X-CrossTenant-Failed` 헤더 + `companies_failed: 1` 모두 확인. [실행로그 §9.4](2026-04-28-cross-tenant-execution-log.md) 참조 |
| 11.3 권한 | ✅ `super_admin_required` (토큰 없이 호출 → 403) — 스모크 (실행로그 §1) + fan-out 검증 (실행로그 §9.3) 모두 통과 |
| 11.4 락 비획득 | ⏳ 미검증 |
| 11.5 캐시 무효화 | ⏳ N/A — 캐시 미구현 (Phase D) |
@@ -333,10 +333,38 @@ curl http://localhost:8081/api/auth/status
- [x] 페이지네이션 cap — 모든 회사가 cap 200 미만이라 `truncated: false`. cap 동작은 2026-04-28 §3.5 에서 이미 검증 (646→200)
- [x] 권한 가드 — 토큰 없이 `/_active-companies` 호출 → `403 super_admin_required`
### 9.4 미검증 (남은 시나리오)
### 9.4 §11.2 부분 실패 시뮬레이션 (2026-04-29 추가)
- §11.2 부분 실패 — TEST02 DB pg_terminate 후 fan-out, `companies_failed: 1` + `X-CrossTenant-Failed: TEST02` 확인
- §11.4 락 비획득 — TEST02 풀 maxPool 점유 후 `connection-timeout`
테넌트 DB 만 만져도 된다는 사용자 OK 받고 진행. 메타 DB 무수정.
**플랜:** TEST02 의 `USER_INFO` 테이블을 임시 RENAME 해서 SELECT 실패 유도 → fan-out 호출 → 즉시 롤백.
```sql
-- test02_invyone 만
ALTER TABLE USER_INFO RENAME TO USER_INFO_HJTEST_BAK; -- 시뮬레이션 시작
-- (curl /users)
ALTER TABLE USER_INFO_HJTEST_BAK RENAME TO USER_INFO; -- 즉시 롤백
```
**결과:**
| 단계 | 응답 |
|---|---|
| 1. RENAME 후 `/users` | HTTP 200, header `X-CrossTenant-Failed: TEST02`, body `total=9 q=2 failed=1 by={'*':8, 'TEST01':1}` |
| 2. 롤백 후 `/users` | `total=10 failed=0 by={'*':8, 'TEST01':1, 'TEST02':1}` — 완전 복귀 |
**검증된 것:**
- [x] **fail-open** — 한 회사 SELECT 실패해도 전체 응답 200, 다른 회사 + 메타 결과 그대로 반환
- [x] **회사 격리** — TEST01·메타 행은 영향 없음 (`'*': 8, 'TEST01': 1` 그대로)
- [x] **`companies_failed: 1`** + `failed_company_codes: ["TEST02"]`
- [x] **응답 헤더 `X-CrossTenant-Failed: TEST02`** — 클라이언트가 토스트 띄우기에 충분한 정보
- [x] 백엔드 로그에 `[CrossTenant] mapper=admin-cross-tenant.listUsers failed for company=TEST02 db=test02_invyone : ...` 형태 메시지 (Aggregator line 159-161)
설계서 §11.2 가 이 결과로 ✅ 처리됨.
### 9.5 여전히 미검증 (남은 시나리오)
- §11.4 락 비획득 — TEST02 풀 maxPool 점유 후 `connection-timeout`. Hikari 풀 점유 시뮬레이션 필요 (별도 부하 테스트 환경)
- §11.5 캐시 무효화 — 캐시 자체가 Phase D 라 N/A
### 9.5 명령 (재현용)