Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/capability-declaration-vs-proof/case/case-the-transport-and-the-validator-answer-differently.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

5.5 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
CASE the-transport-and-the-validator-answer-differently 같은 어댑터의 능력을 전송과 검증기가 다르게 답하고, 런타임이 쓰는 쪽이 record 의 의미와 어긋난다 capability-declaration-vs-proof clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:the-transport-and-the-validator-answer-differently 2026-09-01
key file
the-transport-and-the-validator-answer-differently ../../../final/evidence/rendered/the-transport-and-the-validator-answer-differently.svg
../../../final/evidence/raw/the-transport-and-the-validator-answer-differently.txt
원본 분석 절은 analysis/messaging/messaging-pulsar-experimental.md §17.1 이다.

같은 어댑터의 능력을 전송과 검증기가 다르게 답하고, 런타임이 쓰는 쪽이 record 의 의미와 어긋난다

Pulsar 어댑터에서 키 공유 구독의 능력을 전송과 검증기가 다르게 답한다. 성분 문서를 기준으로 보면 검증기 쪽이 맞고 전송 쪽이 자기 안에서 모순인데, 런타임이 읽는 것은 전송 쪽이다.

관계

  • 능력 선언의 세 출처와 그것이 파생되지 않을 때 이 사례가 속한 구조다.
  • 능력 플래그의 무게는 그것을 읽는 코드가 정한다 어느 쪽이 틀렸는지가 아니라 어느 쪽이 읽히는지가 심각도를 정한다.
  • 중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다 같은 판정 절차의 일반형이다.

문제

이 어댑터는 두 구독 종류를 노출한다. 공유 구독은 경쟁 소비자에 순서 없음이고, 키 공유 구독은 경쟁 소비자에 키별 순서다.

능력을 답하는 자리가 둘이다. 전송이 구독 종류에 따라 두 상수 중 하나를 고르고, 검증기가 같은 판단을 자기 메서드로 한다.

결론

키 공유에 대해 두 답이 갈린다.

전송은 순서 있는 스트림을 거짓, 키별 순서를 참으로 답한다. 검증기는 둘 다 참으로 답한다.

성분 문서가 판정 기준이다. 순서 있는 스트림은 순서 단위 안에서 순서가 보존되는지를 뜻하고, 키 공유의 순서 단위는 키다. 그 단위 안에서 순서는 보존된다. 그러므로 검증기 쪽이 문서화된 의미와 맞다.

전송 쪽은 자기 안에서도 모순이다. 키별 순서를 참이라고 하면서 순서 있는 스트림을 거짓이라고 하면, 순서가 보존되는 단위가 있는데 그 단위 안에서 순서가 보존되지 않는다는 말이 된다.

그리고 어긋난 쪽이 런타임이 읽는 쪽이다. 목적지별 능력을 돌려주는 것은 SPI 메서드이고 그것을 구현하는 것은 전송이다. 순서 있는 스트림은 이 저장소에서 production 코드가 실제로 읽는 몇 안 되는 능력 중 하나로, 재시도 결정 엔진이 그 값을 보고 순서 보존 재시도를 고를지 정한다. 결과적으로 키별 순서를 약속한 목적지가 순서 보존 재시도를 받지 못한다.

두 리터럴을 묶는 것은 아무것도 없다. 열두 개의 불리언이 두 파일에 각각 손으로 적혀 있다. 테스트는 키별 순서만 단언하고 순서 있는 스트림은 보지 않는다.

자매 어댑터인 NATS 는 두 곳이 같은 값을 답한다. 다만 그 일치도 공유가 아니라 손으로 복사한 리터럴이므로, 오늘 같다는 것이 내일도 같으리라는 보장은 코드에 없다.

검증 환경

OpenJDK : 21.0.12 Gradle : 9.0.0 확인 방식 : 두 열두 성분 리터럴의 성분별 대조와 성분 문서 확인 소스 수정 : x

재현 조건

  1. 전송의 키 공유용 능력 상수 열두 성분을 순서대로 적는다.
  2. 검증기의 능력 메서드가 키 공유에 대해 만드는 열두 성분을 적는다.
  3. 두 목록을 성분별로 대조한다.
  4. 능력 record 의 성분 문서에서 두 이름의 정의를 읽는다.
  5. 순서 있는 스트림을 읽는 production 코드를 찾는다.

본문

Key_Shared 구독에 대해 전송은 orderedStream=false, keyedOrdering=true 를, 검증기는 orderedStream=true, keyedOrdering=true 를 답한다.

성분 문서가 판정 기준이다

orderedStream 은 "순서 단위 안에서 순서가 보존되는가" 이고 Key_Shared 의 순서 단위는 키다. 그러므로 검증기 쪽이 문서화된 의미와 맞고, 전송 쪽은 자기 안에서 모순이다.

어긋난 쪽이 런타임이 읽는 쪽이다

:::evidence key="the-transport-and-the-validator-answer-differently" alt="코드베이스에서 DefaultRetryDecisionEngine 를 검색한 출력 3줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="DefaultRetryDecisionEngine 코드베이스 검색 — 3줄 · exit 0" zoom="true" :::

capabilities(DestinationName) 이 SPI 메서드이고 orderedStream 은 production 코드가 실제로 읽는 세 능력 중 하나다 — DefaultRetryDecisionEngine 이 그 값으로 순서 보존 재시도를 고른다.

두 리터럴을 묶는 것이 없다

테스트는 keyedOrdering 만 단언해 orderedStream 을 보지 않는다. 자매 어댑터 NATS 는 두 곳이 같은 값을 답하지만 그 일치도 공유가 아니라 손으로 복사한 리터럴이다.

확인하지 못한 것

두 답이 실제 재시도 선택을 어떻게 가르는지 실행으로 재현하지 않았다. 이 가족은 배선 경로가 없다.