Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/capability-declaration-vs-proof/case/case-a-delayed-delivery-flag-without-the-topology-that-delivers-it.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

6.6 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 a-delayed-delivery-flag-without-the-topology-that-delivers-it 지연 배달을 참으로 선언하는데 그 지연을 제공할 토폴로지가 조립되지 않는다 capability-declaration-vs-proof clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:a-delayed-delivery-flag-without-the-topology-that-delivers-it 2026-09-02
key file
a-delayed-delivery-flag-without-the-topology-that-delivers-it ../../../final/evidence/rendered/a-delayed-delivery-flag-without-the-topology-that-delivers-it.svg
../../../final/evidence/raw/a-delayed-delivery-flag-without-the-topology-that-delivers-it.txt
원본 분석은 Rabbit 어댑터 문서 §17.4 다. 성분 위치와 소비 사슬, 큐 선언 코드의 부재는 위 자산에서 확인할 수 있다.

지연 배달을 참으로 선언하는데 그 지연을 제공할 토폴로지가 조립되지 않는다

Rabbit 어댑터가 지연 배달을 참으로 선언한다. 그 지연을 만드는 큐를 선언하는 코드는 저장소에 없다.

관계

  • 능력 선언의 세 출처와 그것이 파생되지 않을 때
  • 능력 선언은 프로파일에서 파생되어야 하고 상수는 그것을 할 수 없다
  • 선택할 수 없는 브로커가 지원 매트릭스에 기능 목록과 함께 실려 있다

문제

능력 선언은 재시도 엔진이 읽는 값이다. 지연 배달이 참이면 브로커에게 지연을 맡기는 재시도 모드를 고를 수 있다.

RabbitMQ 의 코어 브로커에는 메시지별 지연이 없다. 지연 교환 플러그인을 설치하거나, 메시지 수명과 데드레터 라우팅으로 대기 큐를 만들어야 한다. 둘 다 토폴로지 선언을 요구한다.

결론

선언과 그것을 뒷받침할 큐 사이가 비어 있다.

이 어댑터는 그 큐를 어떻게 만드는지 이미 기술해 두었다. 그런데 그 기술을 참조하는 파일이 자기 자신과 시험 하나뿐이고, 큐를 실제로 선언하는 코드는 저장소 전체에 없다.

값을 읽는 엔진은 조립되어 있다. 지금 그 값이 엔진까지 닿지 않는 이유와, 닿더라도 남는 문제는 본문이 다룬다.

검증 환경

OpenJDK : 21.0.12 Gradle : 9.0.0 확인 방식 : 능력 성분의 위치 확인, 값을 읽는 엔진과 그 조립 지점 확인, 재시도 결정 소비자의 인자 확인, 큐 선언 코드 검색 소스 수정 : x

재현 조건

  1. 능력 record 의 성분 순서에서 지연 배달이 몇 번째인지 확인하고, Rabbit 전송이 그 자리에 넘기는 값을 읽는다.
  2. 그 값을 읽는 조건문과 그 엔진이 빈으로 등록되는 지점을 확인한다.
  3. 재시도 결정을 소비하는 코드가 지연 값을 어떻게 다루는지 확인한다.
  4. 지연 큐를 기술하는 타입을 참조하는 파일과, 큐를 선언하는 코드를 각각 검색한다.

본문

능력 record 는 열두 개의 불리언을 위치로 받고, 여덟째가 지연 배달이다. Rabbit 전송이 그 자리에 참을 넘긴다.

소비 사슬을 끝까지 따라가면 세 군데가 끊겨 있다

:::evidence key="a-delayed-delivery-flag-without-the-topology-that-delivers-it" alt="코드베이스에서 능력 성분의 여덟째 자리와 Rabbit 이 넘기는 값, 그 값을 읽는 엔진과 엔진의 조립 지점, 재시도 결정의 유일한 소비자, 지연 큐 타입을 참조하는 파일, 큐 선언 코드 매치 수를 뽑은 출력 23줄. 엔진이 빈으로 등록되고 소비자가 지연 값을 넘기지 않으며 큐를 선언하는 코드가 0 이라는 것이 그 출력에 그대로 보인다." caption="여덟째 성분 · 엔진과 조립 지점 · 결정 소비자 · 지연 큐 참조 · 큐 선언 매치 0 — 23줄 · exit 0" zoom="true" :::

DefaultRetryDecisionEngine 이 64행에서 그 값을 읽는다. 그리고 그 엔진은 자동 설정이 빈으로 등록한다 — 오늘 조립되어 돌고 있다.

끊긴 곳은 그 앞이다. Rabbit 은 전송을 출하하지 않아서 Rabbit 의 능력 record 가 엔진까지 도달하지 못한다. 스타터의 브로커 선택이 rabbit 을 이름으로 거절하고, 이유를 문장으로 적는다 — 검증기와 보안 설정은 출하하지만 전송이 없어 발행이 탈 것이 없다는 것이다.

지연을 만드는 방법은 이미 기술되어 있다

RabbitRetryQueueTopology 가 대기 큐를 기술한다. javadoc 이 왜 필요한지부터 적는다 — 코어 브로커에 메시지별 지연이 없으므로, 재시도 큐는 메시지 수명이 걸린 큐이고 그 데드레터 교환이 작업 큐를 다시 가리킨다. 메시지는 수명이 다할 때까지 앉아 있다가 다시 라우팅된다.

플러그인 뒤에 숨기지 않고 명시적으로 모델링한 이유도 적는다 — 그래야 동작이 검토 가능하다는 것이다. 함정까지 같이 적는다. 수명 만료는 큐 머리에서 평가되므로, 한 재시도 큐에 서로 다른 지연이 섞이면 각자 독립적으로 만료되지 않는다.

그 타입을 참조하는 파일은 자기 자신과 시험 하나다. 그리고 큐를 선언하는 코드를 이름으로 찾으면 매치가 0 이다.

배선해도 지연은 아직 흐르지 않는다

전송을 구현하는 것만으로 끝나지 않는다.

재시도 결정은 목적지와 지연을 함께 담는다. 그런데 그 결정을 소비하는 production 코드가 하나뿐이고 — Kafka 쪽 실행기다 — 그 실행기는 목적지만 넘기고 지연 값을 넘기지 않는다.

Rabbit 에는 대응하는 실행기가 없다. 그러니 배선하는 쪽이 해야 할 일은 전송 구현과 재시도 실행기와 큐 선언 셋이고, 그중 어느 하나만 해도 이 플래그는 여전히 참이다.

상수는 아무것도 강제하지 않는다

이 플래그는 프로파일에서 파생된 값이 아니라 소스에 박힌 상수다. 큐가 선언되었는지, 플러그인이 설치되었는지 보지 않는다.

그래서 위의 세 가지 중 무엇이 언제 채워지든 이 값은 바뀌지 않고, 바꿔야 한다고 알려 주는 것도 없다.

오늘 무엇이 이 결함을 막고 있나

Rabbit 능력이 엔진에 닿지 않는다는 것 하나다. 어댑터 자신이 아니라 그 위의 배선 부재가 막고 있다.

확인하지 못한 것

실제 배달 시점은 브로커를 띄워 확인해 보지 못했다. 큐 선언의 부재는 이름 기반 검색으로 판정했다.