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.2 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source, module, priority
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | assets | evidence | source | module | priority | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | messaging-runtime-core-f01 | 관측이 구현·호출부·주입 자리를 모두 갖추고도 출하에서 no-op이다 | runtime-reachability-and-composition | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:messaging-runtime-core-f01 | 2026-09-01 |
|
|
|
messaging-runtime-core | P2 |
관측이 구현·호출부·주입 자리를 모두 갖추고도 출하에서 no-op이다
DefaultMessagePublisher가 모든 발행 결과를 observation.recordPublish(...)로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다. MessagingMetrics가 MessagingObservation을 구현한다.
관계
- 만들어 두고 흘리지 않는 진단값은 진단이 아니다 같은 분석 리프에서 끌어낸 규칙이다.
- 증가한다고 문서화한 값이 리터럴이면 그 사실을 적는다 같은 분석 리프에서 끌어낸 규칙이다.
- 안정 코드는 운영자의 행동이 갈리는 지점마다 나눈다 같은 분석 리프에서 끌어낸 규칙이다.
CompletionStage를 반환하는 메서드는 동기적으로 던지지 않는다 같은 분석 리프에서 끌어낸 규칙이다.
문제
DefaultMessagePublisher가 모든 발행 결과를 observation.recordPublish(...)로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다.
MessagingMetrics가 MessagingObservation을 구현한다.
결론
그런데 출하 조립(MessagingCoreAutoConfiguration:446)은 6인자 생성자를 써서 NO_OBSERVATION을 넣고, MessagingMetrics는 저장소 전체에서 자기 테스트에서만 생성된다.
starter는 MessagingMetrics의 협력자 둘(MessagingRedactor:253, CardinalityGuard:264)을 bean으로 만든다.
이 필드의 javadoc이 정확히 이 상황을 막으려고 쓰였다 — "an unobserved publish path is how 'the dashboards were empty during the incident' happens".
그리고 같은 javadoc이 이전 결함을 "bean은 있고 호출 경로가 없었다"로 기록한다.
지금은 반대다 — 호출 경로가 있고 bean이 없다.
관측 결과는 같다.
고침이 간극을 닫은 게 아니라 반대편으로 옮겼다.
"decorator가 아니라 생성자 인자"라는 선택도 막지 못했는데, 인자를 기본값으로 채우는 짧은 생성자가 함께 있기 때문이다.
검증 환경
OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : DefaultMessagePublisher 참조 22건 검색과 출하 조립이 고르는 생성자의 인자 수 확인 소스 수정 : x
재현 조건
원문은 analysis/messaging/messaging-runtime-core.md#L710 에 있다.
본문
DefaultMessagePublisher가 모든 발행 결과를 observation.recordPublish(...)로 기록하고, 관측을 "constructor argument rather than an optional decorator"로 받는다. MessagingMetrics가 MessagingObservation을 구현한다.
조립이 되돌린 것
:::evidence key="messaging-runtime-core-f01-diagram" alt="MessagingMetrics 구현과 recordPublish 호출부와 생성자 인자 자리가 갖춰진 것 안에 놓이고 출하 조립의 6인자 생성자가 바깥에 빗금으로 놓인다" caption="조립이 되돌린 것" zoom="false" :::
그런데 출하 조립(MessagingCoreAutoConfiguration:446)은 6인자 생성자를 써서 NO_OBSERVATION을 넣고, MessagingMetrics는 저장소 전체에서 자기 테스트에서만 생성된다.
DefaultMessagePublisher 참조 위치
:::evidence key="messaging-runtime-core-f01" alt="코드베이스에서 DefaultMessagePublisher 를 검색한 출력 22줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DefaultMessagePublisher 코드베이스 검색 — 22줄 · exit 0" zoom="true" :::
협력자 둘은 bean 으로 만들어진다
starter가 MessagingRedactor:253, CardinalityGuard:264 를 만든다. 이 필드의 javadoc이 정확히 이 상황을 막으려고 쓰였다 — "an unobserved publish path is how 'the dashboards were empty during the incident' happens".
고침이 간극을 반대편으로 옮겼다
같은 javadoc이 이전 결함을 "bean은 있고 호출 경로가 없었다"로 기록한다. 지금은 반대다 — 호출 경로가 있고 bean이 없다. 관측 결과는 같다. "decorator가 아니라 생성자 인자"라는 선택도 막지 못했는데, 인자를 기본값으로 채우는 짧은 생성자가 함께 있기 때문이다.
확인하지 못한 것
6인자 생성자 선택이 의도인지 확인할 수 없었다. 커밋이 대량 커밋 4개뿐이고 이 선택을 설명하는 기록이 없다.