- 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>
14 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 | analysis-finding-a06-f014 | 인덱스 diff 는 열네 요소 중 keySignature 와 unique 만 비교한다 | multitenancy-isolation | clean-architecture-backend-template | 게시 전 | 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 | case:analysis-finding-a06-f014 | 2026-09-04 | case-analysis-finding-a06-f014.body.md |
|
|
|
인덱스 diff 는 열네 요소 중 keySignature 와 unique 만 비교한다
MongoIndexManifest:22:35 가 열네 요소를 선언하는데 MongoIndexDescriptorView:15:20 은 여섯 컴포넌트만 갖는다. MongoIndexDiffEngine.compare 가 두 값을 함께 읽는 곳은 셋인데, :49~:50 은 양방향이고 :55 의 hidden 은 한 방향만 본다.
관계
- 빠뜨림이 통과가 되는 게이트는 게이트가 아니다
이 사례가 그 규칙의 형태다. 견주지 않은 요소가 달라져도
MongoIndexDiff의 네 목록이 전부 빈다.MongoIndexDiff:9~:11은 그 결과를 CI 산출물로 설계했다고 적는데, 지금 그것을 만드는 프로덕션 코드는 없다. - 문서와 상수가 서로 일치하는 것으로는 아무것도 증명되지 않는다
그 규칙이 요구하는 검사 경계 명시가 여기에 없다. 그 규칙 3 이 요구하는 검사 경계 명시가 여기에 없다.
MongoIndexDescriptorView:9가 축소 사실은 밝히지만, 그 결과 비교되지 않는 아홉 요소를 어디에도 적지 않는다. - TTL 규칙 셋을 가진 타입들을 부르는 프로덕션 코드가 없다
그 기록의
expireAfter가 이 diff 에서 견주어지지 않는 열 요소 중 하나다. TTL 보존 기간을 바꿔도 빈 보고서가 나온다.
문제
인덱스 드리프트 보고서는 선언한 인덱스와 서버에 있는 인덱스를 견주어 만든다. 매니페스트는 인덱스 하나에 대해 열네 가지를 선언한다.
그 열넷 중 무엇이 실제로 견주어지는지 확인했다.
결론
MongoIndexManifest:22~:35 의 요소는 열넷이고 MongoIndexDescriptorView:15~:20 은 여섯이다. 매니페스트 요소 가운데 뷰에 같은 이름이 있는 것은 name 과 unique 와 hidden 과 metadataOwnership 넷뿐이다.
MongoIndexDiffEngine.compare:41~:58 이 매니페스트와 뷰를 대조한다. 두 값을 한 줄에서 함께 읽는 곳은 셋인데, :49~:50 이 keySignature 와 unique 로 change 를 정하고 :55 가 declared.hidden() && !actual.hidden() 로 hide 를 정한다.
그 반대는 검사하지 않는다. 서버가 숨긴 인덱스를 매니페스트가 보인다고 선언한 경우인데, MongoIndexDiff:13~:14 가 적은 폐기·숨김·관측·승인 순서에서 숨김까지 간 인덱스를 다시 쓰기로 바꾸면 그 조합이 남는다. 그때 계획기는 그 인덱스를 쓰지 않는다.
뷰에 대응 필드가 없는 열 요소는 어느 방향으로도 비교되지 않는다. 그 열에서 keys 하나만 keySignature 로 요약돼 들어가고, 남는 아홉에 expireAfter 가 있다.
MongoIndexDiff:9~:11 은 이 결과가 CI 산출물이며 실행마다 순서가 바뀌면 이전 실행과 대조할 수 없어서 모든 목록을 정렬한다고 적는다. 네 목록이 모두 비면 render() 가 빈 문자열을 낸다.
다만 그 산출물을 지금 만드는 프로덕션 코드는 없다. 엔진 이름이 main 에 나오는 줄이 자기 클래스 선언 하나뿐이고, 부르는 것은 시험 넷이다.
MongoIndexDescriptorView:9~:12 는 그 축소 자체는 밝힌다. 무엇이 감지 불가가 되는지만 적지 않는다.
검증 환경
OpenJDK : 21.0.12 확인 방식 : 매니페스트와 뷰의 요소 전수 인용, 두 목록의 요소 계수와 자기검증, 요소마다 뷰에 있는지 이름으로 대조, 비교 엔진 본문 인용과 두 값을 함께 읽는 줄 계수와 전수, diff 레코드 전문 인용, 엔진의 main 등장 계수와 시험 호출 대조 소스 수정 : x
재현 조건
- MongoIndexManifest 와 MongoIndexDescriptorView 의 레코드 헤더를 전문으로 싣는다.
- 두 목록의 요소를 세고, 열넷이 아니면 스크립트가 실패하게 한다.
- 매니페스트 요소마다 같은 이름이 뷰에 있는지 대조해 전부 나열한다.
- MongoIndexDiffEngine.compare 를 인용하고 그 안에서 declared 와 actual 을 한 줄에서 함께 읽는 줄을 세고 전부 뽑는다.
- MongoIndexDiff 의 클래스 자바독을 인용한다.
- MongoIndexDiffEngine 이 main 에 나오는 줄을 세고, 그 엔진의 compare 를 부르는 시험 줄과 대조한다.
본문
MongoIndexDiffEngine 이 매니페스트와 서버에서 읽은 뷰를 대조해 드리프트 보고서를 만든다.
매니페스트가 선언하는 열넷
:::evidence key="analysis-finding-a06-f014" alt="저장소 루트에서 돌린 정적 검색 출력 192줄. 먼저 MongoIndexManifest 1145번이 실린다. 1219번 자바독은 이것이 diff 나 검토에 필요한 모든 것을 담은 선언된 인덱스 하나이고, expectedUsage 가 이 인덱스가 존재하는 연산을 이름 지어 이 인덱스가 아직 필요한지를 프로덕션 통계로 추측하지 않고 답할 수 있게 하며, metadataOwnership 이 누가 만들었는지를 적어 드리프트 정리가 선언하지 않은 암호화나 검색 인덱스를 지우자고 제안하지 못하게 한다고 적고, 키 순서가 표현상의 세부가 아니라 인덱스 정체성의 일부라 보존된다고 적는다. 2135번의 record 헤더가 name 과 keys 와 unique 와 sparse 와 hidden 과 deprecated 와 partialFilterExpression 과 collationProfile 과 expireAfter 와 wildcardProjection 과 shardKeySupport 와 expectedUsage 와 owner 와 metadataOwnership 열넷을 선언한다. 이어서 MongoIndexDescriptorView 620번이 실린다. 712번 자바독은 이것이 서버에 실제로 있는 인덱스이며 D4 관리 클라이언트로 읽어 diff 가 견줄 수 있는 필드로 줄인 것이고, 소유권은 매니페스트를 믿는 대신 여기서 추론하는데 diff 의 목적 자체가 매니페스트가 모르는 인덱스를 찾는 것이며 그중 일부는 암호화나 검색에 속해 절대 삭제 후보가 되면 안 되기 때문이라고 적는다. 1420번이 collection 과 name 과 keySignature 와 unique 와 hidden 과 metadataOwnership 여섯을 선언한다. 다음으로 MongoIndexDiffEngine 2570번이 실린다. 4158번 반복문이 선언된 인덱스마다 같은 이름의 뷰를 찾는데, 4348번이 뷰가 없고 폐기 표시도 없으면 create 에 넣고, 4950번이 keySignature 가 다르거나 unique 가 다르면 change 에 넣으며 5152번 주석이 같은 이름에 다른 정의는 MongoDB 가 조용히 다시 만들지 않으므로 질의가 스캔을 시작할 때 발견되는 대신 재빌드가 계획돼야 한다고 적고, 5557번이 선언은 숨김인데 서버는 아닐 때만 hide 에 넣는다. 6067번은 매니페스트에 없는 서버 인덱스를 dropCandidates 에 넣는데 애플리케이션 드리프트로 삭제 가능한 소유권만 본다. 그 아래 한 줄에서 declared 값과 actual 값을 견주는 줄이 3 개라고 나오고 그 셋이 49번과 50번과 55번으로 실리며, 참고로 그 반복문에서 declared 나 actual 이 나오는 줄 전부도 함께 나온다. 이어서 매니페스트 요소가 14 개이고 뷰 요소가 6 개라고 나온 뒤, 매니페스트 요소마다 뷰에 있는지가 전부 나열되는데 name 과 unique 와 hidden 과 metadataOwnership 넷만 뷰에 있고 나머지 열은 뷰에 없다. 요소 수가 14 가 아니면 실패하는 자기검증도 함께 찍힌다. 마지막으로 MongoIndexDiff 149번이 실리는데 615번 자바독이 모든 목록이 정렬돼 있어 같은 입력에 대해 diff 가 바이트 단위로 같으며 실행마다 순서가 바뀌는 CI 산출물은 이전 실행과 견줄 수 없고 그것이 인덱스 드리프트 보고서의 목적 대부분이라고 적고, dropCandidates 는 이름 그대로 폐기와 숨김과 관측과 승인을 통과해야 무엇이든 삭제되는 제안일 뿐이라고 적으며, 31번의 empty 와 3638번의 isClean 과 41~49번의 render 가 이어진다. 그 아래 MongoIndexDiffEngine 이 main 에 나오는 줄이 1 개인데 그것이 23번의 클래스 선언이고, 대조로 센 시험 쪽 engine.compare 호출은 MongoIndexDiffEngineTest 30·42·49·61번 네 줄이다." caption="매니페스트가 선언하는 열넷과 그 자바독 · 비교용 뷰가 나르는 여섯과 축소를 밝힌 자바독 · 비교 엔진 본문과 실제로 견주는 두 줄 · 요소마다 뷰에 있는지 대조한 전수와 자기검증 · CI 산출물이라 적은 diff 자바독과 호출 계수 — 192줄 · exit 0" zoom="true"
:::
MongoIndexManifest:22~:35 가 인덱스 하나에 대해 열네 요소를 선언한다. name, keys, unique, sparse, hidden, deprecated, partialFilterExpression, collationProfile, expireAfter, wildcardProjection, shardKeySupport, expectedUsage, owner, metadataOwnership 이다.
:14~:17 자바독은 그 열넷 가운데 둘을 따로 설명한다. expectedUsage 는 이 인덱스가 존재하는 연산을 이름 지어 "아직 필요한가" 를 프로덕션 통계로 추측하지 않고 답할 수 있게 하고, metadataOwnership 은 누가 만들었는지를 적어 드리프트 정리가 선언하지 않은 암호화나 검색 인덱스를 지우자고 제안하지 못하게 한다.
비교용 뷰가 선언하는 여섯 필드
MongoIndexDescriptorView:15~:20 이 여섯 컴포넌트를 선언한다. collection, name, keySignature, unique, hidden, metadataOwnership 이다.
:9~:12 자바독이 그 축소를 밝힌다. D4 관리 클라이언트로 읽어 diff 가 비교할 수 있는 필드로 줄였다는 것이다. 소유권을 매니페스트에서 받지 않고 여기서 추론하는 이유도 적는다 — diff 의 목적이 매니페스트가 모르는 인덱스를 찾는 것이고, 그중 일부는 암호화나 검색에 속해 삭제 후보가 되면 안 되기 때문이다.
그 축소로 어떤 요소가 비교 대상에서 빠지는지는 어디에도 적혀 있지 않다.
compare 가 declared 와 actual 을 함께 읽는 세 줄
MongoIndexDiffEngine.compare:41 이 선언된 인덱스마다 같은 이름의 뷰를 찾는다.
:43~:48 이 뷰가 없고 폐기 표시도 없으면 create 에 넣는다.
:49~:50 이 keySignature 가 다르거나 unique 가 다르면 change 에 넣는다. :51~:52 주석은 같은 이름에 다른 정의를 MongoDB 가 조용히 다시 만들지 않으므로, 질의가 스캔을 시작할 때 발견되는 대신 재빌드가 계획돼야 한다고 적는다.
:55~:57 이 declared.hidden() && !actual.hidden() 일 때 hide 에 넣는다.
한 줄에서 선언 값과 서버 값을 함께 읽는 줄은 셋이다. :49 와 :50 과 :55 다. 앞의 둘이 change 를 결정하고 :55 가 hide 를 결정한다.
한 방향만 보는 검사
:55 의 조건은 선언이 숨김이고 서버가 아닌 경우다.
반대에 대한 분기가 없다. 서버에서는 숨겨져 있는데 매니페스트가 보인다고 선언한 상태다.
이 상태에서는 매니페스트가 hidden 을 거짓으로 선언한 인덱스를 서버가 숨겨 두고 있고, 질의 계획기는 숨겨진 인덱스를 쓰지 않는다. 보고서는 그것을 어느 목록에도 넣지 않는다.
뷰에 없는 열 요소
매니페스트 요소마다 뷰에 같은 이름이 있는지 대조했다. name 과 unique 와 hidden 과 metadataOwnership 넷만 있다. 뷰 요소가 여섯이 아니면 스크립트가 실패한다.
나머지 열은 없다. keys, sparse, deprecated, partialFilterExpression, collationProfile, expireAfter, wildcardProjection, shardKeySupport, expectedUsage, owner 다.
keys 만 keySignature 라는 다른 이름으로 요약돼 들어간다. 나머지 아홉에는 뷰에 대응하는 컴포넌트가 없다.
그 아홉에 expireAfter 가 있다. 30일을 1일로 바꾸는 것은 대량 삭제인데, 이 diff 는 그것을 차이로 보고하지 않는다.
네 목록이 모두 비었을 때의 산출물
MongoIndexDiff:9~:11 은 모든 목록을 정렬해 같은 입력에 대해 바이트 단위로 같은 결과를 낸다고 적는다. 실행마다 순서가 바뀌는 CI 산출물은 이전 실행과 견줄 수 없고 그것이 드리프트 보고서의 목적 대부분이기 때문이다.
:13~:14 는 dropCandidates 가 이름 그대로 제안일 뿐이며 폐기와 숨김과 관측과 승인을 통과해야 무엇이든 삭제된다고 적는다.
:41~:49 의 render() 가 네 목록을 이어 붙인다. 모두 비면 결과가 빈 문자열이다. 비교하지 않는 아홉 요소가 달라져도 같은 결과다.
다만 지금 그 보고서를 만드는 프로덕션 코드는 없다. MongoIndexDiffEngine 이 main 에 나오는 줄이 :23 의 클래스 선언 하나뿐이고, 그 엔진의 compare 를 부르는 것은 MongoIndexDiffEngineTest:30·:42·:49·:61 네 줄이다.
원문과 갈리는 자리
원문은 비교되는 것이 keySignature 와 unique 둘이고 hidden 이 한 방향이라고 적는다. 그 판정은 그대로다. 다만 한 줄에서 두 값을 함께 읽는 줄로 세면 :55 를 포함해 셋이다.
여기에 더한 것은 둘이다. 하나는 뷰에 무엇이 없는지를 이름으로 전부 맞춰 본 결과다 — 매니페스트 열넷 가운데 뷰에 이름이 있는 것은 넷이고 keys 는 keySignature 로 형태를 바꿔 들어가므로 감지 불가가 되는 것은 아홉이다. 다른 하나는 그 엔진이 아직 프로덕션에서 불리지 않는다는 것이다.
확인하지 못한 것
선언과 서버 상태를 실제로 넣어 빈 보고서가 나오는 것을 프로브로 보이지 않았다.
이 보고서가 어느 CI 단계의 산출물이 되는지 워크플로 파일을 훑지 않았다.
keySignature 의 생성 규칙을 열어 보지 않았다. 키 순서가 그 서명에 담기는지 확인하지 않았다.