Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/delivery-and-settlement-models/concept/concept-messaging-observability-c03.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

4.1 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source, module
kind slug title topic project status sourceRevision rootTreeNode evidenceCapturedOn assets evidence source module
CONCEPT messaging-observability-c03 차원이 닫혀 있고, 사전 확인과 커밋 사이에 lock이 없다 delivery-and-settlement-models clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 concept:messaging-observability-c03 2026-09-01
key file
messaging-observability-c03 ../../../final/evidence/rendered/messaging-observability-c03.svg
key file
messaging-observability-c03-diagram ../../../final/assets/diagrams/messaging-observability-c03.svg
../../../final/evidence/raw/messaging-observability-c03.txt
원본 분석 절은 analysis/messaging/messaging-observability.md#L113 이다.
messaging-observability

차원이 닫혀 있고, 사전 확인과 커밋 사이에 lock이 없다

여섯 차원: broker, destinationProfile, operation, outcome, failureCategory, retryStage. 없는 값은 NONE = "none"이다 — null도 빈 문자열도 아니고 명시적 sentinel이다.

관계

  • 타입이 문서화한 불변식은 타입이 강제한다 같은 분석 리프에서 끌어낸 규칙이다.

본문

MessagingTags는 열린 map이 아니라 고정 record다 — "Every field here is bounded by configuration or by an enum, so the cardinality of the metric is known before it is ever scraped. Message ids, partition keys, tenant ids, and offsets are all deliberately absent: each of them is unbounded at runtime and would multiply every series by the message volume."

태그에서 일부러 뺀 것

:::evidence key="messaging-observability-c03-diagram" alt="여섯 고정 차원과 없는 값은 none 이 MessagingTags 안에 놓이고 message id 와 partition key, tenant id 와 offset 이 바깥에 빗금으로 놓인다" caption="태그에서 일부러 뺀 것" zoom="false" :::

여섯 차원은 broker, destinationProfile, operation, outcome, failureCategory, retryStage다. 없는 값은 NONE = "none"이다 — null도 빈 문자열도 아니고 명시적 sentinel이다.

두 factory의 차이

new MessagingTags(6개 인자)failureCategoryretryStage를 호출자가 지정하고, MessagingTags.of(4개 인자)는 둘 다 NONE 고정이다. 이 차이가 §12.1의 핵심이 된다.

LinkedHashMap 참조 위치

:::evidence key="messaging-observability-c03" alt="코드베이스에서 LinkedHashMap 를 검색한 출력 10줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="LinkedHashMap 코드베이스 검색 — 10줄 · exit 0" zoom="true" :::

asMap()LinkedHashMap으로 순서를 고정하고 Map.copyOf로 불변화한다.

호출자가 철자를 정하지 않는다

MessagingObservation은 네 메서드와 네 상수(PUBLISH, CONSUME, SETTLE, DEAD_LETTER)를 갖는다. publish(...)PublishCompletionOptional<FailureCategory>를 받아 enum에서 문자열을 파생한다. 이 클래스는 소비자가 0이다(§12.1).

이전 결함 둘이 코드에 남아 있다

기본 상한은 200/차원이다. 먼저 lock 없이 values.contains(value)로 빠른 경로를 두고, 새 값일 때만 synchronized로 들어가 다시 확인한다 — double-checked 패턴이다. 테스트가 경합을 직접 재현한다(MessagingSecretLeakTest.concurrentAdmissionNeverExceedsTheLimit).

사전 확인과 커밋 사이에 lock이 없다

wouldAdmit으로 전수 사전 확인 후 admit으로 커밋한다. 그 사이에 lock이 없으므로 두 스레드가 동시에 통과할 수 있고, 그 경우 두 번째 admit이 false를 반환해 admitted &= ...가 false가 된다 — 상한은 지켜지고 결과만 거절이 된다. 안전한 방향이다.

같은 문제를 두 강도로 푼다

거부 목록은 27개 키이고 두 범주를 섞어 담는다. isDenied가 소문자 정규화 후 정확 일치다. messaging-core-apiMessageHeaders.carriesACredential은 세그먼트 매칭 + 인접 결합(그쪽 §4.6)인데 이쪽은 정확 일치다(§12.3). msg.id가 목록에 리터럴로 들어 있다.