36 KiB
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 |
|
|
|
2026-06-09 | in-progress | BR-CA-SKELETON-OPERATIONAL-CONTRACT-050 | project-work-item | ca-skeleton-operational-contract | WI-CA-SKELETON-OPERATIONAL-CONTRACT-050 |
|
|
1 | 8bb34c64971d280776b949d71b980bec1b4203786e4043b7a22ca9f6174104ec |
branch: feature-database-connection-pool-contract
Layer:
raw/branch-notes/— 단일 브랜치의 TODO·결정·진행 기록. 머지/종료 후 verified 결과는/ingest로wiki/projects/에 추출. 원본은 raw에 영구 보관.status_label:in-progress|review|merged|abandoned
부모 (필수)
- Parent project (canonical SSOT): raw/project-notes/ca-skeleton-operational-contract
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 관심사 공동 소유):
- raw/branch-notes/feature-persistence-failure-baseline — persistence 실패 분류 + Hikari pool exhaustion alert (D3) + pool metric 노출 + acquire-timeout 실패 분류 owner
- raw/branch-notes/feature-env-driven-runtime-configuration —
APP_DATASOURCE_*env key owner (pool max/min-idle/connection-timeout/idle-timeout/max-lifetime + numeric bounds validation) - raw/branch-notes/feature-metrics-alerting-contract — DB pool metric 공동 소유 (
hikaricp.connections.*) - raw/branch-notes/feature-application-port-usecase-contract —
REQUIRES_NEWpool-sizing 제약 (D12) — 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_UNAVAILABLE503 retryable) →feature-persistence-failure-baseline소유. - SQLState classifier / OSIV off →
feature-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.ymlHikariCP 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(
5sduration vs ms) 정합 권고 — 등급:needs-confirmation(HikariPoolConstraintValidator 가 방어 파싱으로 crash 방지 — actually-implemented)
진행 중 메모
- ground truth:
application.yml의spring.datasource.hikari.*5 knob 만 env binding(app-bootstrap/src/main/resources/application.ymlL25-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 Claims는raw/<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.ymlL25-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" 만 권고하고 정확한 마진 초수 미지정. DBwait_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) +ParameterTransformerBean 으로 전 파라미터[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 와 조율.
- Pool acquire timeout: connectionTimeout(5s) 내 커넥션 미확보 →
- 다른 계약 의존:
- raw/branch-notes/feature-env-driven-runtime-configuration 의
APP_DATASOURCE_*env key (pool/timeout) 에 의존 — 본 브랜치가 정책을 정하면 그 키의 default/validation 갱신·신규 키 등록을 그 브랜치가 수행. 계약 변경 시 본 정책 재검토. - raw/branch-notes/feature-persistence-failure-baseline 의 D3(Hikari alert) + acquire-timeout →
DB_UNAVAILABLE분류에 의존 — 본 브랜치의 timeout 값이 alert threshold 의미를 바꾸면 D3 재검토. - raw/branch-notes/feature-application-port-usecase-contract 의 D12(
REQUIRES_NEWpool 하한 공식) 에 의존 — maximumPoolSize 하한이 그 공식을 만족해야 함. - raw/branch-notes/feature-metrics-alerting-contract 의 pool metric(
hikaricp.connections.*) 에 의존 — leak/keepalive 효과 관측은 그 metric 으로.
- raw/branch-notes/feature-env-driven-runtime-configuration 의
검증해야 할 주장
| 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): registryAPP_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.yamldefault5s(duration) vsapplication.yml주석 "milliseconds" vsapplication-test.ymlliteral30000. 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 ✓ 주석 보강
- 위반 메시지가 operator-facing env key 명명 (
검증 결과
./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에서 파생된 자료)
- raw/company-tech-blogs/postgresql-slow-query-logging-crunchydata
- raw/company-tech-blogs/slow-query-datasource-proxy-spring-boot-galovics
- raw/official-docs/datasource-micrometer-observation-official
- raw/official-docs/datasource-proxy-slow-query-official
- raw/official-docs/hibernate-slow-query-log-official
- raw/official-docs/p6spy-configuration-official
- raw/official-docs/persistence-hikaricp-configuration-knobs
- raw/official-docs/postgresql-slow-query-log-official
이 branch는 단일 노트가 아니라 작업 묶음의 entry point.
근거 자료
- raw/official-docs/persistence-hikaricp-configuration-knobs — HikariCP 공식 README 설정 레퍼런스 (connectionTimeout·maxLifetime·idleTimeout·keepaliveTime·leakDetectionThreshold·validationTimeout·initializationFailTimeout·minimumIdle 기본값·권고 근거; Claims HIKARI-CFG-C1~C8)
- raw/official-docs/persistence-hikaricp-pool-sizing-wiki — HikariCP About Pool Sizing (small-pool axiom + formula + pool-locking; HIKARI-POOL-C1~C6)
- raw/official-docs/hibernate-slow-query-log-official — Hibernate
SQL_SLOW/LOG_QUERIES_SLOWER_THAN_MS파라미터 노출 동작 - raw/official-docs/datasource-proxy-slow-query-official — datasource-proxy slow query listener + ParameterTransformer 마스킹
- raw/company-tech-blogs/slow-query-datasource-proxy-spring-boot-galovics — datasource-proxy 기본 파라미터 노출 실증
- raw/official-docs/p6spy-configuration-official — P6Spy executionThreshold + 기본 파라미터 노출(채택 제외 근거)
- raw/official-docs/postgresql-slow-query-log-official — PostgreSQL
log_min_duration_statementDB-side 탐지 + 보안 경고 - raw/company-tech-blogs/postgresql-slow-query-logging-crunchydata — PostgreSQL slow query 로그 production 운영 패턴
- raw/official-docs/datasource-micrometer-observation-official — Micrometer JDBC observation (기본 파라미터 미포함)
Sub-branches (세부 작업)
- (없음)
오류 기록 (이 branch 작업 중 발생)
- (없음)
면접 준비 (이 작업에서 나올 수 있는 면접 질문)
- (생성 시 연결)
강의 (이 작업을 위해 학습한 강의)
- (없음)
job-posting tie-ins (이 작업에서 파생된 글감)
- raw/blog-topics/hikaricp-inter-knob-constraints-startup-guard-2026-06-09 — HikariCP knob 간 제약을 Spring Boot 시작 guard 로 강제하는 패턴 (SmartInitializingSingleton + defensive parseMillis)
관련 일일 노트
- (작업 시 연결)
완료 후 정리
머지/종료 시점에 채움.
/ingest가 이 섹션을 기준으로 wiki/projects/에 추출.
- PR 링크:
- 리뷰 메모:
- 머지 결과 / 배포 환경:
- wiki 추출 대상 (verified만,
wiki/projects/로만 추출):actually-implemented항목:locally-verified항목:prod-verified항목:
- 추출하지 않을 항목 (planned / documented-only / abandoned):