Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/transaction-deadline-and-pool/concept/concept-requires-new-connection-cost.md
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

107 lines
4.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
kind: CONCEPT
slug: requires-new-connection-cost
title: REQUIRES_NEW의 커넥션 비용과 풀 사이징 제약
topic: transaction-deadline-and-pool
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: concept:requires-new-connection-cost
evidenceCapturedOn: 2026-09-01
assets:
- key: requires-new-connection-cost
file: ../../../final/evidence/rendered/requires-new-connection-cost.svg
evidence:
- ../../../final/evidence/raw/requires-new-connection-cost.txt
source:
- 원본 분석 절은 final/document.md#3-2 · analysis/05 §3.1, §13.2 이다.
---
# REQUIRES_NEW의 커넥션 비용과 풀 사이징 제약
새 트랜잭션은 새 커넥션을 요구하고 바깥 커넥션은 반납되지 않는다. 그래서 풀 크기 하한이 동시 스레드 수와 중첩 깊이의 곱에 묶인다.
## 관계
- **REQUIRES_NEW가 바깥 커넥션을 핀한 채 새 커넥션을 딴다**
이 제약이 실제로 나타나는 사례다.
- **호출 예산에서 DB 로컬 타임아웃까지의 데드라인 전파**
풀 획득이 예산의 일부라는 점에서 연결된다.
- **풀 계약 레인이 실행되지 않아 포화 동작이 확인되지 않았다**
이 제약의 검증에 대한 미해결 질문이다.
## 본문
<!-- body:start -->
`REQUIRES_NEW`는 바깥 트랜잭션의 커넥션을 **핀한 채로** 새 물리 JDBC 커넥션을 딴다. 그래서 풀 사이징 제약이 곱셈이 된다 — `maximumPoolSize >= (concurrent_threads × (1 + max_inNew_depth)) + 1`.
## 커넥션 비용이 곱셈이 되는 이유
:::evidence key="requires-new-connection-cost" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
:::
## 레코드마다 inNew 를 도는 루프가 금지인 이유
풀 고갈과 데드락이다.
## 같은 곱셈 함정이 database-per-tenant에서 반복된다
각 tenant 풀은 개별적으로 합리적이고 그 합이 아니다 — 50 tenant × 10 = 서버 `max_connections` 100에 500 커넥션. 실패는 idle이던 것 포함 모든 tenant에 동시에 도착한다.
:::note
풀 계약 레인 미실행
:::
## 일시 중단은 커넥션을 놓지 않는다
새 트랜잭션 모드는 바깥 트랜잭션을 일시 중단한다. 그 일시 중단은 트랜잭션 경계에 대한 것이다.
바깥 커넥션은 유지된다. 나중에 재개해야 하기 때문이다.
## 그래서 하한이 생긴다
설정 파일이 그 제약을 수식으로 적는다.
```text
maxPoolSize >= concurrent_threads * (1 + max_inNew_depth) + 1
```
중첩 깊이 1 이면 스레드당 두 커넥션이다. 동시 스레드가 100 이면 최소 201 이 필요하다.
## 지켜지지 않으면 데드락이다
모든 스레드가 바깥 커넥션을 잡고 안쪽 커넥션을 기다린다. 아무도 반납하지 않으므로 전부 풀 획득 타임아웃까지 대기한다.
이 상태는 부하가 임계를 넘는 순간 한꺼번에 나타난다. 그 전까지는 아무 증상이 없다.
## 풀 사이징의 다른 근거와 충돌한다
같은 주석 블록이 반대 방향의 근거도 적는다.
```text
D1 (feature-database-connection-pool-contract): small-pool axiom + PostgreSQL formula
starting point (maximumPoolSize = cores * 2 + effective_spindle_count, adjust via load
test). Fixed-size pool recommended (minimumIdle = maximumPoolSize).
```
작은 풀이 낫다는 공리와 코어 수 기반 시작점이다. 그 값은 대개 동시 스레드 수보다 훨씬 작다.
:::warning
두 근거가 같은 손잡이를 반대 방향으로 민다. 작은 풀 공리는 값을 줄이라 하고 중첩 하한은 늘리라 한다. 해소하는 방법은 풀을 키우는 것이 아니라 중첩 깊이를 줄이는 것이다.
:::
## 고정 크기 풀
최소 유휴를 최대 크기와 같게 두는 것을 권장한다. 풀이 줄었다가 늘어나는 동안 중첩 하한이 일시적으로 깨지는 것을 막는다.
## 멀티테넌시에서의 확장
테넌트별 풀 예산 타입이 이 제약을 테넌트 단위로 다시 적용한다. 테넌트 하나가 풀 전체를 소진하는 것을 막으면서도 각 테넌트의 중첩 하한을 만족해야 한다.
<!-- body:end -->