--- kind: CASE slug: blocking-means-startup-fails-and-nothing-runs-it title: BLOCKING 이면 기동이 실패한다는 보장이 어떤 배선에서도 실행되지 않는다 topic: operator-approval-and-destructive-operations project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:blocking-means-startup-fails-and-nothing-runs-it evidenceCapturedOn: 2026-09-01 assets: - key: blocking-means-startup-fails-and-nothing-runs-it file: ../../../final/evidence/rendered/blocking-means-startup-fails-and-nothing-runs-it.svg evidence: - ../../../final/evidence/raw/blocking-means-startup-fails-and-nothing-runs-it.txt source: - 원본 분석 절은 analysis/messaging/messaging-admin-api.md §17 P2 이다. --- # BLOCKING 이면 기동이 실패한다는 보장이 어떤 배선에서도 실행되지 않는다 토폴로지 검증에서 차단 심각도는 기동을 실패시킨다고 두 javadoc 이 적는다. 그 판정을 부르는 production 코드가 없고, 그것을 부를 수 있는 유일한 진입점도 호출자가 없으며, 서비스 빈을 스타터가 만들지 않는다. ## 관계 - **승인·검증·실행의 분리와 그것을 타입으로 표현하기** 이 사례가 속한 표면이다. - **검증기는 발행이 아니라 주입이 강제다** 이 사례가 어기는 규칙이다. - **시작 검증기가 도는지는 그 능력에 자동설정 루트가 있는지와 일치한다** 같은 계열의 판정 규칙이다. - **시작 검증기 13개 규칙이 유일한 조립 지점에서 호출되지 않는다** 같은 형태가 gRPC 쪽에서 나타난 사례다. ## 문제 목적지 프로파일은 보장을 선언한다. 내구성이 필요하다거나, 순서가 필요하다거나, 특정 복제 수준이 필요하다는 선언이다. 브로커의 실제 토폴로지가 그 보장을 줄 수 없으면 그것은 기동 시점에 잡아야 한다. 런타임에 발견하면 이미 메시지를 받은 뒤다. ## 결론 타입은 그 상황을 정확히 표현하고, 표현한 것을 아무도 읽지 않는다. 심각도 열거형의 차단 값 javadoc 이 적는다. > The destination cannot deliver a declared guarantee; startup must fail. 검증 보고서의 수용 가능성 요구 메서드 javadoc 이 적는다. > Fails startup when any blocking issue was found. 그 메서드의 production 호출부가 0이다. 그것을 부를 수 있는 유일한 진입점인 토폴로지 검증 메서드도 호출부가 0이고, 그 메서드를 가진 서비스 빈을 스타터가 만들지 않는다. 결과: 복제 계수가 1인 목적지에 내구성을 선언해도 컨텍스트는 정상 기동한다. 고치는 방법이 같은 리프에 이미 있다. 내구성 검증기가 초기화 콜백으로 저널 내구성을 기동 시점에 검사하고 실패시킨다. 같은 모양의 빈 하나면 된다. 다만 브로커 검사기가 없으면 검증할 수 없으므로 그 조건부는 유지해야 한다. 이 항목이 무거운 이유는 리프가 출하 애플리케이션 소속이고, 보장이 문서와 타입과 테스트 세 겹으로 존재하는데 배선만 없다는 점이다. 읽는 사람은 보장이 있다고 믿을 근거를 세 개 갖는다. 그리고 토폴로지 검증 스택이 이 가족에 두 벌 있다. 파티션 스케일업에 대해 한쪽은 권고로 두어 기동을 허용하고 다른 쪽은 차이로 보아 거부한다. 둘 다 호출자가 0이라 지금은 충돌하지 않지만, 배선하는 순간 어느 스택을 고르느냐가 스케일업한 배포의 기동 여부를 가른다. ## 검증 환경 OpenJDK : 21.0.12 Gradle : 9.0.0 확인 방식 : 두 메서드의 호출자 전수 검색과 스타터의 빈 목록 확인 소스 수정 : x ## 재현 조건 원문은 final/evidence/raw/302-admin-api-topology-guarantee-unwired.txt (EVD-302) 에 있다. 1. 차단 심각도와 수용 가능성 요구 메서드의 javadoc 을 읽는다. 2. 그 메서드의 호출자를 저장소에서 검색한다. 3. 그것을 부를 수 있는 진입점의 호출자를 검색한다. 4. 스타터가 그 서비스 빈을 만드는지 확인한다. 5. 같은 리프의 내구성 검증기가 어떻게 배선되어 있는지 확인한다. ## 본문 `BLOCKING` 의 javadoc 이 "The destination cannot deliver a declared guarantee; startup must fail" 이라고 적고, `requireAcceptable()` 의 javadoc 이 "Fails startup when any blocking issue was found" 라고 적는다. ## MessagingAdminDurabilityValidator 참조 위치 :::evidence key="blocking-means-startup-fails-and-nothing-runs-it" alt="코드베이스에서 MessagingAdminDurabilityValidator 를 검색한 출력 3줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="MessagingAdminDurabilityValidator 코드베이스 검색 — 3줄 · exit 0" zoom="true" ::: ## 세 겹으로 호출자가 없다 그 메서드의 production 호출부가 0 이고, 그것을 부를 수 있는 유일한 진입점도 호출부가 0 이며, 그 서비스 빈을 스타터가 만들지 않는다. 결과적으로 복제 계수 1인 목적지에 내구성을 선언해도 컨텍스트는 정상 기동한다. 타입은 그 상황을 정확히 표현할 수 있고 표현한 것을 아무도 읽지 않는다. ## 고치는 방법이 같은 리프에 이미 있다 `MessagingAdminDurabilityValidator` 가 `InitializingBean` 으로 저널 내구성을 기동 시점에 검사하고 실패시킨다. 같은 모양의 빈 하나면 된다. ## 이 항목이 무거운 이유 이 리프가 `app-bootstrap` 소속이고 보장이 문서·타입·테스트 세 겹으로 존재하는데 배선만 없다 — 읽는 사람은 보장이 있다고 믿을 근거를 세 개 갖는다. 그리고 토폴로지 검증 스택이 이 리프에 두 벌 있고 파티션 스케일업에 대한 판정이 서로 반대라, 배선하는 순간 어느 스택을 고르느냐가 스케일업한 배포의 기동 여부를 가른다. P2. ## 확인하지 못한 것 컨텍스트를 세워 기동 성공을 관측하지 않았다. 호출부 부재로 판정했다.