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>
138 lines
7.7 KiB
Markdown
138 lines
7.7 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: analysis-finding-a14-f006
|
|
title: 용량 보호 계층 전체(41 main files)가 자기 테스트 픽스처 안에서만 실행된다
|
|
topic: web-inbound-and-http-surface
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:analysis-finding-a14-f006
|
|
evidenceCapturedOn: 2026-09-01
|
|
body: case-analysis-finding-a14-f006.body.md
|
|
assets:
|
|
- key: analysis-finding-a14-f006
|
|
file: ../../../final/evidence/rendered/analysis-finding-a14-f006.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/analysis-finding-a14-f006.txt
|
|
source:
|
|
- 원본 분석 절은 analysis/14-adapter-inbound-web.md#L677 이다.
|
|
---
|
|
|
|
# 용량 보호 계층 전체(41 main files)가 자기 테스트 픽스처 안에서만 실행된다
|
|
|
|
바이트 상한과 마감과 부하 차단과 반응형 속도 제한을 구현한 마흔한 파일 중 프로덕션 컨텍스트에 등록되는 것이 없다. 이 플랫폼을 그대로 배포하면 요청 본문 상한도 응답 상한도 마감도 동시성 상한도 대기열 상한도 없다.
|
|
|
|
## 관계
|
|
|
|
- **멱등 실행 계층과 durable-operation 표면이 픽스처에서만 조립된다**
|
|
같은 형태의 반복이다.
|
|
- **플랫폼 요청 컨텍스트가 서블릿에는 생산자가 없고 리액티브에는 익명 액터로 고정되어 있다**
|
|
같은 리프의 다른 P1 이다.
|
|
- **레인은 게이트가 올바른가를 증명하고 플랫폼이 게이트를 설치하는가는 묻지 않는다**
|
|
이 사례가 그 규칙의 형태다.
|
|
|
|
## 문제
|
|
|
|
이 리프는 용량 보호 계층을 갖추고 있다.
|
|
|
|
바이트 경계와 마감, 부하 차단, 속도 제한, 예산 초과 문제 문서다.
|
|
|
|
각 능력이 프로덕션 배포에서 실제로 설치되는지 확인했다.
|
|
|
|
## 결론
|
|
|
|
대부분 설치되지 않는다.
|
|
|
|
바이트 경계와 마감을 구현한 일곱 타입이 등록되지 않는다.
|
|
|
|
부하 차단의 네 타입도 등록되지 않는다.
|
|
|
|
두 전송의 조절 필터도 등록되지 않는다.
|
|
|
|
속도 제한은 인터셉터 경로 하나만 있다. 그것은 등록되지만 기본이 비활성이다.
|
|
|
|
예산 초과 문제 문서 처리기는 기본이 꺼짐이고 의존 빈도 선언되지 않는다.
|
|
|
|
즉 이 플랫폼을 그대로 배포하면 요청 본문 크기 상한도, 응답 크기 상한도, 요청 마감도, 동시성 상한도, 대기열 상한도 없다.
|
|
|
|
표준 예산이 정의하고 예산 목록이 담고 있는 값들은 아무도 읽지 않는다.
|
|
|
|
실패 시나리오는 이렇다.
|
|
|
|
배포된 API 에 무제한 조각 본문이 도착한다.
|
|
|
|
예산 필터가 필터 사슬에 없으므로 유계 요청 래퍼가 스트림을 감싸지 않고 예산 계량기가 바이트를 세지 않는다.
|
|
|
|
컨테이너 기본값 외에 상한이 없다. 그 기본값은 다중 파트와 폼 인코딩에만 적용되고 임의 본문에는 적용되지 않는다.
|
|
|
|
같은 요청에 대해 동시성 상한도 없으므로 승인 제어기가 내기로 되어 있던 과부하 응답도 나오지 않는다.
|
|
|
|
왜 드러나지 않는가가 이 모듈의 핵심 형태다.
|
|
|
|
이 리프는 교차 스택 게이트를 갖고 있다. 세 런타임의 유선 계약을 비교하는 레인과 개별 런타임 계약 레인들이다.
|
|
|
|
그 레인들은 게이트가 올바른가를 증명한다. 플랫폼이 게이트를 설치하는가는 묻지 않는다.
|
|
|
|
픽스처 애플리케이션이 필요한 것을 손수 배선해 레인을 돌리기 때문이다.
|
|
|
|
판정은 P1 이다.
|
|
|
|
## 검증 환경
|
|
|
|
Spring Boot : 4.0.8
|
|
확인 방식 : 능력별 구현과 프로덕션 등록 대조
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
원문은 final/evidence/raw/176 계열에 있다.
|
|
|
|
1. 용량 보호 능력 목록을 만든다.
|
|
2. 각 능력의 구현 타입을 확인한다.
|
|
3. 각 타입이 프로덕션 컨텍스트에 등록되는지 확인한다.
|
|
4. 표준 예산 값을 읽는 코드를 검색한다.
|
|
5. 계약 레인의 픽스처가 무엇을 손수 배선하는지 확인한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
프로덕션 배포에서 실제로 설치되는 것과 설치되지 않는 것이 갈린다.
|
|
|
|
| 능력 | 구현 | 프로덕션 등록 |
|
|
|---|---|---|
|
|
| 요청/응답 바이트 바운드 · 데드라인 | `WebMvcBudgetFilter`(122) · `WebFluxBudgetFilter`(122) · `BoundedHttpServletRequest`(115) · `BoundedHttpServletResponse`(151) · `BoundedServerWebExchange`(79) · `WebBudgetMeter`(89) · `WebRequestBudget`(141) | **없음** |
|
|
| 부하 차단(제한된 동시성 + 제한된 큐 + 503) | `SemaphoreAdmissionController`(118) · `AdmissionProfile`(80) · `AdmissionDecision`(59) · `AdmissionPermit`(21) | **없음** |
|
|
| 429 속도 제한 (`WebRateLimiter` 경로) | `WebMvcThrottleFilter`(136) · `WebFluxThrottleFilter`(142) | **없음** |
|
|
| 429 속도 제한 (인터셉터 경로) | `RateLimitInterceptor` ← `RateLimitWebConfig` | 있음, 기본 비활성(`APP_RATE_LIMIT_ENABLED:false`) |
|
|
| 예산 초과 문제 문서 | `WebMvcBudgetExceptionHandler`(84) | 기본 꺼짐 + 의존 빈 미선언 |
|
|
|
|
## WebBudgetCatalog 참조 위치
|
|
|
|
:::evidence key="analysis-finding-a14-f006" alt="코드베이스에서 WebBudgetCatalog 를 검색한 출력 5줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="WebBudgetCatalog 코드베이스 검색 — 5줄 · exit 0" zoom="true"
|
|
:::
|
|
|
|
## 그대로 배포하면 상한이 하나도 없다
|
|
|
|
요청 본문 크기 상한도, 응답 크기 상한도, 요청 데드라인도, 동시성 상한도, 큐 상한도 없다. `WebRequestBudget.standard()`가 정의하고 `WebBudgetCatalog`가 담고 있는 값들은 아무도 읽지 않는다(§15.3). P1.
|
|
|
|
## 왜 드러나지 않는가
|
|
|
|
이 leaf는 크로스 스택 게이트를 갖고 있다. `webCrossStackParityTest`가 Tomcat·Jetty·Reactor Netty 세 런타임의 와이어 계약을 비교하고, `JettyWebBudgetIT` · `ReactiveWebBudgetIT` · `JettyWebThrottleIT` · `ReactiveWebThrottleIT`가 예산과 스로틀을 실제로 검증한다. 그런데 그 IT들이 띄우는 것은 `BudgetFixtureApplication` 계열이고, **그 픽스처들이 `FilterRegistrationBean`으로 필터를 손수 등록한다**. 레인은 "필터가 올바르게 동작하는가"를 세 런타임에서 증명하고, "플랫폼이 필터를 설치하는가"는 어디서도 묻지 않는다.
|
|
|
|
## 이 저장소가 이미 이름 붙인 형태다
|
|
|
|
`EndpointGuardCallSiteTest`(notification)의 javadoc이 그 문장을 갖고 있다 — "A green test on a control nothing invokes is the shape this repository keeps finding, and testing the helper again would not have caught it." 여기서는 그 형태가 41개 파일 규모로 반복된다.
|
|
|
|
## 권고
|
|
|
|
`WebMvcPlatformAutoConfiguration`·`WebFluxPlatformAutoConfiguration`이 이미 `WebBudgetCatalog`를 등록하므로 자리는 있다. 네 필터와 `SemaphoreAdmissionController`, `BudgetProblemMapper`를 같은 자동설정에서 `backend.web.budgets.enabled` 게이트 아래 등록하고, **픽스처가 아니라 자동설정이 세운 컨텍스트에서** 하나 이상의 IT를 돌린다.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
배포해서 큰 본문을 보내 상한 부재를 재현하지 않았다. 등록 부재상 그 결과가 나온다.
|
|
|
|
실패 시나리오 — 배포된 API에 무제한 청크 본문이 도착한다. WebMvcBudgetFilter가 필터 체인에 없으므로 BoundedHttpServletRequest가 스트림을 감싸지 않고, WebBudgetMeter가 바이트를 세지 않는다. 컨테이너 기본값(Tomcat maxPostSize는 multipart/form-data와 폼 인코딩에만 적용되고 임의 본문에는 적용되지 않는다) 외에 상한이 없다. 같은 요청에 대해 동시성 상한도 없으므로 SemaphoreAdmissionController가 내기로 되어 있던 503도 나오지 않는다.
|
|
|
|
<!-- body:end -->
|