Files
llm-wiki/raw/branch-notes/feature-database-connection-pool-contract.md
T

36 KiB
Raw Blame History

title, source_type, status, branch, parent_branch, related_projects, governing_docs, tags, created, target_merge, status_label, id, kind, project, work_item, inherits, refines, overrides, depends_on, contract_packet, contract_packet_sha256
title source_type status branch parent_branch related_projects governing_docs tags created target_merge status_label id kind project work_item inherits refines overrides depends_on contract_packet contract_packet_sha256
branch / feature-database-connection-pool-contract branch-note raw feature-database-connection-pool-contract
ca-skeleton
wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound
branch
ca-skeleton
persistence
hikaricp
connection-pool
database
2026-06-09 in-progress BR-CA-SKELETON-OPERATIONAL-CONTRACT-050 project-work-item ca-skeleton-operational-contract WI-CA-SKELETON-OPERATIONAL-CONTRACT-050
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1
WI-CA-SKELETON-OPERATIONAL-CONTRACT-004
WI-CA-SKELETON-OPERATIONAL-CONTRACT-006
WI-CA-SKELETON-OPERATIONAL-CONTRACT-035
WI-CA-SKELETON-OPERATIONAL-CONTRACT-019
1 8bb34c64971d280776b949d71b980bec1b4203786e4043b7a22ca9f6174104ec

branch: feature-database-connection-pool-contract

Layer: raw/branch-notes/ — 단일 브랜치의 TODO·결정·진행 기록. 머지/종료 후 verified 결과는 /ingestwiki/projects/에 추출. 원본은 raw에 영구 보관. status_label: in-progress | review | merged | abandoned

부모 (필수)

ca-skeleton 은 별도 root branch 없이 project-note 가 SSOT 역할. 본 feature branch 는 project-note 의 운영 계약 중 §9 Env-driven Runtime Configuration (DB pool env) · §11 Adapter Failure Contract — Persistence · §18 Metrics/Alerting (DB pool metric) 영역의 connection pool 설정 정책 을 정제한다. 분해표 위치: project-note §B "데이터/영속성 영역" priority #4 (L2031/L2082).

선택 (형제 branch — DB pool 관심사 공동 소유):

브랜치 계약 패킷

  • 생성 시 프로젝트 개정: 1
  • 패킷 스키마: contract_packet: 1
  • 완료 조건: connection pool 설정·lifecycle·metric·failure gate가 명시된다

상속한 프로젝트 결정

Decision Ref Project Summary Branch Application Source
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-STACK-DATABASE-001@1 database는 PostgreSQL 16 단일 stack이다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract
DEC-CA-SKELETON-OPERATIONAL-CONTRACT-RESILIENCE-001@1 Resilience4j가 default이며 Spring Retry는 simple blocking retry에만 허용한다 Work Item 완료 조건에 적용 raw/project-notes/ca-skeleton-operational-contract

브랜치 지역 결정

기존 branch-local 결정은 아래 ## Decision Evidence Map / 결정-근거 매핑의 D-row가 소유하며 이 packet에서 복제하지 않는다.

Decision ID Decision Relation Supporting Claims Status

선언한 예외

Override ID Overrides Reason Approval Status

목표

ca-tmpl 의 DB 접근은 HikariCP 위에서 동작하지만, 풀 설정값의 "정책/근거" 는 어디에도 고정되어 있지 않다. 현재 application.yml 에는 5개 knob (maximum-pool-size/minimum-idle/connection-timeout/idle-timeout/max-lifetime) 만 env binding 되어 있고, 운영 안정성에 직결되는 leak detection / keepalive / validation timeout / 초기화 fail-fast / slow query 탐지 는 미설정·미결정 상태다.

이 브랜치는 env key 의 값 자체 (그건 env-driven 이 소유) 가 아니라, 그 값들이 왜 그래야 하는가 + knob 간 제약 관계 + 아직 노출 안 된 knob 의 채택 여부 + slow query 를 어느 계층에서 파라미터 노출 없이 탐지할지 를 결정한다. 목표는 persistence 코드를 작성하는 다음 사람이 되묻지 않고 HikariConfig 와 application.yml 을 채울 수 있는 수준의 정책 명세.

  • 이슈:
  • PR:

범위

포함 범위

  • Pool sizing 정책 — 고정 크기 풀(minimumIdle = maximumPoolSize) 권고 vs 현재 min-idle=2 설정의 정합, HikariCP small-pool axiom + formula 를 default 값의 근거 로 고정 (값 자체 변경은 env-driven 소유).
  • connectionTimeout 정책 — 30s 기본 대신 fail-fast 값 pin 의 근거 + 의미.
  • maxLifetime 정책 — DB/인프라 idle timeout 보다 수 초 짧게 (production 최우선 설정), DB wait_timeout 대조 절차.
  • keepaliveTime 채택 (greenfield — 미노출 knob) — 방화벽/DB idle-kill 방지, < maxLifetime 제약.
  • leakDetectionThreshold 채택 (greenfield — 미노출 knob) — 활성화 여부 + 임계값 정책, runbook "leak detection 활성화" 의 실 설정 backing.
  • initializationFailTimeout 정책 (greenfield) — 풀 초기화 시 startup fail-fast 동작, runtime-health startup validation 과 정합.
  • validationTimeout 정책 (greenfield) — < connectionTimeout 제약 강제 (현재 잠재 충돌).
  • slow query 탐지 메커니즘 (greenfield) — 어느 계층에서 1s+ 쿼리를 파라미터 노출 없이 탐지/로깅할지 (HikariCP 는 쿼리 인터셉터 미제공).

제외 범위

의도적으로 제외 — 다른 owner branch 가 소유하거나 별도 영역.

  • DB pool env key 등록·검증 (APP_DATASOURCE_POOL_MAX_SIZE/_MIN_IDLE/_CONNECTION_TIMEOUT/_POOL_IDLE_TIMEOUT/_POOL_MAX_LIFETIME + numeric bounds) → feature-env-driven-runtime-configuration 소유. 본 브랜치는 greenfield knob 의 신규 key 등록을 제안 하되 등록 자체는 그 브랜치로 위임.
  • Pool exhaustion alert threshold (pool wait p99 > 100ms 5분 → P2, active=max > 1분 → P1) → raw/branch-notes/feature-persistence-failure-baseline D3 소유.
  • Pool metric 이름 (hikaricp.connections.acquire/.usage/.active) → feature-persistence-failure-baseline + feature-metrics-alerting-contract 공동 소유.
  • Pool-acquire-timeout 실패 분류 (커넥션 미확보 → DB_UNAVAILABLE 503 retryable) → feature-persistence-failure-baseline 소유.
  • SQLState classifier / OSIV offfeature-persistence-failure-baseline.
  • Read replica lag threshold / PgBouncer transaction pooling → 미생성 별도 branch (project-note §11 deferred).
  • Transaction isolation / lock 정책feature-transaction-concurrency-contract.

근거 (필수, 최소 1개+)

Source 정당화하는 결정
raw/official-docs/persistence-hikaricp-configuration-knobs connectionTimeout/maxLifetime/idleTimeout/keepaliveTime/leakDetectionThreshold/validationTimeout/initializationFailTimeout/minimumIdle 기본값·제약·권고 (D1~D7, HIKARI-CFG-C1~C8)
raw/official-docs/persistence-hikaricp-pool-sizing-wiki small-pool axiom + sizing formula + pool-locking 공식 + MBean (D1, HIKARI-POOL-C1~C5)
raw/official-docs/hibernate-slow-query-log-official Hibernate SQL_SLOW 가 materialized SQL(파라미터 치환)을 출력 → prod 금지 근거 (D8, #C1/#C4)
raw/official-docs/datasource-proxy-slow-query-official datasource-proxy logSlowQueryBySlf4j + ParameterTransformer 마스킹 (D8, #C1/#C2)
raw/company-tech-blogs/slow-query-datasource-proxy-spring-boot-galovics datasource-proxy 기본 출력에서 파라미터 노출 실증 (D8, #C1)
raw/official-docs/p6spy-configuration-official P6Spy effective SQL 기본 파라미터 노출 + 빌트인 마스킹 부재 → 채택 제외 근거 (D8, #C2/#C3/#C4)
raw/official-docs/postgresql-slow-query-log-official DB-side log_min_duration_statement + extended-protocol 파라미터 포함 + 공식 보안 경고 (D8, #C1/#C2/#C4)
raw/company-tech-blogs/postgresql-slow-query-logging-crunchydata PostgreSQL slow query 로그 production 운영 패턴·비용 (D8, #C1)
raw/official-docs/datasource-micrometer-observation-official Micrometer JDBC observation 기본 파라미터 미포함(opt-in) (D8, #C2)

TODO

  • D1~D8 결정 확정 후 application.yml HikariCP block 확장 — 등급: actually-implemented (2026-06-09)
  • validationTimeout < connectionTimeout 제약 위반(현 5000ms = 5s) 정합 — 등급: actually-implemented (validation-timeout: 3000 literal, HikariPoolConstraintValidator 강제)
  • greenfield knob 신규 env key 제안서 → feature-env-driven-runtime-configuration 로 이관 (APP_DATASOURCE_LEAK_DETECTION_THRESHOLD, _KEEPALIVE_TIME, _VALIDATION_TIMEOUT, _INIT_FAIL_TIMEOUT, _SLOW_QUERY_THRESHOLD_MS) — 등급: planned
  • slow query 탐지: datasource-proxy + ParameterTransformer 가 slow query 로그에도 마스킹 적용되는지 로컬 검증 — 등급: needs-confirmation
  • connectionTimeout env 값 포맷 drift(5s duration vs ms) 정합 권고 — 등급: needs-confirmation (HikariPoolConstraintValidator 가 방어 파싱으로 crash 방지 — actually-implemented)

진행 중 메모

  • ground truth: application.ymlspring.datasource.hikari.* 5 knob 만 env binding(app-bootstrap/src/main/resources/application.yml L25-35). leak/keepalive/validation/init knob 부재. test yml 은 literal(connection-timeout: 30000).
  • adapter-persistence 에 별도 DataSource/@Configuration 클래스 없음 — 전적으로 Spring Boot auto-config + env binding. 본 브랜치 결정은 설정값 + (필요 시) 하나의 검증 컴포넌트 수준이지 datasource bean 재작성이 아님.

결정 사항

  • 2026-06-09: 고정 크기 풀 권고를 정책으로 채택하되 현 min-idle=2 와의 정합은 env-driven 으로 위임 / 이유: HikariCP 공식이 spike 응답성·성능 위해 minimumIdle 미설정(=fixed) 권고 / 대안: 탄력적 풀(min<max) — idle eviction 비용 + cold-connection 지연 / 근거: [[raw/official-docs/persistence-hikaricp-configuration-knobs]]#HIKARI-CFG-C8
  • 2026-06-09: slow query 는 앱 baseline = datasource-proxy + ParameterTransformer, prod 보강 = DB-side, dev = Hibernate SQL_SLOW 허용 / Hibernate SQL_SLOW prod 금지, P6Spy 제외 / 이유: "SQL/param 로그 금지" 하드 룰 하에서 앱 레이어 명시적 마스킹 제어 가능한 유일 방식 / 대안: Hibernate SQL_SLOW(파라미터 materialized 노출), P6Spy(마스킹 API 부재), DB-side(DBA 의존) / 근거: 아래 D8 Supporting Claims
  • (나머지 D2~D7 — Decision Evidence Map 참조)

결정-근거 매핑

Supporting Claimsraw/<category>/<slug>.md#C1 형식. 선택 조건 = 언제 이 결정 / 언제 대안.

Decision ID Decision 선택 조건 (언제 이 결정 / 언제 대안) Supporting Claims Evidence Strength Open Risk
D1 Pool sizing 정책: 고정 크기 풀(minimumIdle = maximumPoolSize) 을 권고 baseline 으로 고정. maximumPoolSize default(=10) 는 small-pool axiom + PostgreSQL formula 의 starting point 로 정당화하고, 부하 테스트로 조정. pool 하한은 application-port D12 REQUIRES_NEW 공식(maxPoolSize ≥ concurrent_threads × (1 + max_inNew_depth) + 1) 을 만족해야 함 일반 use case → fixed-size; spike/탄력 수요 명시 분석 있을 때만 min<max 탄력 풀 raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C8, raw/official-docs/persistence-hikaricp-pool-sizing-wiki.md#HIKARI-POOL-C1, #HIKARI-POOL-C2, #HIKARI-POOL-C4 + cross-branch: application-port D12 official-reference (HikariCP wiki) + cross-branch-delegation 현 registry min-idle=2(탄력) 가 fixed 권고와 불일치 → §Audit MIN_IDLE_POLICY_DRIFT. 값 변경은 env-driven 소유라 본 브랜치는 정책 권고
D2 connectionTimeout fail-fast pin: 30s 기본에 의존하지 않고 명시 pin(현 5s). 풀 고갈 시 30s 동안 스레드 점유 대신 빠르게 503 으로 실패시키는 정책. 최솟값 250ms 준수 동기 HTTP 요청 경로 → 짧은 fail-fast(수 초); 배치/장시간 작업 전용 풀이면 별도 더 긴 값 허용 raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C1 official-reference 정확한 값(5s)이 SLA 에 맞는지는 미증명 — env 값 owner=env-driven. acquire-timeout 실패 분류 는 persistence-failure(DB_UNAVAILABLE)
D3 maxLifetime < DB/인프라 idle limit: production 최우선 설정. DB(wait_timeout)·proxy(PgBouncer)·방화벽이 강제하는 커넥션 수명보다 수 초 짧게. 현 30분 default 는 실제 DB limit 확인 후 정합 항상 적용 (모든 환경). DB limit 미확인 시 30분 default 잠정 유지 + needs-confirmation raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C2 official-reference (공식 strong recommend) "수 초" 의 정확한 마진을 HikariCP 가 수치 미지정 → DB별 wait_timeout 확인 필요(§Claims)
D4 keepaliveTime 채택(greenfield): 유휴 커넥션이 DB/방화벽에 의해 끊기는 것 방지하는 ping 활성화. < maxLifetime 제약. default 120000ms(2분) 커넥션이 NAT/방화벽/클라우드 LB 뒤 → 활성화; 동일 호스트 로컬 DB 만이면 생략 가능 raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C4 official-reference DB/방화벽 실제 idle timeout 미확인 시 keepalive 주기 산정 불가(§Claims). 신규 env key 필요 → env-driven 위임
D5 leakDetectionThreshold 채택(greenfield): 커넥션 누수 조기 경고 활성화. 활성화 최솟값 2000ms 이상으로 설정. runbook "pool 고갈 시 leak detection 활성화" 의 상시 backing 정상 트랜잭션 최대 지속시간보다 충분히 큰 값으로 설정 가능할 때 활성화; long-running 배치 풀은 false positive 위험으로 비활성/별도 풀 raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C5 + cross-branch: persistence-failure runbook dependency-unavailable.md official-reference + internal-runbook "프로덕션 적정 임계값" 은 공식 미정의 — long-running tx false positive(§Claims). 신규 env key → env-driven
D6 initializationFailTimeout fail-fast: 풀 초기화 시 DB 미가용이면 startup 실패(default 1=fail-fast 유지). runtime-health startup validation + project-note §9 "잘못된 env 값 startup fail-fast" 정합 일반 서비스 → fail-fast(양수 default 유지); DB 가 앱보다 늦게 뜨는 보장 없는 컨테이너 오케스트레이션은 음수값 신중 검토(out-of-scope 위임) raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C7 + cross-branch: runtime-health-lifecycle startup validation official-reference + cross-branch-delegation 컨테이너 起動 순서(DB before app) 미보장 환경의 음수값 안전성 미증명 → runtime-health 와 조율
D7 validationTimeout < connectionTimeout 강제: aliveness 검증 시간이 acquire 타임아웃을 넘지 않게. default 5000ms 는 connectionTimeout 5s(=5000ms) 와 동일 → 제약 위반 이므로 connectionTimeout 상향 또는 validationTimeout 하향 중 택1 connectionTimeout=5s 유지 시 → validationTimeout 명시 하향(예 3s); connectionTimeout 상향 결정 시 → default 유지 가능 raw/official-docs/persistence-hikaricp-configuration-knobs.md#HIKARI-CFG-C6, #HIKARI-CFG-C1 official-reference 현 설정 잠재 충돌 = §Audit VALIDATION_TIMEOUT_CONFLICT. 두 값 모두 env-driven 소유 — 본 브랜치 정책 권고
D8 slow query 탐지 메커니즘: 앱 baseline = datasource-proxy + ParameterTransformer(파라미터 [REDACTED] 마스킹), prod 보강 = DB-side log_min_duration_statement(앱 로그에 SQL 미도달), dev = Hibernate SQL_SLOW 허용. Hibernate SQL_SLOW prod 금지(materialized SQL 파라미터 노출), P6Spy 제외(마스킹 API 부재). 하드 룰 "SQL/param 로그 금지" 와 정합 APM 있으면 datasource-micrometer(기본 param opt-out)로 대체 가능; DBA 분리 운영이면 DB-side 우선; dev 빠른 확인엔 Hibernate SQL_SLOW raw/official-docs/datasource-proxy-slow-query-official.md#C1, #C2, raw/company-tech-blogs/slow-query-datasource-proxy-spring-boot-galovics.md#C1, raw/official-docs/hibernate-slow-query-log-official.md#C1, raw/official-docs/p6spy-configuration-official.md#C3, raw/official-docs/postgresql-slow-query-log-official.md#C2, #C4, raw/official-docs/datasource-micrometer-observation-official.md#C2 official-reference × 4 + company-case-study × 2 ParameterTransformer 가 slow query 리스너 출력에도 적용되는지 공식 미보장 → 로컬 검증 전 needs-confirmation(§Claims)

구현 가이드

본 브랜치 결정(D1~D8)에서 도출되는 in-scope 설정/컴포넌트 만. 값 자체(env key)는 env-driven 소유 → 여기서는 정책의 application.yml 표현결정이 강제하는 제약 만 명세.

1. HikariCP knob 설정 정책 (application.yml 표현)

Trace: D1(#HIKARI-CFG-C8) · D2(#HIKARI-CFG-C1) · D3(#HIKARI-CFG-C2) · D4(#HIKARI-CFG-C4) · D5(#HIKARI-CFG-C5) · D6(#HIKARI-CFG-C7) · D7(#HIKARI-CFG-C6). 현 SSOT = app-bootstrap/src/main/resources/application.yml L25-35 (spring.datasource.hikari.*, 5 knob). env key owner = feature-env-driven-runtime-configuration.

  • UNSUPPORTED_IMPL_DECISION: greenfield knob 의 신규 env key 이름(APP_DATASOURCE_LEAK_DETECTION_THRESHOLD / _KEEPALIVE_TIME / _VALIDATION_TIMEOUT / _INIT_FAIL_TIMEOUT)은 cited raw 가 권고하지 않음 — 기존 APP_DATASOURCE_* 명명 컨벤션 차용한 임의 제안. trade-off: 컨벤션 일관성 vs env-driven 이 최종 명명 소유(이관 시 변경 가능).
  • UNSUPPORTED_IMPL_DECISION (maxLifetime 마진, D3): #HIKARI-CFG-C2 는 "several seconds shorter" 만 권고하고 정확한 마진 초수 미지정. DB wait_timeout 확인 전 임시 보수값으로 마진 60s (max-lifetime = DB_idle_limit 60s) 제안. trade-off: 큰 마진=죽은 커넥션 위험 ↓ / 커넥션 회전 ↑, 작은 마진=경계 race. DBA 확인 + 부하테스트로 조정.
  • UNSUPPORTED_IMPL_DECISION (leak threshold 값, D5): #HIKARI-CFG-C5 는 최솟값(2000ms)만 정의, 프로덕션 적정값 미지정. ca-tmpl 정상 트랜잭션이 단건(배치 풀 부재) 전제 하에 임시 30000ms(30s) 제안 — 최장 트랜잭션 추정 ~5s 대비 충분한 여유로 false positive 회피. trade-off: 작을수록 누수 조기탐지 / long-tx 오탐 ↑. 실측 트랜잭션 분포로 조정.
knob (Spring property) 현 상태 본 브랜치 정책 제약 상태
maximum-pool-size env binding (default 10) small-pool + formula 근거 (D1). 값 변경은 env-driven ≥ application-port D12 하한 actually-implemented (binding)
minimum-idle env binding (default 2) fixed-size 권고: = maximum-pool-size (D1) 권고 위반 시 §Audit drift planned (정책 정합)
connection-timeout env binding (default 5s) fail-fast pin (D2) ≥ 250ms; 포맷 drift 정합 needs-confirmation (포맷)
max-lifetime env binding (default 30분) < DB wait_timeout 수 초 (D3) DB limit 확인 필요 planned
idle-timeout env binding (default 10분) fixed-size 면 무효(D1 시 N/A) min-idle < max 일 때만 적용 actually-implemented (binding)
keepalive-time 미설정 채택 (D4) < max-lifetime actually-implemented (literal 120000, HikariPoolConstraintValidator 강제)
leak-detection-threshold 미설정 채택 ≥ 2000ms (D5) ≥ 2000ms actually-implemented (literal 30000, HikariPoolConstraintValidator 강제)
validation-timeout 미설정 (default 5000ms) < connection-timeout 강제 (D7) < connectionTimeout actually-implemented (literal 3000, HikariPoolConstraintValidator 강제)
initialization-fail-timeout 미설정 (default 1) fail-fast 유지 (D6) runtime-health 조율 actually-implemented (literal 1)

2. Slow query 탐지 wiring (D8)

Trace: D8. baseline = datasource-proxy ProxyDataSourceBuilder.logSlowQueryBySlf4j(threshold, TimeUnit) (datasource-proxy#C1) + ParameterTransformer Bean 으로 전 파라미터 [REDACTED] 치환 (#C2). 하드 룰 "SQL/param 로그 금지" = persistence-failure In-scope 와 정합.

  • UNSUPPORTED_IMPL_DECISION: slow query 임계값(1000ms)로그 레벨(WARN) 은 cited raw 가 권고하지 않는 운영 SLO — 임의 채택. trade-off: 1s=일반적 사용자 체감 경계 vs 워크로드별 상이(부하 테스트로 조정). 신규 env key APP_DATASOURCE_SLOW_QUERY_THRESHOLD_MS 제안.
  • UNSUPPORTED_IMPL_DECISION: 라이브러리 선택(datasource-proxy vs spring-boot-data-source-decorator 경유)은 cited raw 가 둘 다 제시 — Spring Boot 3.x 통합 검증된 spring-boot-data-source-decorator 경유를 임의 채택. trade-off: 자동 wiring vs 의존성 2개. application.yml property = decorator.datasource.datasource-proxy.slow-query.threshold (초 단위 — ms env key 와 단위 변환 필요), .slow-query.log-level=warn. ParameterTransformer 는 @Bean 등록(빌트인 마스킹 부재).
항목 명세 근거 상태
baseline 메커니즘 datasource-proxy SlowQueryListener + ParameterTransformer datasource-proxy#C1/#C2 planned
파라미터 마스킹 전 파라미터 [REDACTED] 치환 Bean datasource-proxy#C2 needs-confirmation (slow 리스너 적용 검증)
prod 보강 DB-side log_min_duration_statement (DBA 소유) postgresql-slow-query#C1 documented-only
dev 허용 Hibernate LOG_QUERIES_SLOWER_THAN_MS (prod 금지) hibernate-slow-query#C4/#C1 documented-only
제외 P6Spy (마스킹 API 부재, format 우회 실수 위험) p6spy#C3/#C4 rejected

엣지·실패·의존

  • 실패·엣지 경로:
    • Pool acquire timeout: connectionTimeout(5s) 내 커넥션 미확보 → DB_UNAVAILABLE(503, retryable) 분류는 persistence-failure 소유. 본 브랜치는 timeout 값/정책 만(D2).
    • validationTimeout ≥ connectionTimeout 충돌: 현 default 5000ms = connectionTimeout 5s → HikariCP 제약 위반(#HIKARI-CFG-C6). 起動 시 reset/경고 가능 → D7 로 정합 필수.
    • maxLifetime ≥ DB wait_timeout: DB 가 먼저 끊은 죽은 커넥션을 풀이 반환 → 첫 쿼리 실패. keepalive(D4) + maxLifetime(D3) 둘 다로 방어. DB limit 미확인이 핵심 미지수.
    • leak false positive: long-running 트랜잭션(배치)이 leakDetectionThreshold 초과 → 오탐 로그. D5 선택 조건으로 분리.
    • slow query 파라미터 누수: 마스킹 미적용 시 PII 노출 → 하드 룰 위반. ParameterTransformer 가 slow 리스너에 적용되는지 미검증(§Claims).
    • startup DB 미가용: initializationFailTimeout 양수 → 起動 실패(fail-fast, 의도). 컨테이너 기동 순서 미보장 시 crash loop 가능 → runtime-health 와 조율.
  • 다른 계약 의존:

검증해야 할 주장

Claim Why uncertain How to verify Status
DB(wait_timeout)/PgBouncer/방화벽의 실제 idle timeout 값 maxLifetime(D3)·keepalive(D4) 산정의 입력인데 환경마다 다름 대상 DB SHOW wait_timeout / 인프라 설정 확인 후 maxLifetime = limit 수 초 needs-confirmation
datasource-proxy ParameterTransformer 가 slow query 로그 출력에도 마스킹 적용 공식 문서가 slow 리스너 적용을 명시 보장 안 함 (datasource-proxy#C2) PII 포함 파라미터로 1s+ 쿼리 유발 후 로그에 [REDACTED] 확인 needs-confirmation
connectionTimeout env 값 포맷 5s(duration) 가 Spring Boot HikariCP 바인딩에서 정상 동작 registry default 5s vs application.yml 주석 "milliseconds" vs test literal 30000 불일치 起動 후 HikariConfig.connectionTimeout 실측 / 잘못된 포맷이면 정합 needs-confirmation
validationTimeout < connectionTimeout 제약 위반 시 HikariCP 실제 동작(경고/reset) 현 default 동일값(5000ms) — 위반 결과 미확인 (#HIKARI-CFG-C6) 두 값 동일 설정 起動 로그 확인 → D7 값으로 정합 planned
fixed-size(min-idle=max) 전환이 현 min-idle=2 대비 spike 응답성 개선 공식 권고지만 ca-tmpl 워크로드 미측정 (#HIKARI-CFG-C8) 부하 테스트로 pool pending/acquire p99 비교 planned
Hibernate SQL_SLOW 가 사용 JDBC 드라이버(Postgres/MySQL)에서 파라미터 materialized 노출 드라이버 PreparedStatement.toString() 구현 의존 (hibernate-slow-query#C1) dev 에서 파라미터 포함 쿼리 로그 확인 → prod 금지 근거 확정 needs-confirmation

Audit & Findings

ground-truth(/home/donghyeon/workspace/ca-tmpl) 대조에서 발견한 drift/정합 항목. 사용자 작성 결정 영역(env 값)은 자동 rewrite 하지 않고 정합 권고 만.

  • MIN_IDLE_POLICY_DRIFT (Should-fix): registry APP_DATASOURCE_POOL_MIN_IDLE=2 (탄력 풀) vs HikariCP fixed-size 권고(#HIKARI-CFG-C8). D1 정책과 불일치 → env-driven 으로 정합 권고(값 owner=env-driven).
  • VALIDATION_TIMEOUT_CONFLICT (Should-fix): validationTimeout default 5000ms = connectionTimeout 5s → validationTimeout < connectionTimeout 제약 위반(#HIKARI-CFG-C6). D7 로 정합.
  • CONNECTION_TIMEOUT_FORMAT_DRIFT (needs-confirmation): env-keys.yaml default 5s(duration) vs application.yml 주석 "milliseconds" vs application-test.yml literal 30000. Spring Boot 바인딩 실 동작 확인 필요(§Claims). owner=env-driven.
  • greenfield knob 미등록 (OUT_OF_BRANCH_SCOPE → env-driven): leakDetectionThreshold/keepaliveTime/validationTimeout/initializationFailTimeout 는 registry·코드 모두 부재. 본 브랜치가 채택 결정(D4~D7) → 신규 env key 등록은 env-driven 으로 이관.
  • slow query 관심사 무주공산 확인: 어느 sibling 도 slow query 탐지 미소유(persistence-failure 는 SQL/param 로그 금지 라는 반대 정책만). D8 로 본 브랜치가 covered-here.

관심사 커버리지 (coverage-auditor 자동 생성 — 있을 때)

아래는 /coverage 실행 전 사전 매핑. coverage-auditor 가 governing doc 대조로 재생성한다.

관심사 상태 owner 심각도 근거
Pool sizing 정책 (formula/fixed-size) covered-here D1
connectionTimeout 정책 covered-here D2
maxLifetime < DB limit covered-here D3
keepaliveTime covered-here D4
leakDetectionThreshold covered-here D5
initializationFailTimeout (startup fail-fast) covered-here D6
validationTimeout 제약 covered-here D7
slow query 탐지 (param-safe) covered-here D8
DB pool env key 등록·검증 delegated raw/branch-notes/feature-env-driven-runtime-configuration OK Out of scope + §Audit
Pool exhaustion alert threshold delegated raw/branch-notes/feature-persistence-failure-baseline OK D3(persistence) §Parent
Pool metric 이름 delegated raw/branch-notes/feature-persistence-failure-baseline / raw/branch-notes/feature-metrics-alerting-contract OK §Parent
Pool-acquire-timeout 실패 분류 delegated raw/branch-notes/feature-persistence-failure-baseline OK §엣지
Pool-sizing 하한 공식 (REQUIRES_NEW) delegated raw/branch-notes/feature-application-port-usecase-contract OK D1 cross-branch

구현 완료 항목 (2026-06-09)

파일 변경

파일 상태 내용
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/HikariPoolConstraintValidator.java added SmartInitializingSingleton; D2/D4/D5/D7 inter-knob constraint 시작 guard; parseMillis 방어 파싱 (CONNECTION_TIMEOUT_FORMAT_DRIFT 대응)
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RuntimeSafetyConfig.java modified hikariPoolConstraintValidator @Bean 추가
src/app-bootstrap/src/main/resources/application.yml modified existing 5 knob 에 D1~D3 decision comment 추가; greenfield 4 knob literal 추가 (keepalive-time/leak-detection-threshold/validation-timeout/initialization-fail-timeout); D8 slow-query DOCUMENTATION comment block 추가
src/app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/HikariPoolConstraintValidatorTest.java added ApplicationContextRunner 기반 10개 케이스 (TDD — 실패 후 구현). boundary(connection-timeout=250 통과) + keepalive==max-lifetime 위반 케이스 포함
src/app-bootstrap/src/test/resources/application-test.yml modified greenfield 4 knob literal 추가 (test context parity)

리뷰 체인 (ca-tmpl SDD)

  • ca-architect-sentinel → PASS: validator 는 business rule 아님(HikariCP 자체 invariant guard), app-bootstrap 한정, 의존성 그래프 불변
  • ca-spec-reviewer → PASS: 36/36 요구사항 MET, extra 없음, 음성 제약(.env/env-keys/build.gradle 무변경) 충족
  • ca-quality-reviewer → NEEDS_FIX 2 Important + 3 Minor → 모두 수정 반영:
    • 위반 메시지가 operator-facing env key 명명 (APP_DATASOURCE_CONNECTION_TIMEOUT/APP_DATASOURCE_POOL_MAX_LIFETIME; greenfield 3종은 "env key pending feature-env-driven-runtime-configuration"). sibling RuntimeNumericBoundsValidator/OpenInViewSafetyValidator 계약 일치
    • 테스트가 APP_DATASOURCE_CONNECTION_TIMEOUT 문자열 핀 추가(계약 회귀 방지)
    • keepalive 테스트 메서드명 정정 + equal-case 추가, connection-timeout=250 boundary 통과 케이스 추가, application-test.yml D6 ✓ 주석 보강

검증 결과

  • ./gradlew :app-bootstrap:test --tests 'dev.caskeleton.bootstrap.runtime.HikariPoolConstraintValidatorTest' → BUILD SUCCESSFUL (10 tests, 0 fail) — test-results XML 로 실측 확인
  • ./gradlew :app-bootstrap:test --tests '*CleanArchitectureTest' → BUILD SUCCESSFUL
  • ./gradlew :app-bootstrap:test → BUILD SUCCESSFUL (전체 모듈 회귀 없음)
  • ./gradlew verifyCleanArchitectureDependencies → BUILD SUCCESSFUL

결정 이행 상태 업데이트

Decision 이전 상태 현재 상태
D1 (pool sizing 정책 comment) planned actually-implemented
D2 (connection-timeout comment + >= 250 강제) needs-confirmation actually-implemented
D3 (max-lifetime comment) planned actually-implemented
D4 (keepalive-time literal) planned (greenfield) actually-implemented (literal 120000)
D5 (leak-detection-threshold literal) planned (greenfield) actually-implemented (literal 30000)
D6 (initialization-fail-timeout literal) planned (greenfield) actually-implemented (literal 1)
D7 (validation-timeout literal + constraint 강제) planned (greenfield) actually-implemented (literal 3000, HikariPoolConstraintValidator)
D8 (slow-query DOCUMENTATION) documented-only documented-only (policy comment in application.yml, no code)

미이행 (타 브랜치 위임)

  • greenfield knob 신규 env key 등록 (APP_DATASOURCE_KEEPALIVE_TIME 등) → feature-env-driven-runtime-configuration
  • datasource-proxy + ParameterTransformer slow-query 마스킹 검증 (D8 TODO #3)
  • DB wait_timeout 확인 후 max-lifetime / keepalive-time 조정

마주친 문제

  • (없음 — scaffold 단계)

묶음 (이 branch에서 파생된 자료)

이 branch는 단일 노트가 아니라 작업 묶음의 entry point.

근거 자료

Sub-branches (세부 작업)

  • (없음)

오류 기록 (이 branch 작업 중 발생)

  • (없음)

면접 준비 (이 작업에서 나올 수 있는 면접 질문)

  • (생성 시 연결)

강의 (이 작업을 위해 학습한 강의)

  • (없음)

job-posting tie-ins (이 작업에서 파생된 글감)

관련 일일 노트

  • (작업 시 연결)

완료 후 정리

머지/종료 시점에 채움. /ingest가 이 섹션을 기준으로 wiki/projects/에 추출.

  • PR 링크:
  • 리뷰 메모:
  • 머지 결과 / 배포 환경:
  • wiki 추출 대상 (verified만, wiki/projects/로만 추출):
    • actually-implemented 항목:
    • locally-verified 항목:
    • prod-verified 항목:
  • 추출하지 않을 항목 (planned / documented-only / abandoned):