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>
5.1 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | assets | evidence | source | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CONCEPT | deadline-propagation | 호출 예산에서 DB 로컬 타임아웃까지의 데드라인 전파 | transaction-deadline-and-pool | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | concept:deadline-propagation | 2026-09-01 |
|
|
|
호출 예산에서 DB 로컬 타임아웃까지의 데드라인 전파
호출자가 가진 시간 예산이 트랜잭션 타임아웃으로, 다시 데이터베이스의 로컬 타임아웃으로 좁혀진다. 각 단계가 앞 단계보다 작아야 상위 호출자가 포기한 뒤에도 하위가 계속 도는 상황이 생기지 않는다.
관계
- 데드라인은 호출 예산에서 시작해 세 단계로 좁힌다 이 개념을 규칙으로 옮긴 것이다.
- 쓰기 트랜잭션에는 유한 타임아웃이 필수다 이 전파의 마지막 단계가 없을 때의 문제를 다룬 규칙이다.
- 세션 스코프 설정은 풀로 돌아간 커넥션에 남는다 로컬 타임아웃을 설정할 때의 함정이다.
본문
호출자의 남은 예산이 세 단계로 좁혀져 DB 세션 설정에 도달하는 구조의 설명이다.
마감이 좁혀지는 세 단계
:::evidence key="deadline-propagation-diagram" alt="호출 예산과 트랜잭션 마감과 DB 로컬 타임아웃이 위에서 아래로 쌓여 있고 오른쪽에 좁아지는 방향 화살표가 있다" caption="마감이 좁혀지는 세 단계" zoom="false" :::
획득 전에 요구하는 것
connectionTimeout + beginBudget + minimumActionWindow + completionMargin을 요구하고, Spring의 초 단위 타임아웃이 1초 미만이면 시작하지 않는다. begin 이후에는 statement/lock/idle 셋을 각각 유도하고 하나라도 1ms 미만이면 거부한다.
예산이 좁혀지는 경로
:::evidence key="deadline-propagation" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true" :::
SET 이 아니라 set_config 인 이유
둘이다 — SET은 파라미터 바인딩 전에 파싱되어 syntax error가 나고, 함수 호출은 값이 statement text에서 빠진다. 세 번째 인자 true가 transaction-local을 뜻한다.
H2가 두 가지에서 다르다
세션 스코프이고 idle 가드가 없다. 그것이 H2의 성질이지 선택이 아니라는 점도 함께 적혀 있다.
:::note
실제 세션에서 SHOW statement_timeout으로 적용을 확인하지 않았다
:::
왜 전파해야 하는가
상위 호출자가 30 초 예산을 갖고 있는데 데이터베이스 쿼리에 타임아웃이 없으면, 호출자가 포기한 뒤에도 쿼리는 계속 돈다. 그 커넥션은 반납되지 않고 풀에서 빠져 있다.
부하가 걸리면 그 상태가 누적된다. 아무도 기다리지 않는 작업이 풀을 점유한다.
계산이 자기 타입을 갖는다
데드라인 계산기가 별도 타입이다. 획득 봉투를 포함하는 형태와 포함하지 않는 형태를 나눠 갖는다.
획득 봉투는 커넥션을 얻는 데 드는 시간이다. 그것을 예산에서 빼지 않으면, 커넥션을 기다리다가 남은 시간이 없는 채로 쿼리를 시작하게 된다.
데이터베이스마다 다른 설정기
로컬 타임아웃을 실제로 거는 방법은 데이터베이스마다 다르다. PostgreSQL 용 설정기와 H2 용 설정기가 따로 있다.
이 분리가 필요한 이유는 두 가지다. 설정 문법이 다르고, 세션 스코프 설정이 커넥션에 남는 방식도 다르다.
세 단계
호출 예산 상위 호출자가 기다릴 수 있는 시간
↓ 획득 봉투를 뺀다
트랜잭션 타임아웃 스프링 트랜잭션 템플릿에 설정
↓ 여유를 남긴다
DB 로컬 타임아웃 데이터베이스가 스스로 끊는 시간
각 단계가 앞 단계보다 작다. 마지막이 가장 작아야 데이터베이스가 먼저 끊고, 그래야 애플리케이션이 그 실패를 분류할 기회를 갖는다.
:::note
순서가 반대가 되면 애플리케이션이 먼저 타임아웃되고 데이터베이스는 계속 돈다. 그 쿼리는 아무도 결과를 받지 않은 채 자원을 쓴다.
:::
템플릿을 미리 만드는 것과의 관계
트랜잭션 템플릿은 모드마다 미리 만들어져 있고 전부 같은 격리 수준에 고정되어 있다. 템플릿을 호출마다 고쳐 쓰면 경합이 생기기 때문이다.
그래서 데드라인은 템플릿의 필드가 아니라 실행 시점에 계산되어 전달된다.