Skip to content

feat: HA/PITR 안정화 + 분산 SQL 라우터 + 온라인 resharding#280

Merged
eastroad1 merged 109 commits into
KeiaiLab:mainfrom
leedg:chore/ha-pitr-e2e-consolidation
Jun 29, 2026
Merged

feat: HA/PITR 안정화 + 분산 SQL 라우터 + 온라인 resharding#280
eastroad1 merged 109 commits into
KeiaiLab:mainfrom
leedg:chore/ha-pitr-e2e-consolidation

Conversation

@leedg

@leedg leedg commented Jun 29, 2026

Copy link
Copy Markdown

개요

chore/ha-pitr-e2e-consolidation 브랜치 전체를 올립니다. main 대비 109 커밋 / 148 파일 / +18,287 −576의 누적 작업으로, 단일 클러스터 HA·PITR 안정화부터 vanilla PostgreSQL 위 수평 샤딩 + 분산 SQL 라우터 + 온라인 resharding까지 포함합니다.

주요 변경 영역

HA / Failover

Backup / PITR

  • PITR restore 오프라인 오케스트레이션(STS scale 0 → Pod 정지 대기 → restore Job → annotation 해제)
  • builders/plugin 경로 정비(archive-push, restore --delta, restore_command 재작성, μs 정밀도)

Sharding / 분산 SQL 라우터 (cmd/pg-router, internal/router)

  • vindex: hash/range/consistent-hash(murmur3/fnv/crc32, 링 캐시)
  • 교체 가능 토폴로지(static ↔ CRD watch), failover-aware 백엔드(status.primary)
  • per-query routing: simple(persession.go) + extended(extsession.go) 매 쿼리 독립 라우팅, 샤드별 백엔드 lazy 풀링, prepare-on-first-use, tx pin
  • scram-sha-256 백엔드 인증 대행(scram.go, RFC 7677, 외부 의존 0) + describe-round로 lib/pq/pgx 등 실 드라이버 동작
  • scatter forwarding(scattermode.go): 키 없는 쿼리 전 샤드 병렬 fan-out → 재조립
  • reference table / read-replica 라우팅, bufio 최적화
  • 배포: Dockerfile.router + config/router/(SA/Role/Deployment/Service)

Online resharding (internal/controller, internal/router)

  • ShardSplitJob 전 phase K8s Job 결선: Bootstrap → InitialCopy → CDCCatchup(online 무중단 CDC) → Cutover(write-block) → RoutingUpdate → Cleanup
  • 데이터 충실도: 범위 복사(CopyShardRange), 인덱스/PK·CHECK/FK 제약 복제, 라이브쓰기 유실 0(TestCDCLive)
  • target 승격(ADR-0029 P-B): named shard-id label adopt(fenced single-authority), source retain-by-default
  • 라이브 full e2e(kind 실 K8s+실 PG): offline·online 양 경로 키유실 0 검증

Router 운영 / 관측

  • Router CPU HPA: spec.router.autoscalebuildRouterHPA(autoscaling/v2) + reconcile/delete gate + Owns(HPA) + RBAC + webhook bounds 검증
  • named shard topology를 ShardRange SSOT로 settle, aggregate status가 reshard target 편입

성능 / 벤치

  • cmd/router-bench + docs/perf/baseline.md §3.0~3.0f: single-shard baseline, 라우터 경유 동시성 스케일, prepared 재사용(1.9×), bufio(+3450%), 멀티샤드/멀티라우터 측정. 단일호스트 물리한계(2샤드 ≤ 1샤드) 확정

문서 / 테스트

  • ROADMAP/WORK_HANDOFF/ROUTER-TESTS/TEST_ANALYSIS를 현 구현 기준으로 동기화
  • envtest/단위 테스트 다수 추가(resharding 컨트롤러 결선, write-block, promote, HPA, scram, scatter, per-query 등), Windows go test wrapper

검증 상태 (정직한 고지)

  • ✅ 정적 코드 리뷰 + 단위/envtest 카탈로그 1:1 매핑 완료
  • ✅ 라우터/resharding의 라이브 e2e 상당수 통과(kind 실 PG): per-query/scatter/scram 라우팅, offline·online resharding full e2e(키유실 0)
  • ⚠️ 본 PR 묶음 전체에 대한 make test 일괄 재실행 green 확인은 미실시(작성 환경에 Go 미상주). 머지 전 CI 또는 Dev Container make test 권장
  • ⏳ 남은 live gate: native router 동시쓰기 무중단 resharding, target 승격 후 chaos/failover drill, 멀티머신 수평스케일 수치

참고

  • 규모가 큰 누적 PR입니다. 영역별 분할 리뷰가 필요하면 알려주세요.

🤖 Generated with Claude Code

NetworkModuleTest Developer and others added 30 commits June 25, 2026 16:51
router/cmd/chart-test 등 16개 파일의 라이선스 헤더를 저장소 LICENSE(MIT)와
일치시킨다. 로직 변경 없음.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
…ion guard

- aggregate_status: instance-status heartbeat 와 별개로 K8s Pod/컨테이너
  readiness 를 검사하고, fenced PVC 멤버를 promotion 후보에서 제외한다.
- failover_promoter: pg_ctl promote → pg_promote(SQL) 전환, 승격 전 old-primary
  PVC fencing, 승격 후보 K8s readiness 검증을 추가한다.
- postgrescluster_controller: primaryEndpointForShard 추출로 초기 HA bootstrap 의
  false failover window 를 차단하고 KeiaiLab#220 failback guard 를 적용한다.
- primary_endpoint.go 신규.
- failover_chaos_test: 승격 경로 완성으로 live chaos drill 을 PContext→Context 로
  활성화 (p1 게이트가 STS self-heal 의 promotion 선점 회귀를 잡도록 유지).

※ postgrescluster_controller.go 에는 PITR(KeiaiLab#209) 의 restore-in-progress 락
  (AnnotationRestoreInProgress) hunk 가 함께 포함된다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
- plugin: --archive-check=n 제거(복구 불가 백업을 실패로 노출), restore --delta,
  restore_command 를 repo env + --pg1-path 포함 형태로 재작성, targetTime
  microsecond 정밀도 보존.
- backupjob_controller: reconcileSidecarRestore — STS scale-0 → Pod 정지 대기 →
  data PVC 마운트 restore Job → 완료 시 restore-in-progress 락 해제.
- builders: pgBackRest filesystem repo 를 data PVC 내부로 이동, spool EmptyDir
  추가, archive-push "%p" 로 shell positional arg 문제 제거.

※ builders.go 에는 failover(KeiaiLab#220) 의 PVC cluster label hunk 가 함께 포함된다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
- quickstart 외부 전제를 제거하고 각 suite 가 ensurePostgresClusterReady 로
  독립 PostgresCluster 를 부트스트랩한다.
- DB/User live smoke 에 status.applied=true 검증(waitPostgresUserApplied)을 추가하고
  CRD spec 구조(cluster.name, schema 내부 privileges)와 kubectl exec -c postgres 를 정정.
- 동일 tag 재실행 시 manager Deployment rollout 을 강제하고 manager 이미지를
  0.4.0-beta.8 로 정렬한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
PgBouncer 동적 파라미터 허용 목록에 stats_users 를 추가한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
PROJECT_OVERVIEW / FEATURE_DEEP_DIVE / TEST_ANALYSIS / E2E_TEST_REPORT(PITR drill
완료 반영) 와 devcontainer·WSL 개발환경 가이드를 추가한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
devcontainer 정의/lock 을 갱신하고, Windows CRLF 로 .sh shebang 이 깨지던 문제를
.gitattributes 로 영구 차단한다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
- builders.go: pvcLabels 복사 루프를 maps.Copy 로 교체 (modernize/mapsloop).
- backupjob_controller.go: ensureClusterRestoreAnnotation 의 항상 nil 인
  ctrl.Result 반환값을 제거하고 단일 호출부를 정렬 (unparam).

동작 변경 없음. 남은 lint 지적은 테스트 코드 스타일 또는 미변경 baseline 파일.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
다른 환경에서 이 작업을 이어받거나 재검증할 수 있도록 정리:
- 브랜치 chore/ha-pitr-e2e-consolidation 의 커밋 8개 구성/의도와 교차 파일.
- 단위·통합·레이스 검증 결과와 baseline-fail lint/scripts 구분.
- 남은 라이브 K8s E2E(failover/PITR drill)와 Docker 2중 중첩 한계,
  실 Linux 호스트/실 K8s에서 돌리는 방법(make test-e2e-failover 등).
- go/make 없는 호스트에서 golang:1.26 컨테이너로 재검증하는 절차.
PROJECT_OVERVIEW 상단에 핸드오프 포인터 링크 추가.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
한국어 분석/작업/환경 문서군의 입구가 없어 관계·중복 관리가 어려웠다.
DOCS_MAP.ko.md 를 신설해 각 문서 역할, 주제별 SSOT(단일 출처),
계층 관계, 추후 보강 가이드를 정의한다.

- 발견된 중복: dev-setup 2종의 make타겟/e2e 절차, 검증 수치 산재,
  CRLF 조치 중복 → 삭제 대신 SSOT 지정 + 상호참조로 정리(보강 예정 전제).
- PROJECT_OVERVIEW 관련문서 표에 DOCS_MAP/TEST_ANALYSIS/E2E_REPORT/WORK_HANDOFF 추가.
- dev-setup-devcontainer/wsl 상단에 문서지도·상호참조·E2E 시나리오 링크.
- WORK_HANDOFF 상단에 DOCS_MAP 링크.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
여러 에이전트(Claude/Codex/Cursor) 및 작업자가 docs/ 를 편집해도 동일한
문서 규칙을 따르도록 구속력을 부여한다.

- 루트 AGENTS.md Conventions 에 "문서 작성 (docs/)" 절 추가:
  DOCS_MAP.ko.md 를 구속력 있는 규칙으로 지정(SSOT 준수, 계층 유지,
  보강 위치, 지도 갱신 의무, 상태는 코드로 확인).
- DOCS_MAP.ko.md 상단에 이 문서가 docs/ 작성 지침이며 AGENTS.md 가
  가리킨다는 점을 명시.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
용어 설명을 어디서 보든 동일하게(SSOT) 유지하면서, 각 문서 마지막
장에서 최소한의 의미파악이 되도록 발췌 형태로 둔다.

- docs/GLOSSARY.ko.md 신설: HA/failover, 백업/PITR, K8s/운영, 분산/샤딩,
  프로젝트/문서 범주의 용어 정의 단일 출처(SSOT).
- PROJECT_OVERVIEW / FEATURE_DEEP_DIVE / TEST_ANALYSIS / E2E_TEST_REPORT /
  WORK_HANDOFF 마지막 장에 "용어집" 절 추가 — 그 문서 등장 용어만
  GLOSSARY 정의를 그대로 발췌 + 전체 링크. (WORK_HANDOFF 말미의 불필요한
  코드펜스도 정리.)
- 지침화: AGENTS.md "문서 작성(docs/)"과 DOCS_MAP §1/§3/§5 에 용어집 유지
  규칙 추가 — 어떤 에이전트/작업자도 마지막 장 용어집을 유지하고 정의는
  GLOSSARY 를 먼저 고친다.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H4fFn82hrtvuCfuZnBMaqZ
manager lease가 reconcile-루프 failover(clusterFailoverDecision→executeClusterPromotion)를 이미 단일-active로 게이팅하므로, IsLeader()를 아무도(테스트 외) 참조하지 않던 전용 failover lease 배선을 cmd/main.go에서 제거(헬퍼·import 정리). lease.go는 leader 단일성·handoff 검증을 갖춘 building block으로 보존하되 헤더를 정직화(미배선·deadlock 주의). RFC 0007 §7 상태 정정. 순진한 IsLeader() 게이팅은 manager-lease holder≠failover-lease holder 시 deadlock이라, P2-T3는 failover를 reconcile 밖 runnable로 분리 후 게이팅해야 함(후행).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
RouteKeyExtractor 인터페이스로 라우팅키 추출을 런타임 선택 가능하게. regex(기본·정규식 컬럼 인지), parser(제로 의존성 토크나이저 — 따옴표/이스케이프/주석/복합 predicate/INSERT 위치/UPDATE·DELETE 정확 처리), auto(parser 우선+regex fallback). 외부 SQL 파서(auxten)는 ~25 transitive 모듈 + 옛 genproto가 오퍼레이터 현대 deps와 ambiguous import 충돌로 빌드 파괴 → 기각, 자체 토크나이저로 대체(murmur3 철학, dep 0). docs/sharding/ROUTER-GAP-ANALYSIS.ko.md: 라우터 두 반쪽 분리 진단 + Vitess-for-Postgres 능력 사다리 + 향후 대작업 백로그.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
key→shard(TopologyProvider)와 shard→backend(BackendResolver)를 분리(교체 가능). StaticTopologyProvider(env)·CRDTopologyProvider(ShardRange CRD watch, hot-reload). StatusBackendResolver는 PostgresCluster.status.shards[].primary.endpoint(Ready만)에서 백엔드 해소 → operator failover가 status 갱신하면 라우터가 자동 추종; primary 부재/not-ready는 에러. K8s는 ShardRangeLister/ClusterStatusReader 인터페이스로 가장자리 격리(router 패키지 순수 유지). pg-router: PGROUTER_TOPOLOGY=static|crd, PGROUTER_BACKEND=env|template|status, dial 타임아웃, 샤드 down 시 조용한 drop 대신 PostgreSQL ErrorResponse로 우아하게 실패. 단위 검증(failover 시뮬·헬스 게이팅·에러 포맷, 라이브 클러스터 불필요).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
pg-router를 실제 배포 가능한 컴포넌트로. Dockerfile.router(distroless 정적 바이너리, operator와 분리). config/router/: ServiceAccount + 최소권한 Role(shardranges·postgresclusters get/list/watch) + RoleBinding + Deployment(replicas 2, restricted SecurityContext, TCP probe, PGROUTER_BACKEND=status·dial 타임아웃) + Service(:5432) + kustomization + README. kustomize build 렌더 검증. docs/WORK_HANDOFF.ko.md: 이번 세션 변경 인수인계.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
호출마다 sql.Open/Close 하던 것을 shard별 *sql.DB(자체 풀) lazy 캐시·재사용으로 교체 — scatter fan-out 부하에서 연결 폭주·지연 방지. MaxOpenConns/MaxIdleConns/ConnMaxIdleTime 튜닝(기본 10/5/5m) + Close(graceful). 백로그 (D) 항목.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
죽은 샤드가 매 연결마다 dial 타임아웃을 다 기다리게 하지 않도록: bounded retry/backoff + failThreshold 연속 실패 시 backend를 cooldown 동안 fast-fail(circuit open). 성공 시 breaker 리셋, cooldown 경과 후 half-open 재시도. dial/clock 주입으로 단위 검증(retry·open·cooldown·reset). env: PGROUTER_DIAL_RETRIES/BACKOFF, PGROUTER_BREAKER_THRESHOLD/COOLDOWN. 백로그 '라우터 HA 강화'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
…+ IsReadOnlyQuery)

StatusBackendResolver가 Ready replica도 추적, ResolveRead가 replica를 round-robin 선택(읽기 확장+primary 부하 경감), replica 없으면 primary 폴백. IsReadOnlyQuery는 토크나이저로 보수적 분류(확실한 읽기만 true; SELECT FOR UPDATE/SHARE·DML 포함 WITH는 쓰기 — 오분류 비용 비대칭). 쿼리 단위 결선은 (E)에서. 백로그 '읽기→replica'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
ShardRangeSpec에 referenceTables 추가(모든 샤드에 복제되는 공통 테이블 선언, CRD/deepcopy/chart 재생성). 라우터: ExtractTables(토크나이저로 FROM/JOIN/INTO/UPDATE 테이블 추출), Topology.ReferenceOnly(쿼리가 reference 테이블만 참조하는지), AnyShard(결정적 첫 샤드). reference-only 쿼리는 샤딩 키 없이 임의 샤드로 → 분산 조인 우회. (E)에서 결선. 백로그 'reference table'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
MergeOrderBy의 fmt.Sprintf 문자열 비교 버그(숫자 '10'<'9') 수정 — compareValues로 숫자는 수치 비교, []byte/문자열은 사전식. ScatterGather.Limit로 merge 후 행 수 제한(LIMIT pushdown 근사). 백로그 'scatter-gather merge 개선'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
한 쿼리에 대해 토폴로지(vindex)+라우팅키 추출+reference-table+읽기/쓰기 분류+백엔드 해소를 단일 RouteDecision으로 합성하는 routing brain. reference-only→AnyShard, 키 추출→단일 shard, 키 부재→Scatter+ErrNoRoutingKey, 읽기→replica resolver. 순수·동기라 완전 검증 가능 — (E) 프로토콜 종단(vtgate급 자체 인증+백엔드 풀+결과 재조립)은 별 대작업으로 백로그 유지하되, 그 핵심 결정 로직은 본 엔진으로 확정.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
2026-06-26 세션 백로그 처리 결과 — 완료 7건(풀링·HA dialer·읽기replica 부품·reference table·scatter merge·QueryRouter), 보류 3건(per-shard primary Service·watch·failover lease P2-T3, 무검증 랜딩 금지 사유)를 ROUTER-GAP-ANALYSIS §6 + WORK_HANDOFF §6.4에 기록.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
ErrVindexUnsupported로 막혀 있던 consistent-hash를 해시 링으로 구현 — 각 샤드를 VirtualNodes개 가상 노드로 링에 흩뿌리고 키를 시계방향 최근접 노드의 샤드로. 샤드 추가 시 ~1/N 키만 이동(테스트: 3→4 샤드에 29%만 이동, modulo 해시는 ~75%). murmur3/fnv/crc32 재사용, VirtualNodes 미설정 시 기본 128. NewConsistentHashRing은 라우터가 캐시 가능. 백로그 'consistent-hash vindex'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
호스트 kind에 오퍼레이터 배포 → 단일샤드 PostgresCluster(PG18.3) Ready → pgbench. 첫 제품 수치: tpcb-like(RW) 496/646/889 TPS @ c=8/16/32, select-only(RO) 9035/10493 TPS @ c=32/64. 환경 metadata(16vCPU/7.6GiB WSL2, local-path, client co-located)+해석(쓰기는 WAL fsync 바운드, 읽기 ~10-18배)+재현 명령 기록. percentile/분산/sysbench/전용 PV는 후속. baseline.md 상태 skeleton→1차 실측.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
internal/router(38) + cmd/pg-router(10) 테스트케이스를 영역별로 색인(vindex/추출/read-write/토폴로지/백엔드해소/reference/QueryRouter/scatter/풀/resharding/placement/metadata/wire프록시) — 각 테스트가 무엇을 검증하는지 + 실행법(컨테이너) + 라이브 검증(perf 완료, failover/PITR drill 미완) 정리. DOCS_MAP에 라우터 갭분석·테스트 카탈로그·perf baseline 3건 등록.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
잘못된 샤드 라우팅을 막는 P0 정확성 보강 2건: ① whereEqTok이 같은 샤딩 컬럼에 서로 다른 리터럴(서브쿼리/OR)을 보면 추측하지 않고 추출 실패→scatter (이전엔 첫 매치 무조건 채택, 서브쿼리의 다른 tenant_id로 쓰기가 갈 수 있었음). ② 토크나이저가 dollar-quote($$...$$/$tag$...$tag$)를 단일 문자열로 처리 — 함수 본문 속 가짜 predicate가 토큰으로 새어 false-match 되던 것 차단. dollar-quote 리터럴 자체는 정상 키로, $1 파라미터는 여전히 비키.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
StatusBackendResolver.MaxReplicaLagBytes — >0이면 lag 초과 replica를 ResolveRead 대상에서 제외(과도하게 stale한 읽기 방지). 0=무제한(현행). 단위 검증. env 결선은 ResolveRead가 프록시에 결선되는 (E) 시점에. 백로그 'ResolveRead lag 무제한' 보강.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
코드 검토에서 나온 hardening 항목을 백로그에. 완료: 모호 키 bail·dollar-quote·lag 임계. 추적: 링 캐시·다중컬럼 ORDER BY·LIMIT pushdown·breaker half-open·ConnMaxLifetime·stale 캐시 정책.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
ResolveShard가 consistent-hash마다 링을 재구성하던 핫패스 낭비 제거 — function|vnodes|정렬 shard집합 fingerprint로 sync.Map 캐시(링 불변이라 동시성 안전). 토폴로지 변경 시 fingerprint가 달라져 새 링. (테스트 미실행 — 다음 배치 검증.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
NetworkModuleTest Developer and others added 28 commits June 28, 2026 15:57
WORK_HANDOFF §6.6: online resharding full e2e(kind 실 K8s+PG) 성공 — CDCCatchup(cdc-setup
→write-block→cdc-finalize)→Completed, t0=44/t1=56/source=0 키유실0 + kv_pkey 복제 + flip.
offline+online 양 경로 라이브 검증 완료. 새 KeiaiLab#1=native 라우터 동시쓰기 무중단 실증 + target
영구 승격.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
ReplicateConstraints: source 의 CHECK·FK 제약을 target 에 멱등 복제(PK·UNIQUE 는
ReplicateIndexes 가 인덱스로 처리). CHECK 는 항상 안전(실패 시 에러), FK 는 참조 테이블이
같은 shard 에 없으면(cross-shard) best-effort 로 건너뜀. 데이터 복사·sync 후 호출.
offline(CopyShardRange)·online(cdc-finalize) 양 경로 결선.

이로써 resharded target 의 스키마 충실도 완성: 컬럼 + 인덱스/PK + CHECK/FK 제약.

검증: 라이브(TestCDCLive 확장) — kv_val_pos CHECK 제약 target 복제 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
WORK_HANDOFF §6.6: ReplicateConstraints(CHECK/FK) 완료·라이브검증, resharded 스키마
충실도 완성. target 영구 승격은 ADR-0027 정체성 격리로 RFC급 설계 선행 필요로 명시.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
resharding 산출 target shard 를 cluster 1급 시민(HA·관측·failover)으로 승격하는 정체성
전이 설계. ADR-0027 P6(미상세)를 구체화: ordinal→명명 shard 일반화(shard-id label),
fenced single-authority 승격(source 관측제외→target adopt→status 편입→source 폐기),
ordinal rename 회피(ShardRange 이름 라우팅 보존), KeiaiLab#220-class race 회피. 구현은 P-A~P-C
분할(코드 미착수 — 정체성-임계라 설계 선행).

(ADR-0028 파일은 세션 전부터 미커밋 WIP 였으며 INDEX 링크 일관성 위해 동봉.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
명명(named) shard 식별의 토대 — ordinal shard pod 에 `postgres.keiailab.io/shard-id=
shard-<N>` label 을 *부가* 한다(P-B 승격이 이 통합 label 로 target 을 cluster 1급 시민으로
편입).

- names.go: ShardIDLabelKey + ShardIDForOrdinal(`shard-<N>`).
- buildPGStatefulSet: podLabels = 셀렉터(labels)의 superset + shard-id. **셀렉터(STS
  .spec.selector)에는 미포함** — 기존 STS selector 불변 + 업그레이드 중 구 pod 누락
  (KeiaiLab#220-class race) 회피. reshard target 은 격리 유지(shard-id 미부가, 승격 시 부여).

behavior-preserving(셀렉터·기존 ordinal label 불변, pod 에 라벨 1개 추가). 단위검증:
ordinal pod 에 shard-id=shard-2, 셀렉터엔 미포함, reshard target 격리 유지.

ADR-0029 다음: P-A.2(aggregate/metrics/failover selector 일반화) → P-B(Promote phase).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
코드 리뷰 발견: CreatePublication 이 DROP+CREATE 라, cdc-setup Job 재시도(pod kill 등) 시
활성 subscription 이 의존하는 publication 을 drop 해 스트림이 깨질 수 있었다. pg_publication
존재 확인 후 skip 으로 변경(drop 안 함). build/vet/gofmt clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
다른 곳에서 작업 이어갈 수 있도록 인수인계 보강: 세션 성과(라우터 종단·온라인 resharding·
성능·target 승격 P-A), 코드 리뷰 발견(CreatePublication 수정 + wal_level restart/PK-없는-target
동시쓰기 미검증/abort 누수/FK best-effort 주의 + 안전 확인 항목), 다음 진입점(ADR-0029 P-A.2→P-B
target 승격, native 라우터 동시쓰기 e2e, abort 정리), 라이브 e2e 재현 절차.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
- reference table write가 임의 shard로 라우팅되지 않게 차단

- keyless write scatter 거부 및 scatter error ReadyForQuery 보장

- reshard-copy Job에 ShardRange vindex type 전달

- online reshard 실패/abort 시 cdc-abort cleanup Job 추가

- shard-id 기반 status aggregation 준비 및 ADR/e2e 계획 문서화

검증: WSL Go 1.26.4에서 go test -count=1 ./internal/router ./cmd/pg-router ./cmd/reshard-copy-poc ./internal/controller
Reshard target Pods now receive the actual StatefulSet service name as POSTGRES_SERVICE_NAME and the instance manager uses it when reporting Pod status endpoints. Existing Pods without the env var keep the legacy ordinal fallback for upgrade compatibility.

This closes a Promote P-B precondition: target Pods must not publish source ordinal headless DNS before status/router/failover starts resolving promoted named shards.

Verification:

- go test -count=1 ./cmd/instance

- go test -count=1 ./internal/controller -run TestBuildTargetShardStatefulSet_Isolation

- go test -count=1 ./cmd/instance ./internal/router ./cmd/pg-router ./cmd/reshard-copy-poc ./internal/controller
PostgresCluster status now includes active non-ordinal shard names from ShardRange.spec.ranges. Reshard target Pods are aggregated by reshard-target or adopted shard-id labels and exposed as name=<target>, ordinal=-1 so the status backend resolver can route to targets after RoutingUpdate.

This is a P-B status/backend-resolution slice only. Source decommission, target HA scale-up, Promote idempotency, and named shard spec migration remain follow-up work.

Verification:
- go test -count=1 ./internal/controller -run TestAggregateNamedShardStatus_UsesReshardTargetLabel
- go test -count=1 ./internal/controller --ginkgo.focus="adds active named reshard targets"
- go test -count=1 ./internal/controller
- go test -count=1 ./cmd/instance ./internal/router ./cmd/pg-router ./cmd/reshard-copy-poc ./internal/controller
Native clusters now treat ShardRange.spec.ranges as the active shard topology once ShardRange exists. Ordinal source shards that are no longer active are scaled to zero and excluded from PostgresCluster.status.shards, while StatefulSet/PVC ownership is retained.

This lets a ready named target shard drive cluster readiness after routing flip instead of being held back by a stale shard-0 fallback status row. Target HA scale-up, explicit Promote phase idempotency, and named shard spec migration remain follow-up work.

Verification:
- go test -count=1 ./internal/controller --ginkgo.focus="adds active named reshard targets"
- go test -count=1 ./internal/controller
- go test -count=1 ./cmd/instance ./internal/router ./cmd/pg-router ./cmd/reshard-copy-poc ./internal/controller
PostgresCluster reconciliation now maintains active non-ordinal target shard ConfigMaps, Services, and StatefulSets once ShardRange points at those shards. Active target StatefulSets scale to 1 + spec.shards.replicas and render POSTGRES_MEMBER_COUNT plus the target primary endpoint for replica bootstrap.

Target identity isolation is preserved: selectors still use reshard-target labels and Pods keep POSTGRES_RESHARD_TARGET for the isolated election lease. Explicit Promote phase idempotency and named shard spec migration remain follow-up work.

Verification:
- go test -count=1 ./internal/controller --ginkgo.focus="adds active named reshard targets"
- go test -count=1 ./internal/controller
- go test -count=1 ./cmd/instance ./internal/router ./cmd/pg-router ./cmd/reshard-copy-poc ./internal/controller
Add ShardSplitJob Promote between Cleanup and Completed, and adopt target shard identity by patching target StatefulSet/template/live Pod shard-id labels while preserving reshard-target selectors.

Regenerate ShardSplitJob CRDs and update handoff/ADR/plan docs with completed adopt scope and remaining source cleanup/chaos work.
Require ShardSplitJob Promote to see sources absent and targets active in the matching ShardRange before adopting target shard-id labels.

Keep Promote requeueing without label mutation while the source is still active, and document the remaining readiness/source cleanup work.
Require ShardSplitJob Promote to see a Running PodReady target pod before applying shard-id labels to target resources.

Keep Promote requeueing without label mutation while the target is not Ready, and document the remaining source cleanup/model migration work.
Assert inactive ordinal source resources are retained by default: StatefulSet scales to zero while Service, pre-existing PDB, and PVC remain.

Document source deletion as a future explicit opt-in design rather than automatic Promote cleanup.
Document ShardRange as the active named-shard topology source of truth and avoid adding a duplicate PostgresCluster named-list field.

Update handoff and hardening plan so the remaining resource-saving-mode gap is live chaos/e2e validation.
…tion

- ROADMAP.ko G4 전 phase 결선(InitialCopy/CDC/Cutover/Cleanup/Promote)·
  G5 per-query/scatter/reference·G3 router CPU HPA 반영, Change log 2건
- WORK_HANDOFF 6.9 router autoscaling "미구현"→CPU HPA 구현완료 정정
- ROUTER-TESTS.ko 2026-06-29 기준 테스트 카탈로그 재구성
  (resharding 컨트롤러 결선 envtest §2.12, router HPA §2.13, router-bench §2.14,
   라이브검증 §3 누적)
- TEST_ANALYSIS 커버리지 스냅샷 stale 경고 + 최신 카탈로그 포인터

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TnVYb8dUQFg2GSiSwo1dtZ
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@eastroad1 eastroad1 merged commit 25476e1 into KeiaiLab:main Jun 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants