Files
llm-wiki/raw/blog-topics/hikaricp-inter-knob-constraints-startup-guard-2026-06-09.md

102 lines
4.9 KiB
Markdown

---
title: "HikariCP knob 간 제약을 Spring Boot 시작 guard 로 강제하는 패턴"
source_type: blog-topic
status: raw
related_branches: [feature-database-connection-pool-contract]
related_projects: [ca-tmpl]
tags: [blog-topic, ca-tmpl, hikaricp, spring-boot, startup-validation, connection-pool, clean-architecture]
created: 2026-06-09
status_label: ready-for-canonical
target_audience: backend-engineer
inspiration_url:
archive_url:
---
# HikariCP inter-knob constraints as a Spring Boot startup guard
## Parent
- [[raw/branch-notes/feature-database-connection-pool-contract]]
## 트리거 / Trigger
- 트리거 유형: `branch-work`
- 트리거 날짜: 2026-06-09
- 트리거 연결 노트: [[raw/branch-notes/feature-database-connection-pool-contract]]
## 글감 / Topic seed
- 한 문장 요지: HikariCP knob 간 제약을 runtime 경고에 맡기지 않고 Spring Boot startup guard로 수집해 fail-fast시키는 패턴이다.
- 예상 제목 후보:
- HikariCP 설정 오류를 startup에서 잡기
- Connection pool knob 제약을 Spring Boot guard로 고정하기
## 핵심 주장 후보 / Claim candidates
- 사실 후보:
- `validationTimeout < connectionTimeout`, `keepaliveTime < maxLifetime` 같은 inter-knob 제약이 있다.
- `SmartInitializingSingleton``ApplicationContextRunner`로 startup guard를 검증할 수 있다.
- 의견/해석 후보:
- pool 설정 오류는 traffic을 받기 전 startup phase에서 실패시키는 편이 운영적으로 더 명확하다.
## Outline seed
1. HikariCP knob 간 제약과 runtime 경고/reset의 한계를 정리한다.
2. String 기반 defensive parse로 Duration drift를 안전하게 처리한다.
3. `ApplicationContextRunner`로 guard failure를 작은 테스트로 고정한다.
## 핵심 아이디어
HikariCP 에는 knob 간 순서 제약이 있다:
- `validationTimeout < connectionTimeout`
- `keepaliveTime < maxLifetime`
- `connectionTimeout >= 250 ms`
- `leakDetectionThreshold >= 2000 ms` (0 = disabled 허용)
이 제약들은 HikariCP 내부에서 경고 또는 reset 으로만 처리되고, 설정 오류가 runtime 에서만 드러나는 경우가 많다. `SmartInitializingSingleton` + `Environment.getProperty(key)` (String, not typed) 패턴으로 context refresh 완료 직전에 모든 위반을 한꺼번에 수집해 `IllegalStateException` 으로 boot fail 시키면, 잘못된 pool 설정이 prod 에 배포되는 것을 막을 수 있다.
## 흥미로운 구현 포인트
### Defensive parseMillis (CONNECTION_TIMEOUT_FORMAT_DRIFT)
`environment.getProperty("spring.datasource.hikari.connection-timeout", Long.class)` 는 env-keys.yaml default 가 `"5s"` (Duration string) 일 때 `ConversionFailedException` 을 던진다. 대신 `getProperty(key)` 로 String 을 받아 `Long.parseLong(raw.trim())` + `NumberFormatException catch → return null` 패턴으로 방어 파싱하면:
1. 숫자 ms 값은 정상 검증
2. Duration string 은 null (absent 취급) — 크래시 없이 skip
3. 명세에서 두 포맷이 공존하는 drift 환경에서 안전
### ApplicationContextRunner 기반 단위 테스트
`@SpringBootTest` 없이 `ApplicationContextRunner.withUserConfiguration(ValidatorConfig.class)` 만으로 `SmartInitializingSingleton``afterSingletonsInstantiated()` 가 호출된다. `context.hasFailed()` + `context.getStartupFailure().hasStackTraceContaining(...)` 으로 각 위반 케이스를 격리 검증.
## 글감 방향
- Spring Boot startup contract 패턴 시리즈 (`SmartInitializingSingleton` vs `ApplicationListener<ContextRefreshedEvent>` vs `@PostConstruct`)
- HikariCP 운영에서 놓치기 쉬운 knob 간 제약 총정리
- "설정 오류를 runtime 이 아닌 startup 에서 잡는다" 원칙의 구현 패턴들
## Canonical 전환 후보 / Canonical extraction candidates
- `wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound.md` 후보:
- HikariCP inter-knob constraint startup guard를 data-layer/pool configuration 글감으로 연결.
- 필요한 추가 검증:
- 실제 validator class, `ApplicationContextRunner` 테스트, env duration drift 처리 범위.
## Sources / 근거 후보
- [[raw/branch-notes/feature-database-connection-pool-contract]]
## 미해결 / Unknown
- 아직 확인해야 할 사실: 현재 ca-tmpl 코드에 startup guard와 관련 테스트가 존재하는지.
- 과장하면 안 되는 부분: HikariCP 자체가 모든 오류를 방치한다고 쓰지 않고, ca-tmpl에서 선택한 fail-fast 보강으로 제한한다.
## Related / 관련
- 관련 branch: [[raw/branch-notes/feature-database-connection-pool-contract]]
## Decision / 처리 결정
- 액션: `promote-to-canonical`
- 이유: `wiki/projects/ca-tmpl/data-layer-persistence-cache-outbound.md` 에 HikariCP startup guard 글감으로 반영했다.
- 다음 단계: target canonical이 아직 `draft` 이므로 `blogify` 전 pool guard 구현·검증 여부를 branch-note/code 기준으로 확인한다.