- 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>
13 KiB
kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, body, assets, evidence, source
| kind | slug | title | topic | project | status | sourceRevision | rootTreeNode | evidenceCapturedOn | body | assets | evidence | source | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| CASE | a06-f003-change-streams-true | 거부라고 적힌 처리가 폐기이고, 그 값을 읽는 시작 검사는 켤 방법이 없다 | runtime-reachability-and-composition | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:a06-f003-change-streams-true | 2026-09-02 | case-a06-f003-change-streams-true.body.md |
|
|
|
거부라고 적힌 처리가 폐기이고, 그 값을 읽는 시작 검사는 켤 방법이 없다
설정 타입의 컴팩트 생성자가 변경 스트림 플래그를 예외 없이 거짓으로 덮어쓴다. 그 자리의 주석은 이 처리를 거부라고 부른다. 거부와 폐기는 운영자에게 다른 사건이다. 하나는 기동이 멈추고 하나는 아무 일도 일어나지 않는다.
관계
- 하위 시스템 전체가 미배선인데 그것을 켜는 플래그는 시작 검사를 수행한다 그 기록에서는 플래그가 켜져 있어 요구를 만드는데 실행체가 없다.
- sanitize가 아니라 reject가 기본이다 이 주석이 따르겠다고 선언한 규칙이다.
- 플래그는 고정 거짓이라 능력 검사를 끄지만, 조립 조건이 아니라서 소비자 빈은 그대로 생성된다 같은 플래그를 다른 절에서 다룬 기록이다. 등급 재조정은 그 기록이 한다.
문제
레코드 컴포넌트는 넷이고 생성자는 넷을 서로 다르게 다룬다. 프로파일은 널이면 빈 맵으로 흡수한다. 필요 보조 노드 수는 널이면 기본값을 넣고 음수면 예외로 거부한다. 형제 불리언인 트랜잭션 플래그는 그대로 보존한다. 변경 스트림 플래그만 예외 없이 거짓이 된다.
결론
이 플래그가 실제로 끄는 것은 실행체가 아니라 검사다. 시작 검증기는 변경 스트림이 켜져 있을 때 토폴로지 능력을 확인하는 분기를 갖는다. 그 인자를 넘기는 프로덕션 지점은 자동 구성 한 줄이고, 그 줄이 읽는 값은 생성자가 이미 거짓으로 덮어쓴 뒤다.
시험도 그 분기 본문에 닿지 않는다. 검증기를 만드는 시험은 하나이고 호출은 넷인데, 둘은 거짓을 넘기고, 하나는 앞 단계인 토폴로지 검사에서 먼저 멈추며, 나머지 하나는 복제 셋이라 능력이 안정으로 나온다. 그 분기가 내는 문장은 저장소 안에서 자기 throw 자리 한 곳에만 있고, 그것을 단언하는 시험은 없다.
오플로그가 없는 단독 서버로 확인했다. 출하 배선의 시작 검증은 통과하고, 같은 검증기에 리터럴 참을 넘기면 토폴로지를 지목하는 예외가 난다. 그 뒤 커서를 열면 드라이버가 명령 단계에서 거절하고, 복구 정책은 그것을 실패로 확정하며 프라이머리 장애 조치 런북을 붙인다. 그 런북에 오플로그 없는 토폴로지 항목은 없다.
주석은 이 처리의 근거로 드라이버 쪽 구현이 출하되지 않아 빈이 0 이라는 것을 든다. 그 근거는 이 리비전에서 성립하지 않는다. 다만 그 사실이 바꾸는 것은 원본 절의 등급 판단이고, 이 기록의 관찰은 거부와 폐기의 차이 그대로다.
수정은 둘 중 하나다. 값을 정말로 거부하거나, 플래그를 레코드 컴포넌트에서 빼 존재하지 않는 스위치로 만드는 것이다.
검증 환경
OpenJDK : 21.0.12 MongoDB : 8.0.16 단독 서버 확인 방식 : 실제 바인딩에 세 입력 통과, 실제 서버에 시작 검증기와 커서 열기와 복구 정책 실행 소스 수정 : x
재현 조건
- 컴팩트 생성자에서 네 컴포넌트의 처리를 비교하고 변경 스트림 자리의 주석을 읽는다.
- 시작 검증기의 변경 스트림 분기, 그 인자를 넘기는 곳, 검증기를 만드는 곳을 전수로 센다.
- 그 분기가 내는 문장이 저장소 어디에 나오는지 센다.
- 검증기를 만드는 시험의 네 호출이 각각 어디서 멈추는지 읽는다.
- 오플로그가 없는 단독 서버를 띄운다.
- 세 입력을 실제 바인딩에 통과시키고 각각의 결과를 읽는다.
- 같은 서버에 대해 출하 배선과 리터럴 참 배선으로 시작 검증을 각각 돌린다.
- 같은 클래스로 커서를 열고, 그 실패를 복구 정책에 넘긴다.
본문
설정 타입의 주석은 이 값을 저장하지 않고 거부한다고 적으면서, 근거로 드라이버 쪽 구현이 없다는 것을 든다.
네 값 중 하나만 삼켜진다
:::evidence key="a06-f003-change-streams-true" alt="설정 타입의 컴팩트 생성자에서 네 컴포넌트가 각각 흡수·보존·덮어쓰기·예외로 처리되는 구간과 변경 스트림 자리의 주석, 시작 검증기의 변경 스트림 분기, 그 분기가 내는 문장이 저장소에 나오는 곳 전수, changeStreams 라는 이름이 나오는 곳 전수, 검증기를 만드는 곳 전수를 출력한 터미널 기록." caption="프로파일은 빈 맵으로 흡수 · 음수 보조 노드 수는 예외 · 트랜잭션은 보존 · 변경 스트림만 예외 없이 거짓 · 분기가 내는 문장은 자기 throw 자리 한 곳뿐 · 검증기 생성 지점은 프로덕션 1 시험 1 — 43줄 · exit 0" zoom="true" :::
// Experimental, and therefore not a switch (MNG-INT-003). The driver-side source — watch,
// resumeAfter/startAfter, cursor lifetime, reconnection — is not shipped; what exists is policy
// and value objects that do not add up to a running consumer. Accepting the flag and ignoring
// it
// would leave an operator believing it took effect, so the value is refused rather than stored:
// zero beans, zero threads, and a `true` that cannot be honoured never becomes one that looks
// honoured.
changeStreams = false;
여덟 줄 아래에서 같은 생성자가 음수 보조 노드 수를 예외로 던진다. 거부가 어떤 모양인지 같은 생성자 안에 있고, 이 줄은 그 모양이 아니다. 형제 불리언인 transactions 는 손대지 않는다.
검증기가 받는 값은 언제나 거짓이다
if (changeStreamsEnabled && !capabilities.isStable(MongoCapability.CHANGE_STREAM)) {
좌항을 넘기는 프로덕션 지점은 자동 구성 한 줄뿐이다. 검증기를 만드는 곳은 그 줄과 시험 하나이고, 시험은 리터럴을 넘긴다. 그런데 그 시험의 네 호출 어느 것도 이 if 의 본문을 실행하지 않는다.
- 단독 서버에 프로덕션 프로파일을 놓는 호출은 참 둘을 넘기지만,
validate()가 능력 검사보다 먼저 부르는 토폴로지 검사에서 멈춘다 - 선언과 실제가 어긋나는 호출은 거짓 둘을 넘긴다
- 트랜잭션만 켜는 호출은 변경 스트림에 거짓을 넘긴다
- 정상 복제 셋 호출은 참을 넘기지만 그 토폴로지에서는 능력이 안정으로 나온다
이 if 가 만드는 문장은 저장소 전체에서 자기 throw 자리 한 곳에만 있다.
실제 서버에서
:::evidence key="a06-f003-change-streams-true-probe" alt="오플로그가 없는 단독 MongoDB 를 띄우고 세 입력을 실제 바인딩에 통과시킨 결과, 관측 토폴로지와 변경 스트림 능력 판정, 출하 배선과 리터럴 참 배선으로 각각 돌린 시작 검증 결과, 같은 클래스를 직접 만들어 커서를 연 결과, 그리고 그 실패를 복구 정책에 넘긴 판정을 출력한 터미널 기록." caption="단독 서버에서 능력 판정은 isStable=false · 출하 배선의 시작 검증은 통과, 리터럴 참 배선은 토폴로지를 지목하며 거절 · 커서를 열면 드라이버가 40573 으로 거절 · 복구 정책은 FAILED 와 failover 런북 — 20줄 · exit 0" zoom="true" :::
오플로그가 없는 단독 서버를 띄우고 같은 클래스들을 그대로 썼다. 리터럴 참을 넘긴 배선의 예외 메시지는 이 배포에 없는 것과 필요한 것을 함께 적는다.
change streams are enabled but unavailable here: {reason=topology is STANDALONE, which has no oplog}; a REPLICA_SET, SHARDED or ATLAS topology is required
출하 배선에서는 이 예외가 만들어지지 않는다. 같은 클래스를 직접 만들어 커서를 열면 드라이버가 명령 단계에서 거절한다.
Command execution failed on MongoDB server with error 40573 (Location40573): 'The $changeStream stage is only supported on replica sets' on server 127.0.0.1:57017.
그 실패를 복구 정책에 넘기면 판정이 나온다.
MongoChangeStreamRecoveryDecision[state=FAILED, autoResume=false, requiredRunbook=docs/mongodb/runbooks/failover.md]
정책은 서버 코드가 이력 소실이면 전용 런북을, 라벨이 재개 가능이면 재개를 고르고, 그 밖은 실패로 확정한다. 40573 은 셋째 갈래다. 붙는 런북은 프라이머리 선출과 서버 선택 지연을 다루고, 이 서버에 오플로그가 없다는 경우는 다루지 않는다.
주석이 근거로 든 사실
:::evidence key="a06-f003-change-streams-true-shipped" alt="드라이버 쪽 구현을 만드는 빈 위에 겹쳐 있는 조건 넷(모듈 opt-in, 블로킹 템플릿 클래스, 리액티브 템플릿 클래스와 빈), 그 빈이 커서를 여는 호출 사슬, 소비자 빈이 요구하는 조건 다섯, 그 다섯을 구현하는 클래스 전수, 저장소 자신의 배선 시험 네 개, 그리고 이 자동 구성 파일에서 해당 플래그가 나오는 유일한 줄을 출력한 터미널 기록." caption="source 빈의 조건은 모듈 opt-in 과 리액티브 템플릿, 그리고 @ConditionalOnMissingBean · 소비자 조건 다섯의 구현체 열은 전부 시험 픽스처 · 배선 시험이 조립·미조립·미배선을 각각 고정 · 플래그는 검증기 인자 한 줄뿐 — 56줄 · exit 0" zoom="true" :::
드라이버 쪽 구현에는 빈 선언이 있다. 그 빈의 메서드에 붙은 조건은 @ConditionalOnMissingBean 하나지만, 그것을 감싼 중첩 설정 클래스가 리액티브 템플릿을 클래스로도 빈으로도 요구하고, 다시 그 바깥이 모듈 opt-in 을 요구한다. 무조건은 아니고 리액티브 템플릿이 있는 배포에서는 언제나다. 그 조건 넷 어디에도 변경 스트림 플래그는 없다.
소비자 빈의 선언도 있다. 조건이 다섯인데 다섯 다 배포가 공급해야 하는 타입이고, 이 저장소는 다섯 중 어느 것도 빈으로 만들지 않는다. 열 개의 구현체가 전부 시험 안의 private static final class 다.
저장소 자신의 배선 시험이 세 상태를 각각 고정한다. 리액티브 템플릿이 없으면 아무것도 만들지 않고, 있으면 source 는 만들고 투영기가 없으면 소비자는 만들지 않고, 다섯을 주면 소비자가 조립된다.
주석이 없다고 적은 넷 가운데 watch 와 resumeAfter/startAfter 는 이 클래스에 있고, 커서 수명과 재연결은 같은 패키지의 소비자에 있다.
확인하지 못한 것
소비자 빈의 조건 다섯을 공급하는 포크의 배포는 다루지 않았다. 이 저장소 안에서 확인한 것은 다섯 타입의 구현체가 전부 시험 픽스처라는 것, 조건 목록에 이 플래그가 없다는 것, 그리고 저장소 자신의 배선 시험이 그 세 상태를 각각 고정한다는 것이다.