Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/delivery-and-settlement-models/concept/concept-messaging-observability-c03.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다
- 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5
  (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를
  techviz 로 만들었다
- 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs
  돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다.
  Concept 이 인용한 코드가 SSOT 에 없어 뺐다
- candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +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
원본 분석 절은 final/document.md#a19-messaging-observability#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가 목록에 리터럴로 들어 있다.