feat: 공식 문서 근거자료, 브랜치 기능 문서 작성
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: AWS — Amazon S3 multipart upload limits
|
||||
source_type: official-doc
|
||||
url: https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-large-object-transfer-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, transfer, upload, multipart, object-storage, aws-s3]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# AWS — Amazon S3 multipart upload limits
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-large-object-transfer-contract]]` | `TRANSFER_PART_SIZE_BYTES` 의 **하한 검증**이 필요한 이유(`D8`), part 수 상한이 최대 전송 크기를 결정한다는 사실 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.aws.amazon.com/AmazonS3/latest/userguide/qfacts.html
|
||||
- 보조 URL:
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: Amazon Web Services (Amazon S3 User Guide)
|
||||
- 발행일: (문서 지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
branch 가 part size default 를 8 MiB, 병렬도를 3 으로 적어 두었는데 **"측정값이 아니라 초기 default"** 라고만 밝혀 두었다. 문제는 part size 가 취향의 문제가 아니라 **vendor 가 강제하는 하한**이 있는 값이라는 점이다. 하한 아래로 설정하면 전송이 마지막 part 를 제외한 전 구간에서 거부된다. vendor 를 아직 고르지 않았더라도(`FE-Q-012`) 대표적인 구현 하나의 실제 수치를 확보해야 "하한 검증을 계약에 넣어야 한다" 를 근거 있게 말할 수 있다.
|
||||
|
||||
> ⚠️ **vendor 선택이 아니다.** 이 문서는 Amazon S3 의 제약을 증명할 뿐이며, 이 프로젝트가 S3 를 쓴다는 결정은 존재하지 않는다(`FE-Q-012` 미해소). 다른 vendor 의 수치는 다를 수 있다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Amazon S3 multipart upload limits] "In general, when your object size reaches 100 MB, you should consider using multipart uploads instead of uploading the object in a single operation."
|
||||
|
||||
> [§Amazon S3 multipart upload limits] "There is no minimum size limit on the last part of your multipart upload."
|
||||
|
||||
> [§표 — Maximum object size] "48.8 TiB"
|
||||
|
||||
> [§표 — Maximum number of parts per upload] "10,000"
|
||||
|
||||
> [§표 — Part numbers] "1 to 10,000 (inclusive)"
|
||||
|
||||
> [§표 — Part size] "5 MiB to 5 GiB. There is no minimum size limit on the last part of your multipart upload."
|
||||
|
||||
> [§표 — Maximum number of parts returned for a list parts request] "1000"
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | S3 multipart 의 part size 는 **5 MiB 이상 5 GiB 이하**여야 한다 | [§표 — Part size] "5 MiB to 5 GiB." | `official-reference` | Amazon S3 | 다른 object storage 의 하한이 같다는 것 |
|
||||
| C2 | 마지막 part 에는 최소 크기 제한이 없다 | [§limits] "There is no minimum size limit on the last part of your multipart upload." | `official-reference` | Amazon S3 | 마지막 part 를 특별 취급하지 않아도 된다는 뜻 — 오히려 분할 로직이 이 예외를 알아야 한다 |
|
||||
| C3 | upload 당 part 수는 최대 **10,000** 이며 part number 는 1~10,000 이다 | [§표] "Maximum number of parts per upload / 10,000" | `official-reference` | Amazon S3 | 10,000 을 넘는 분할이 다른 vendor 에서도 불가능하다는 것 |
|
||||
| C4 | 단일 객체 최대 크기는 48.8 TiB 다 | [§표 — Maximum object size] "48.8 TiB" | `official-reference` | Amazon S3 | 브라우저가 그 크기를 다룰 수 있다는 것 |
|
||||
| C5 | list parts 요청 1회가 돌려주는 part 는 최대 1000 개다 | [§표] "Maximum number of parts returned for a list parts request / 1000" | `official-reference` | S3 API 소비자 | 프론트가 list parts 를 직접 호출한다는 것 — presigned 방식에서는 backend 책임일 수 있다 |
|
||||
| C6 | 100 MB 부터 multipart 사용을 고려하도록 권고한다 | [§limits] "when your object size reaches 100 MB, you should consider using multipart uploads" | `official-reference` | S3 사용자 일반 | 100 MB 미만에서 multipart 가 금지된다는 것 — 권고이지 제약이 아님 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`~`C5`: S3 의 part size 범위·part 수 상한·객체 크기 상한이라는 **하드 제약**의 존재와 실제 수치
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 이 프로젝트가 S3 를 쓴다는 것 (`FE-Q-012` 미해소)
|
||||
- part size 8 MiB 나 병렬도 3 이 **최적**이라는 것 — 이 문서는 성능이 아니라 허용 범위만 말한다
|
||||
- 브라우저에서 10,000 part 를 병렬 관리하는 것이 현실적이라는 것
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 실제 선택 vendor 의 part size 하한 (`FE-Q-012`) — 그 값이 `TRANSFER_PART_SIZE_BYTES` 검증의 기준이 된다
|
||||
- part size × part 수 상한으로 계산되는 최대 전송 크기를 제품이 수용하는지
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C1`+`C3` 을 곱하면 **고정 part size 가 최대 전송 크기를 결정**한다. 8 MiB × 10,000 ≈ 80 GB. 이 계산은 우리 도출이며 AWS 가 그렇게 적은 것이 아니다. 그 이상을 전송하려면 part size 를 키우거나 분할 전략을 바꿔야 한다.
|
||||
- 인용 2 해석 후보 (미검증): `C1` 의 하한 때문에 `TRANSFER_PART_SIZE_BYTES` 는 자유값이 아니라 **검증 대상 계약값**이다. 8 MiB 는 S3 하한을 넘지만, 하한을 넘는다는 사실 자체를 부팅 시 검사하지 않으면 vendor 를 바꾸는 순간 조용히 깨진다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `mpuoverview` (multipart upload 개요), presigned URL 만료 정책 문서
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/mdn-http-range-fetch-transfer]]`, `[[raw/official-docs/mdn-storage-quotas-eviction-persistence]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: official-doc / MongoDB Change Streams — Availability, Resume Tokens, Invalidate Events
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/changeStreams/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, change-stream]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# MongoDB Change Streams — Availability, Resume Tokens, Invalidate Events
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
- `official-doc` — MongoDB 공식 Database Manual (Server 8.3 current)
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | "change stream 은 replica set / sharded cluster 요건 검증을 통과할 때만 활성화하고, resume token 을 checkpoint 저장소에 영속화하며, oplog window 를 감시한다" 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/changeStreams/
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: MongoDB, Inc. (공식 Database Manual)
|
||||
- 발행일: 확인 불가 (지속 갱신 레퍼런스 문서, 페이지 상단 버전 표기: Database Manual 8.3 (Current))
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` 브랜치가 change stream 활성화 조건(배포 형태·storage engine·read concern majority 요건), resume token 기반 checkpoint 설계(resumeAfter/startAfter/startAtOperationTime), oplog window 부족 시 resume 실패, invalidate event 로 인한 stream 종료 조건을 결정하기 위한 1차 공식 근거.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Availability] "Change streams are available for replica sets and sharded clusters"
|
||||
|
||||
> [§Availability — Storage Engine] "The replica sets and sharded clusters must use the WiredTiger storage engine."
|
||||
|
||||
> [§Availability — Read Concern "majority" Enablement] "Change streams are available regardless of the "majority" read concern support; that is, read concern majority support can be either enabled (default) or disabled to use change streams."
|
||||
|
||||
> [§Resume a Change Stream — resumeAfter] "You can resume a change stream after a specific event by passing a resume token to resumeAfter when opening the cursor."
|
||||
|
||||
> [§Resume a Change Stream — startAfter] "Unlike resumeAfter , startAfter can resume notifications after an invalidate event by creating a new change stream."
|
||||
|
||||
> [§Open a Change Stream — startAtOperationTime] "You can specify a startAtOperationTime to open the cursor at a particular point in time. If the specified starting point is in the past, it must be in the time range of the oplog."
|
||||
|
||||
> [§resumeAfter for Change Streams / startAfter for Change Streams — Important note (oplog window)] "The oplog must have enough history to locate the operation associated with the token or the timestamp, if the timestamp is in the past."
|
||||
|
||||
> [§Open a Change Stream — cursor lifecycle] "The cursor is explicitly closed." / "An invalidate event occurs; for example, a collection drop or rename." / "The connection to the MongoDB deployment closes or times out." / "If the deployment is a sharded cluster, a shard removal may cause an open change stream cursor to close."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-CHANGESTREAM-C1 | Change stream 은 replica set 또는 sharded cluster 배포에서만 사용 가능하며, standalone 배포는 지원하지 않는다 | [§Availability] "Change streams are available for replica sets and sharded clusters" | `official-vendor-doc` | 배포 형태 사전 검증 로직 (standalone 배포에서 change stream 활성화 시도를 거부해야 함) | ca-tmpl/ca-skeleton 의 실제 배포 토폴로지에서 이 검증이 어떻게 구현되어야 하는지는 증명하지 않음 |
|
||||
| MONGO-CHANGESTREAM-C2 | Change stream 을 사용하는 replica set/sharded cluster 는 WiredTiger storage engine 을 사용해야 한다 | [§Availability — Storage Engine] "The replica sets and sharded clusters must use the WiredTiger storage engine." | `official-vendor-doc` | storage engine 사전 조건 검증 | 다른 storage engine(MMAPv1 등) 환경에서의 대체 동작은 다루지 않음 |
|
||||
| MONGO-CHANGESTREAM-C3 | Change stream 사용에 `"majority"` read concern 지원 활성화 여부는 무관하다 (활성화/비활성화 모두 가능) | [§Read Concern "majority" Enablement] "Change streams are available regardless of the "majority" read concern support; that is, read concern majority support can be either enabled (default) or disabled to use change streams." | `official-vendor-doc` | change stream 자체의 가용 조건 판단 | change stream 이 반환하는 이벤트의 durability/일관성 보장 수준 자체를 설명하지 않음 — read concern majority 가 비활성화된 상태에서 change stream 이 반환하는 이벤트가 이후 rollback 될 수 있는지는 이 문장만으로 증명 불가 (별도 확인 필요) |
|
||||
| MONGO-CHANGESTREAM-C4 | `resumeAfter` 는 resume token 을 지정해 특정 이벤트 이후부터 change stream 을 재개하는 옵션이다 | [§resumeAfter for Change Streams] "You can resume a change stream after a specific event by passing a resume token to resumeAfter when opening the cursor." | `official-vendor-doc` | checkpoint 저장소에서 읽은 resume token 으로 정상 재개하는 경로 설계 | invalidate event 이후에도 동작하는지는 이 claim 만으로 보장되지 않음 (C5 참조) |
|
||||
| MONGO-CHANGESTREAM-C5 | `resumeAfter` 와 달리 `startAfter` 는 invalidate event 이후에도 새 change stream 을 시작해 알림을 재개할 수 있다 | [§startAfter for Change Streams] "Unlike resumeAfter , startAfter can resume notifications after an invalidate event by creating a new change stream." | `official-vendor-doc` | invalidate event(컬렉션 drop/rename 등) 발생 후 checkpoint 기반 재개 전략 분기 | `resumeAfter` 로 invalidate event 이후 재개를 시도했을 때의 정확한 에러 형태까지는 이 문장만으로 명시되지 않음 |
|
||||
| MONGO-CHANGESTREAM-C6 | `startAtOperationTime` 은 특정 시점부터 커서를 여는 옵션이며, 과거 시점을 지정하면 그 시점이 oplog 의 시간 범위 안에 있어야 한다 | [§Open a Change Stream] "You can specify a startAtOperationTime to open the cursor at a particular point in time. If the specified starting point is in the past, it must be in the time range of the oplog." | `official-vendor-doc` | 최초 checkpoint 가 없는 상태에서 특정 시점부터 change stream 을 시작하는 경로 | timestamp 가 oplog 범위를 벗어났을 때의 정확한 실패 메시지/예외 타입은 명시하지 않음 |
|
||||
| MONGO-CHANGESTREAM-C7 | resume token 또는 timestamp 로 지정된 operation 을 찾으려면 oplog 가 충분한 history 를 보유하고 있어야 하며, 그렇지 못하면 resume 이 불가능하다 (oplog window 요구) | [§resumeAfter/startAfter for Change Streams — Important] "The oplog must have enough history to locate the operation associated with the token or the timestamp, if the timestamp is in the past." | `official-vendor-doc` | oplog window 감시 알람 설계, checkpoint 지연이 oplog retention 을 초과하지 않도록 하는 운영 기준 근거 | 구체적인 oplog 크기 산정 공식이나 권장 retention 시간(초/일 단위)은 이 문장에 없음 — 별도 Production Recommendations 페이지 확인 필요 (`needs-confirmation`) |
|
||||
| MONGO-CHANGESTREAM-C8 | change stream cursor 는 (a) 명시적 종료, (b) invalidate event(예: 컬렉션 drop 또는 rename) 발생, (c) MongoDB 배포와의 연결 종료/타임아웃, (d) sharded cluster 에서 shard 제거 중 하나가 발생하면 닫힌다 | [§Open a Change Stream] "The cursor is explicitly closed." / "An invalidate event occurs; for example, a collection drop or rename." / "The connection to the MongoDB deployment closes or times out." / "If the deployment is a sharded cluster, a shard removal may cause an open change stream cursor to close." | `official-vendor-doc` | invalidate event 발생 시 stream 종료를 감지하고 checkpoint 재개 전략(`startAfter`)으로 분기하는 에러 핸들링 설계 | 각 종료 사유를 애플리케이션 코드에서 어떻게 구분해 감지하는지(드라이버별 API)는 이 문장에 없음 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGO-CHANGESTREAM-C1`: change stream 은 replica set/sharded cluster 배포에서만 사용 가능하다 (standalone 불가)
|
||||
- `MONGO-CHANGESTREAM-C2`: WiredTiger storage engine 이 필요하다
|
||||
- `MONGO-CHANGESTREAM-C3`: read concern majority 지원 활성화 여부와 무관하게 change stream 사용 가능하다
|
||||
- `MONGO-CHANGESTREAM-C4`~`C6`: resumeAfter/startAfter/startAtOperationTime 세 옵션의 역할과 각각의 적용 조건
|
||||
- `MONGO-CHANGESTREAM-C7`: oplog 가 resume token/timestamp 에 해당하는 이벤트를 이미 롤오버(삭제)했다면 resume 이 실패한다 (oplog window 요구)
|
||||
- `MONGO-CHANGESTREAM-C8`: invalidate event 를 포함한 4가지 조건에서 change stream cursor 가 닫힌다
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 구체적인 oplog 크기 산정 공식이나 권장 retention 시간 (별도 Production Recommendations 페이지 영역)
|
||||
- resume token 을 애플리케이션 checkpoint 저장소(DB 테이블 등)에 영속화하는 구체적 구현 패턴 — 이는 원문에 없는 ca-tmpl/ca-skeleton 측 설계 결정
|
||||
- `resumeAfter` 실패 시 정확한 예외 타입/에러 코드 (드라이버별 상이할 수 있음)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 실제로 배포할 MongoDB 토폴로지(replica set 최소 멤버 수, WiredTiger 기본 여부)가 이 요건을 만족하는지 로컬/dev 환경에서 검증 필요
|
||||
- oplog window 감시 알람의 구체적 임계값(예: checkpoint 지연이 oplog 예상 소진 시간의 몇 %를 넘으면 경보)은 이 자료에 없으므로 별도 설계 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- oplog window 요구(`MONGO-CHANGESTREAM-C7`)는 정성적 서술("must have enough history")만 있고 정량 기준이 없다. Production Recommendations 페이지(원문에서 "Back Limitations Next Production Recommendations" 로 다음 페이지 링크됨)를 별도 raw 자료로 추가 수집할 필요가 있는지는 `/branch-spec` 단계에서 판단.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `Production Recommendations`, `Change Stream Events` (invalidate 이벤트의 정확한 document 스키마)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/write-concern-mongodb-official]], [[raw/official-docs/read-preference-mongodb-official]] (동일 벤더, 인접 결정 영역)
|
||||
- 이 자료를 인용한 wiki 요약: (아직 생성되지 않음)
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: official-doc / Lettuce ClientOptions — autoReconnect, disconnectedBehavior, requestQueueSize
|
||||
source_type: official-doc
|
||||
url: https://redis.github.io/lettuce/advanced-usage/client-options/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, redis, runtime, connection-pool]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Lettuce ClientOptions — autoReconnect, disconnectedBehavior, requestQueueSize
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
- `official-doc` — 공식 레퍼런스 / 표준 / 사양 (Lettuce 프로젝트 공식 GitHub Pages 문서, `redis/lettuce` 리포의 공식 산출물)
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | Redis 연결 런타임에서 `autoReconnect` / `disconnectedBehavior=REJECT_COMMANDS` / 유한한 `requestQueueSize` 를 명시 설정해 Redis 장애가 애플리케이션 heap 고갈로 번지지 않게 한다는 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.github.io/lettuce/advanced-usage/client-options/ (요청받은 `https://redis.github.io/lettuce/advanced-usage/` 는 404 — Lettuce 공식 사이트 내 `Advanced Usage → Client Options` 하위 페이지로 대체)
|
||||
- 보조 URL (command replay / at-least-once 시맨틱): https://redis.github.io/lettuce/advanced-usage/command-execution-reliability/
|
||||
- 아카이브 URL: (미수집 — 필요 시 사용자가 archive.org 스냅샷 지정)
|
||||
- 저자 / 조직: Lettuce 프로젝트 (Redis 공식 Java 클라이언트, `redis/lettuce` GitHub org)
|
||||
- 발행일: (페이지에 명시된 발행일 없음 — living reference doc)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Redis 런타임 role-isolation 브랜치(`feature-redis-runtime-role-isolation-contract`)에서 "Redis 장애 시 애플리케이션 heap 이 커맨드 큐 적체로 고갈되지 않아야 한다"는 요구를 만족하려면 Lettuce `ClientOptions` 의 `autoReconnect`/`disconnectedBehavior`/`requestQueueSize` 기본값과 각 옵션의 의미를 정확히 알아야 한다. 이 문서는 그 결정(명시적 설정 필요성)의 1차 공식 근거다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [Client Options] "Controls auto-reconnect behavior on connections. As soon as a connection gets closed/reset without the intention to close it, the client will try to reconnect, activate the connection and re-issue any queued commands."
|
||||
|
||||
> [Client Options — Disconnected Behavior] "DEFAULT: Accept commands when auto-reconnect is enabled, reject commands when auto-reconnect is disabled." / "ACCEPT_COMMANDS: Accept commands in disconnected state." / "REJECT_COMMANDS: Reject commands in disconnected state."
|
||||
|
||||
> [Client Options — Request Queue Size] "Controls the per-connection request queue size. The command invocation will lead to a RedisException if the queue size is exceeded."
|
||||
|
||||
> [Client Options — Cluster] "Clustered operations use multiple connections. The resulting overall-queue limit is requestQueueSize * ((number of cluster nodes * 2) + 1)."
|
||||
|
||||
> [Command Execution Reliability] "commands may be duplicated but not lost" (at-least-once execution, via retry mechanisms during reconnection; commands are buffered in memory during failures)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| LETTUCE-CO-C1 | `autoReconnect` 기본값은 true 이며, 의도치 않게 연결이 끊기면 클라이언트가 재연결·활성화 후 큐에 쌓인 command 를 재발행(re-issue)한다 | "Controls auto-reconnect behavior on connections. As soon as a connection gets closed/reset without the intention to close it, the client will try to reconnect, activate the connection and re-issue any queued commands." | official-vendor-doc | Lettuce 클라이언트 기본 동작 (버전 명시 없음, 현재 reference 문서 기준) | 이 재발행이 모든 command 타입에 안전(idempotent)함을 증명하지 않음 — 중복 실행 가능성은 별도 검토 필요 |
|
||||
| LETTUCE-CO-C2 | `disconnectedBehavior` 는 DEFAULT(autoReconnect 여부에 종속) / ACCEPT_COMMANDS(항상 버퍼링) / REJECT_COMMANDS(항상 즉시 거부) 3가지 값을 제공한다 | "DEFAULT: Accept commands when auto-reconnect is enabled, reject commands when auto-reconnect is disabled." / "REJECT_COMMANDS: Reject commands in disconnected state." | official-vendor-doc | Lettuce `ClientOptions.disconnectedBehavior` 설정 전반 | REJECT_COMMANDS 를 쓰는 것이 이 프로젝트에 "올바른 선택"이라고 증명하지 않음 — 이는 branch 의 trade-off 결정 |
|
||||
| LETTUCE-CO-C3 | `requestQueueSize` 기본값은 `Integer.MAX_VALUE`(2147483647)이며, per-connection 단위이고 초과 시 `RedisException` 을 던진다 | "Controls the per-connection request queue size. The command invocation will lead to a RedisException if the queue size is exceeded." | official-vendor-doc | Lettuce `ClientOptions.requestQueueSize` 기본값(사실상 무제한에 가까운 상한) | 기본값이 실제로 OOM 을 유발한다는 실측치는 아님 — "사실상 무제한"이라는 설계상 위험만 보여줌, heap 고갈 실증은 이 문서 범위 밖 |
|
||||
| LETTUCE-CO-C4 | Redis Cluster 모드에서는 여러 커넥션을 사용하므로 전체 큐 상한이 `requestQueueSize * ((number of cluster nodes * 2) + 1)` 공식으로 배수 증가한다 | "Clustered operations use multiple connections. The resulting overall-queue limit is requestQueueSize * ((number of cluster nodes * 2) + 1)." | official-vendor-doc | Lettuce cluster client 의 큐 상한 계산 | 이 프로젝트가 실제 cluster 배포인지, 이 공식이 모든 Lettuce 버전에서 동일한지는 이 문서만으로 확정 불가 |
|
||||
| LETTUCE-CO-C5 | auto-reconnect 활성 상태의 기본 동작(at-least-once)은 재연결 시 buffered command 를 재시도하므로 "손실은 없지만 중복될 수 있다" | "commands may be duplicated but not lost" | official-vendor-doc | auto-reconnect 활성 상태의 command 재전송(replay) 시맨틱 | Lettuce 6.6+ 의 "replay filter" 기능 존재 여부·설정 방법은 fetch 결과에서 verbatim 인용으로 확인되지 않아 이 문서에서 claim 화하지 않음(§Usage Boundaries 참조) |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `LETTUCE-CO-C1`: `autoReconnect` 기본값(true)과 재연결 시 큐에 쌓인 command 재발행 동작
|
||||
- `LETTUCE-CO-C2`: `disconnectedBehavior` 3개 값의 정의 (DEFAULT/ACCEPT_COMMANDS/REJECT_COMMANDS)
|
||||
- `LETTUCE-CO-C3`: `requestQueueSize` 기본값이 `Integer.MAX_VALUE` 이고 per-connection 단위이며 초과 시 예외를 던진다는 사실
|
||||
- `LETTUCE-CO-C4`: cluster 모드에서 전체 큐 상한이 노드 수에 비례해 배수 증가한다는 공식
|
||||
- `LETTUCE-CO-C5`: auto-reconnect 활성 상태에서 command 가 손실 없이(다만 중복 가능하게) 재시도된다는 at-least-once 시맨틱
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `requestQueueSize` 기본값(사실상 무제한)이 실제로 이 프로젝트에서 heap 고갈을 일으킨다는 실측 근거 — 이 문서는 설계상 위험(상한이 매우 큼)만 보여줄 뿐, 이 프로젝트의 부하·메모리 프로파일에서의 결과는 별도 검증 필요
|
||||
- `disconnectedBehavior=REJECT_COMMANDS` 선택이 이 프로젝트에 "공식 best practice"라는 것 — 공식 문서는 옵션과 기본값만 설명하며 어떤 값을 골라야 하는지 권고하지 않음. 선택 자체는 branch 의 trade-off 결정
|
||||
- Lettuce 6.6+ "command replay filter" 기능의 존재·설정 방법 — WebFetch 결과에서 이 부분은 verbatim 인용으로 확인되지 않아 claim 화하지 않았음 (fabrication 방지, §메모 참조)
|
||||
- Cluster topology refresh (periodic/adaptive) 설정 — 요청받은 항목이었으나 `advanced-usage/client-options/` 와 `advanced-usage/command-execution-reliability/` 두 페이지 어디에도 verbatim 으로 존재하지 않아 이 문서에 포함하지 않음. 필요 시 별도 raw-source 조사 대상 (아래 §메모 참조)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 의 실제 `LettuceClientConfiguration`/`ClientOptions` 빌더 설정에서 `autoReconnect(true)` + `disconnectedBehavior(REJECT_COMMANDS)` + 명시적 `requestQueueSize` 상한이 role(cache/coordination/session) 별로 실제 반영되는지 코드/테스트 검증
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- WebFetch 처리 특성상 결과가 "요약 + 원문 발췌(따옴표)" 혼합 형태로 반환됨. 이 문서의 `## 핵심 인용`/`## Claims Extracted` 표에는 **따옴표로 감싸져 있고 self-grep 으로 확인된 문장만** 채택했다. "Lettuce 6.6+ replay filter" 서술은 fetch 결과에서 따옴표 없이(모델의 재서술로) 나타나 verbatim 확인 대상에서 제외 — claim 화하지 않음.
|
||||
- Cluster topology refresh (periodic/adaptive refresh) 설정은 별도 페이지(추정: High-Availability and Sharding 섹션 하위)에 있을 가능성이 높음. 이 branch 에서 topology refresh 근거가 필요하면 별도 dispatch(URL: 해당 페이지 확정 후) 필요.
|
||||
- 요청받은 원 URL `https://redis.github.io/lettuce/advanced-usage/` 는 404. 실제 유효 페이지는 `https://redis.github.io/lettuce/advanced-usage/client-options/` (frontmatter `url:` 에 반영).
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Redis Cluster topology refresh 전용 자료는 후속 수집 대상)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -3,7 +3,7 @@ title: "official-doc / Spring Boot — Externalized Configuration (Features Refe
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-boot/reference/features/external-config.html
|
||||
archive_url:
|
||||
related_branches: [feature-env-driven-runtime-configuration]
|
||||
related_branches: [feature-env-driven-runtime-configuration, feature-capability-provider-selection-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, application, spring-boot, bean-validation, externalized-config, profile-activation]
|
||||
created: 2026-06-05
|
||||
@@ -19,6 +19,7 @@ created: 2026-06-05
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-env-driven-runtime-configuration]] | D4: Duration `30s`/`PT30S` 양쪽 허용 확인 (우리 규약이 `30s` 1택을 선택해도 됨을 Spring 공식 근거로 확인) / D6: `spring.profiles.active` 및 relaxed binding 규칙(`SPRING_PROFILES_ACTIVE` 도출 메커니즘) Spring Boot native 공식 근거 / D10: `@ConfigurationProperties + @Validated` JSR-303 startup validation fail-fast 공식 근거 |
|
||||
| [[raw/branch-notes/feature-capability-provider-selection-contract]] | capability 활성화 property 를 단일 prefix 로 통일하는 결정의 근거 — relaxed binding 이 dash/capitalized 환경변수를 동일 property 로 취급하는 메커니즘, placeholder canonical form(kebab-case) 권장, `@ConfigurationProperties` prefix→bean 명명 규칙, type-safe binding 이점, OS 환경변수가 `application.yml`(config data)보다 나중에 평가되는(우선하는) PropertySource 순서 — 현재 `ca-skeleton.*`/`app.*` 이중 prefix 와 `APP_*` 환경변수 registry 통일 필요성의 공식 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
@@ -46,6 +47,22 @@ created: 2026-06-05
|
||||
|
||||
> [§features.external-config.typesafe-configuration-properties.validation, line 4897–4898] "Spring Boot attempts to validate `@ConfigurationProperties` classes whenever they are annotated with Spring's `@Validated` annotation. You can use JSR-303 `jakarta.validation` constraint annotations directly on your configuration class."
|
||||
|
||||
### 추가 인용 (2026-07-28, `feature-capability-provider-selection-contract` 근거 수집, 6개)
|
||||
|
||||
> 셀프그렙 검증 원본: `/tmp/source-fetch-20260728-155300.txt` (WebFetch 결과 저장, self-grep 통과)
|
||||
|
||||
> [§Relaxed Binding — 환경변수 특수문자 치환, self-grep L7] "If you use environment variables rather than system properties, most operating systems disallow period-separated key names, but you can use underscores instead (for example, `SPRING_CONFIG_NAME` instead of `spring.config.name`)."
|
||||
|
||||
> [§Relaxed Binding — 일반 규칙 + 예시, self-grep L11] "Spring Boot uses some relaxed rules for binding Environment properties to @ConfigurationProperties beans, so there does not need to be an exact match between the Environment property name and the bean property name. Common examples where this is useful include dash-separated environment properties (for example, `context-path` binds to `contextPath`), and capitalized environment properties (for example, `PORT` binds to `port`)."
|
||||
|
||||
> [§Placeholder canonical form 권장, self-grep L17] "You should always refer to property names in the placeholder using their canonical form (kebab-case using only lowercase letters). This will allow Spring Boot to use the same logic as it does when relaxed binding @ConfigurationProperties."
|
||||
|
||||
> [§@ConfigurationProperties bean 명명 규칙 (prefix), self-grep L29] "When the @ConfigurationProperties bean is registered using configuration property scanning or through @EnableConfigurationProperties, the bean has a conventional name: `<prefix>-<fqn>`, where `<prefix>` is the environment key prefix specified in the @ConfigurationProperties annotation and `<fqn>` is the fully qualified name of the bean."
|
||||
|
||||
> [§Type-safe Configuration Properties 이점, self-grep L33] "Using the @Value(\"${property}\") annotation to inject configuration properties can sometimes be cumbersome, especially if you are working with multiple properties or your data is hierarchical in nature. Spring Boot provides an alternative method of working with properties that lets strongly typed beans govern and validate the configuration of your application."
|
||||
|
||||
> [§PropertySource 순서 목록 (item 3, 5) + 순서 방향 확인 문장, self-grep L45/L47/L61] "3. Config data (such as `application.properties` files)" ... "5. OS environment variables" ... "As mentioned previously, command line properties always take precedence over file-based property sources."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
@@ -57,6 +74,12 @@ created: 2026-06-05
|
||||
| SPRING-EXTCONFIG-C3 | Spring Framework `DataSize` 프로퍼티는 `long`(기본 bytes)과 단순 suffix(`10MB`) 두 형식을 허용한다 | [§conversion.data-sizes, l.4733] "To specify a buffer size of 10 megabytes, `10` and `10MB` are equivalent." | `official-vendor-doc` | `@ConfigurationProperties` 바인딩 `DataSize` 필드 | `DataSize` 가 ISO-8601 형식을 지원하지 않음을 증명하지 않음 (Duration 과 달리 ISO-8601 언급 없음) |
|
||||
| SPRING-EXTCONFIG-C4 | Spring Boot relaxed binding 은 프로퍼티 이름의 점(`.`)을 언더스코어(`_`)로, 대시(`-`)를 제거하고, 대문자로 변환하여 OS 환경 변수 이름에 매핑한다 | [§relaxed-binding.environment-variables, l.3966] "For example, the configuration property `spring.main.log-startup-info` would be an environment variable named `SPRING_MAIN_LOGSTARTUPINFO`." | `official-vendor-doc` | Spring Boot 환경 변수 바인딩 전체 (`systemEnvironment` property source 및 `-systemEnvironment` suffix 를 가진 추가 property source) | `SPRING_PROFILES_ACTIVE` 라는 이름이 문서에 명시적으로 나열되지는 않음 — 규칙 적용의 당연한 귀결 |
|
||||
| SPRING-EXTCONFIG-C5 | Spring Boot 는 `@Validated` 애노테이션이 붙은 `@ConfigurationProperties` 클래스를 자동으로 검증하며, `jakarta.validation` JSR-303 제약 애노테이션을 필드에 직접 사용할 수 있다 | [§validation, l.4897–4898] "Spring Boot attempts to validate `@ConfigurationProperties` classes whenever they are annotated with Spring's `@Validated` annotation. You can use JSR-303 `jakarta.validation` constraint annotations directly on your configuration class." | `official-vendor-doc` | Spring Boot 의 `@ConfigurationProperties` + `@Validated` 조합 | 검증 실패 시 startup 이 fail-fast 로 중단된다는 명시적 문구는 이 문서에 없음 — Spring Bean 초기화 실패로 컨텍스트 로드 실패가 발생함은 Spring Framework 일반 동작 |
|
||||
| SPRING-EXTCONFIG-C6 | Spring Boot relaxed binding 은 dash-separated 환경변수(`context-path`→`contextPath`)와 capitalized 환경변수(`PORT`→`port`)를 각각 bean property 이름으로 매핑한다 | [§Relaxed Binding, self-grep L11] "Common examples where this is useful include dash-separated environment properties (for example, `context-path` binds to `contextPath`), and capitalized environment properties (for example, `PORT` binds to `port`)." | `official-vendor-doc` | `@ConfigurationProperties` 바인딩 전체 (환경변수 소스) | 어떤 표기가 "정식" 표기인지는 말하지 않음 — canonical form 권장은 별도 claim(C8) |
|
||||
| SPRING-EXTCONFIG-C7 | 대부분의 OS 는 마침표(`.`)로 구분된 키 이름을 환경변수로 쓸 수 없어서, Spring Boot 는 언더스코어(`_`) 치환을 허용한다(예: `spring.config.name` → `SPRING_CONFIG_NAME`) | [§Relaxed Binding — 환경변수 특수문자 치환, self-grep L7] "...most operating systems disallow period-separated key names, but you can use underscores instead (for example, `SPRING_CONFIG_NAME` instead of `spring.config.name`)." | `official-vendor-doc` | 환경변수로 property 를 지정하는 모든 경우 | 대문자화가 반드시 함께 요구되는지는 이 문장만으로는 규정하지 않음(대문자 규칙은 C6/C9 예시들이 뒷받침) |
|
||||
| SPRING-EXTCONFIG-C8 | `${...}` placeholder 에서 property 이름을 참조할 때는 항상 canonical form(소문자 kebab-case)을 쓰는 것이 권장된다 — 그래야 relaxed binding 과 동일 로직이 적용된다 | [§Placeholder canonical form 권장, self-grep L17] "You should always refer to property names in the placeholder using their canonical form (kebab-case using only lowercase letters)." | `official-vendor-doc` | `${demo.item-price}` 류의 placeholder 참조 표기 | property 소스 파일(`application.yml`) 자체의 키 표기 관례까지 강제한다고 명시하진 않음 — placeholder 참조 표기에 대한 권장 |
|
||||
| SPRING-EXTCONFIG-C9 | `@ConfigurationProperties` bean 이 configuration property scanning 또는 `@EnableConfigurationProperties` 로 등록되면, bean 이름은 `<prefix>-<fqn>` 관례를 따르며 `<prefix>` 는 애노테이션에 지정된 environment key prefix 다 | [§@ConfigurationProperties bean 명명 규칙, self-grep L29] "...the bean has a conventional name: `<prefix>-<fqn>`, where `<prefix>` is the environment key prefix specified in the @ConfigurationProperties annotation..." | `official-vendor-doc` | scanning 또는 `@EnableConfigurationProperties` 로 등록된 `@ConfigurationProperties` bean | 이 문장은 bean 이름 관례를 설명할 뿐, "단일 prefix 를 써야 한다"는 정책을 직접 규정하지 않음 — prefix 는 클래스마다 자유롭게 지정 가능하다는 사실만 확인됨 |
|
||||
| SPRING-EXTCONFIG-C10 | `@Value("${property}")` 로 설정을 주입하는 방식은 다수 property 나 계층 구조 데이터를 다룰 때 번거로울 수 있으며, Spring Boot 는 strongly-typed bean 이 설정을 관리·검증하게 하는 대안(type-safe `@ConfigurationProperties`)을 제공한다 | [§Type-safe Configuration Properties 이점, self-grep L33] "Using the @Value(\"${property}\") annotation to inject configuration properties can sometimes be cumbersome... Spring Boot provides an alternative method of working with properties that lets strongly typed beans govern and validate the configuration of your application." | `official-vendor-doc` | `@Value` vs `@ConfigurationProperties` 선택 비교 | 특정 property 개수·계층 깊이 기준으로 `@ConfigurationProperties` 전환을 강제하지 않음 — "cumbersome 할 수 있다"는 정성적 진술 |
|
||||
| SPRING-EXTCONFIG-C11 | 공식 문서는 PropertySource 를 15개 항목의 순서 목록으로 제공하며, Config data(`application.properties` 류 파일)는 3번째, OS 환경변수는 5번째로 나열된다. 별도로 커맨드라인 인자(11번째)가 파일 기반 소스보다 항상 우선한다고 명시하여, 목록에서 더 나중에 나열된 소스가 우선한다는 방향을 확인해준다 | [§PropertySource 순서, self-grep L45/L47/L61] "3. Config data (such as `application.properties` files)" ... "5. OS environment variables" ... "As mentioned previously, command line properties always take precedence over file-based property sources." | `official-vendor-doc` | Spring Boot 의 전체 PropertySource 우선순위 체계 (config data 파일 vs OS 환경변수 포함) | "OS 환경변수가 config data 파일보다 우선한다"는 문장이 이 발췌에 축자적으로 존재하지는 않음 — 목록 순서 + 커맨드라인/파일 우선순위 진술의 결합 추론. `SPRING_PROFILES_ACTIVE` 같은 특정 키의 실제 override 동작은 별도 통합 테스트 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
@@ -66,14 +89,23 @@ created: 2026-06-05
|
||||
- `SPRING-EXTCONFIG-C3`: `DataSize` 가 `10MB` suffix 형식을 수용 (D4 DataSize 근거)
|
||||
- `SPRING-EXTCONFIG-C4`: `spring.profiles.active` 는 relaxed binding 규칙에 의해 `SPRING_PROFILES_ACTIVE` 로 매핑됨 (D6 메커니즘 근거)
|
||||
- `SPRING-EXTCONFIG-C5`: `@ConfigurationProperties + @Validated` 는 공식 Spring Boot API (D10 공식 근거)
|
||||
- `SPRING-EXTCONFIG-C6`: relaxed binding 이 dash/capitalized 환경변수 표기를 property 이름으로 매핑 (capability-provider-selection 근거)
|
||||
- `SPRING-EXTCONFIG-C7`: 마침표 구분 키를 언더스코어 환경변수로 대체 허용
|
||||
- `SPRING-EXTCONFIG-C8`: placeholder 참조는 canonical form(kebab-case) 사용을 권장
|
||||
- `SPRING-EXTCONFIG-C9`: `@ConfigurationProperties` bean 이름이 `<prefix>-<fqn>` 관례를 따름 — prefix 는 애노테이션에 클래스마다 지정하는 값
|
||||
- `SPRING-EXTCONFIG-C10`: `@Value` 대비 type-safe `@ConfigurationProperties` 의 이점(다수/계층적 property 관리·검증)
|
||||
- `SPRING-EXTCONFIG-C11`: PropertySource 순서 목록 + "커맨드라인이 파일 기반보다 우선" 진술로 뒷받침되는 우선순위 방향
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `30s` 형식이 `PT30S` 보다 더 권장됨 (C1은 "동등하다"고만 말함 — 규약 선택은 팀 결정)
|
||||
- `SPRING_PROFILES_ACTIVE` 와 `APP_PROFILE` 이 불일치할 때 startup 이 자동으로 fail-fast 되는 동작 (별도 `EnvironmentPostProcessor` 구현 필요)
|
||||
- `@Validated` 실패가 반드시 startup 중단을 일으킨다는 명시 (Spring context 초기화 실패가 JVM exit 을 일으키는 것은 Spring Boot 런처 일반 동작이나 이 문서에 명시 없음)
|
||||
- `SPRING_PROFILES_ACTIVE` 라는 정확한 환경 변수 이름이 문서에 명시적으로 나타남 (규칙 귀결)
|
||||
- **"capability 활성화 property 를 단일 prefix 로 통일해야 한다"는 정책을 이 문서가 직접 규정하지 않음** — C9 는 prefix 가 클래스마다 자유롭게 지정 가능한 애노테이션 속성임을 보여줄 뿐, "prefix 를 하나로 통일하라"는 권고는 없음. 단일 prefix 결정은 `ca-skeleton.*`/`app.*` 이중 prefix 가 relaxed binding·canonical form 규칙 위에서 일관성을 해친다는 branch 자체의 판단이며, 이 raw 자료는 그 판단이 딛고 서는 메커니즘(관례상 규칙 자체는 일관 표기를 전제로 설계됨)만 뒷받침한다
|
||||
- `SPRING_PROFILES_ACTIVE`/`APP_*` 같은 특정 키가 config data 보다 실제로 override 하는지의 런타임 동작 확인(C11 은 목록 순서 + 별도 진술의 결합 추론이며, 이 문서 발췌에 "환경변수가 config data 보다 우선한다"는 축자 문장은 없음)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `APP_PROFILE` 과 `SPRING_PROFILES_ACTIVE` 불일치 시 startup fail 동작 — `EnvironmentPostProcessor` 또는 `@PostConstruct` validator 구현 후 통합 테스트로 검증
|
||||
- `@Validated` 실패 시 Spring Boot launcher 가 exit code 1 로 종료되는지 — contract test `StartupFailFastTest` 로 검증
|
||||
- `ca-skeleton.*`/`app.*` 이중 prefix를 `APP_*` 단일 registry 로 통일할 때, relaxed binding 이 실제로 기존 `ca-skeleton.*` YAML 키와 새 `APP_*` 환경변수를 충돌 없이 매핑하는지 — 통합 테스트로 검증 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
@@ -82,6 +114,12 @@ created: 2026-06-05
|
||||
- D10 resolution: C5 는 `@Validated` API 지원의 공식 근거. fail-fast startup 동작은 Spring framework 컨텍스트 로드 실패 일반 동작으로 추가 raw 없이 합리적으로 추론 가능 — 단, 추론이므로 claim 에는 넣지 않음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: property precedence priority list (페이지 상단 §1), `@ConfigurationPropertiesScan`, constructor binding with `@DefaultValue`.
|
||||
|
||||
### 2026-07-28 추가 메모 (`feature-capability-provider-selection-contract` 근거 수집)
|
||||
|
||||
- capability 활성화 property 단일 prefix 통일 결정: relaxed binding(C6/C7)과 canonical form 권장(C8)은 "표기가 달라도 같은 property 로 매핑된다"는 사실을 확인해주지만, 반대로 이는 **현재 `ca-skeleton.*` 와 `app.*` 두 prefix 가 서로 다른 property 트리로 남아 relaxed binding 으로도 통합되지 않는다**는 점을 부각시킨다 — relaxed binding 은 표기 변형(대소문자/구분자)만 흡수하고, 서로 다른 prefix 자체를 통합하지는 않음. C9(prefix→bean 명명 규칙)는 prefix 가 클래스 단위로 자유롭게 지정된다는 사실만 보여주므로 "두 prefix 혼재가 Spring 표준 관행이 아니다"라는 직접 근거는 아니다 — 단일 prefix 채택은 이 문서가 아니라 팀의 일관성 정책 판단.
|
||||
- **버전 드리프트 주의**: 2026-06-05 원 메모(D6 resolution)는 "OS env = 우선순위 10번째"라고 기록했으나, 2026-07-28 재확인한 동일 URL 발췌(Spring Boot 4.1.0 기준, javadoc 링크로 확인)에서는 OS 환경변수가 15개 항목 중 **5번째**, config data(`application.properties`)가 **3번째**로 나열됨. 항목 개수·순서가 문서 버전에 따라 달라졌을 가능성이 있음 — 두 메모 모두 "OS env 가 config data 파일보다 나중(=우선)"이라는 **상대적 방향**은 일치하나, 절대 순번은 재검증 시점의 문서 버전에 의존하므로 절대 순번을 claim 근거로 인용하지 말 것.
|
||||
- 추가로 봐야 할 것: `ca-skeleton.*`/`app.*` 두 prefix 를 하나의 `APP_*` registry 로 합칠 때, 기존 YAML 계층 구조(`ca-skeleton.foo.bar`)를 `app.foo.bar` 로 옮기는 마이그레이션 경로가 relaxed binding 만으로 자동 해결되지 않음(수동 rename 필요) — 이 문서 범위 밖.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: GraphQL over HTTP (draft) — 상태 코드와 errors 처리
|
||||
source_type: official-doc
|
||||
url: https://graphql.github.io/graphql-over-http/draft/
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-multi-protocol-api-transport-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, graphql, http, api, protocol]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# GraphQL over HTTP (draft) — 상태 코드와 errors 처리
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
> ⚠️ **draft 문서다.** 확정 표준이 아니며 내용이 바뀔 수 있다. 아래 claim 을 `official-reference` 로 쓰되 "draft" 라는 사실을 함께 인용해야 한다.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-multi-protocol-api-transport-contract]]` | GraphQL 성공 판정을 상태 코드가 아니라 본문으로 하는 근거(`D4`), `errors` 가 있는 응답을 실패로 정규화하는 것이 **규격과 다른 우리 정책**임을 밝히는 근거(`D3`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://graphql.github.io/graphql-over-http/draft/
|
||||
- 보조 URL: https://spec.graphql.org/draft/#sec-Handling-Field-Errors
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: GraphQL Foundation (GraphQL over HTTP working draft)
|
||||
- 발행일: (draft, 지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
branch 가 "`200 OK` + `errors[]` 를 실패로 정규화한다" 고 적어 두었는데 두 가지를 확인해야 했다. (a) 그 응답이 정말 `200` 으로 오는가, (b) 규격이 그 상황을 어떻게 부르는가. 결과는 둘 다 예상과 달랐다. 데이터와 errors 가 함께 있으면 규격은 `294` 를 권고하고, 규격 자신은 그 상황을 **"successful execution"** 이라고 부른다. 즉 우리 결정은 규격을 따르는 것이 아니라 **의도적으로 다르게 가는 것**이며, 그 사실을 문서에 밝히지 않으면 다음 사람이 규격 준수로 오해한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§6.4] "If the GraphQL response contains the data entry and it is not null, then the server MUST reply with a `2xx` status code."
|
||||
|
||||
> [§6.4] "If the GraphQL response contains the data entry and does not contain the errors entry, then the server SHOULD reply with a `200` status code."
|
||||
|
||||
> [§6.4] "If the GraphQL response contains both the data entry (even if it is null) and the errors entry, then the server SHOULD reply with a `294` status code."
|
||||
|
||||
> [§6.4] "If the GraphQL response does not contain the data entry then the server MUST reply with an appropriate `4xx` or `5xx` status code:"
|
||||
|
||||
> [§6.4] "Clients should process a response using the `application/graphql-response+json` media type as a well-formed GraphQL response independent of the HTTP status code."
|
||||
|
||||
> [§6.4 Note] "There are no circumstances where the GraphQL specification allows for a response having data as null without errors being present."
|
||||
|
||||
> [§6.4.1] "The GraphQL specification differentiates field errors from request errors and refers to the situation wherein a GraphQL field error occurs as a partial response; it still indicates successful execution."
|
||||
|
||||
> [§6.1] "If the `Accept` header does not indicate support for one of the server's preferred media types but does indicate support for `application/json` ... any response that produces a `2xx` status code should replace the `Content-Type` header with `Content-Type: application/json`."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 데이터와 `errors` 가 함께 있는 응답에 규격이 권고하는 상태 코드는 `200` 이 아니라 **`294`** 다 | [§6.4] "then the server SHOULD reply with a `294` status code." | `official-reference` (draft) | `application/graphql-response+json` | 모든 서버가 `294` 를 보낸다는 것 — SHOULD 이며 draft 다 |
|
||||
| C2 | data 가 있으면 상태 코드는 반드시 `2xx` 다 | [§6.4] "then the server MUST reply with a `2xx` status code." | `official-reference` (draft) | data 가 non-null 인 응답 | `2xx` 이면 성공이라는 것 |
|
||||
| C3 | 클라이언트는 응답을 **HTTP 상태 코드와 무관하게** 잘 형성된 GraphQL 응답으로 처리해야 한다 | [§6.4] "independent of the HTTP status code" | `official-reference` (draft) | `application/graphql-response+json` 소비자 | 상태 코드를 아예 무시해도 된다는 것 |
|
||||
| C4 | 규격은 field error 상황을 **partial response 이며 "successful execution"** 이라고 부른다 | [§6.4.1] "it still indicates successful execution." | `official-reference` (draft) | field error | 애플리케이션이 이를 성공으로 취급해야 한다는 것 — 규격의 명명일 뿐 |
|
||||
| C5 | data 가 없으면 `4xx` 또는 `5xx` 여야 한다 | [§6.4] "MUST reply with an appropriate `4xx` or `5xx` status code" | `official-reference` (draft) | request error | 어떤 코드인지의 세부 |
|
||||
| C6 | `data: null` 이면서 `errors` 가 없는 응답은 규격상 존재할 수 없다 | [§6.4 Note] "There are no circumstances where the GraphQL specification allows for a response having data as null without errors being present." | `official-reference` (draft) | 모든 GraphQL 응답 | 서버가 그 규칙을 실제로 지킨다는 것 |
|
||||
| C7 | 구식 클라이언트가 `application/json` 만 받으면 `2xx` 응답의 Content-Type 이 `application/json` 으로 바뀐다 | [§6.1] "should replace the `Content-Type` header with `Content-Type: application/json`" | `official-reference` (draft) | legacy Accept 협상 | 그 경우에도 상태 코드가 같다는 것 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`~`C3`: GraphQL 성공 판정에 상태 코드만 쓰면 안 되며, `200` 만 검사하는 구현은 `294` 를 놓친다
|
||||
- `C4`: 규격은 부분 응답을 "성공" 으로 분류한다
|
||||
- `C5`·`C6`: data 유무가 request error 와 field error 를 가르는 축이다
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `errors` 가 있는 응답을 **애플리케이션이 실패로 취급해야 한다**는 것 — 오히려 `C4` 는 반대 방향이다. 실패 취급은 우리 정책이다
|
||||
- 서버가 `294` 를 실제로 보낸다는 것 (SHOULD + draft)
|
||||
- GraphQL 클라이언트 라이브러리들의 기본 동작
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 우리 backend 가 `application/graphql-response+json` 을 쓰는지, `294` 를 보내는지
|
||||
- 부분 데이터를 버리는 정책이 제품에서 수용 가능한지
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C1`+`C3` 때문에 adapter 의 성공 판정은 **상태 코드가 아니라 본문의 `data`·`errors` 구조**를 봐야 한다. "`200` 인지 확인" 하는 구현은 `294` 응답을 실패로 오분류하거나, `2xx` 만 보고 errors 를 놓친다.
|
||||
- 인용 2 해석 후보 (미검증): `C4` 는 우리 `PARTIAL_RESULT_FAILURE` 결정이 **규격 이탈**임을 뜻한다. 이탈 자체는 문제가 아니지만, 문서에 "규격은 성공이라 부르지만 우리는 실패로 다룬다" 를 명시하지 않으면 근거를 오해하게 된다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: §5 (GET/POST 와 persisted document), §6.2 (media type 협상)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/grpc-connect-status-codes-error-model]]`, `[[raw/official-docs/zod-runtime-schema-validation-official]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: gRPC status codes / Connect protocol — 오류 표현 방식
|
||||
source_type: official-doc
|
||||
url: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-multi-protocol-api-transport-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, grpc, grpc-web, connect, protocol, error-handling]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# gRPC status codes / Connect protocol — 오류 표현 방식
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-multi-protocol-api-transport-contract]]` | protocol 별 성공 판정을 분리하는 근거(`D2`), gRPC-Web 과 Connect 를 한 adapter 로 묶지 못하는 근거(`D7`), 재시도 가능 판정이 규격이 아니라 **우리 결정**이라는 근거(`D6`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
|
||||
- 보조 URL: https://connectrpc.com/docs/protocol/
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: gRPC Authors / Connect (Buf)
|
||||
- 발행일: (지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
branch 가 `grpc-status` ↔ 정규화 kind 매핑표를 "명세 확인 후" 로 비워 두었다. 확인해 보니 매핑표에 필요한 것은 두 가지였고 성격이 정반대였다. (a) 코드 목록은 규격이 확정해 준다. (b) **어떤 코드가 재시도 가능한지는 규격이 정해 주지 않는다** — 명시적으로 애플리케이션에 떠넘긴다. 그리고 gRPC-Web 과 Connect 는 이름이 비슷한데 오류를 알리는 방식이 정반대여서, 하나의 adapter 로 묶으면 한쪽이 반드시 틀린다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§gRPC statuscodes — Status code 표] "OK 0 / CANCELLED 1 / UNKNOWN 2 / INVALID_ARGUMENT 3 / DEADLINE_EXCEEDED 4 / NOT_FOUND 5 / ALREADY_EXISTS 6 / PERMISSION_DENIED 7 / RESOURCE_EXHAUSTED 8 / FAILED_PRECONDITION 9 / ABORTED 10 / OUT_OF_RANGE 11 / UNIMPLEMENTED 12 / INTERNAL 13 / UNAVAILABLE 14 / DATA_LOSS 15 / UNAUTHENTICATED 16"
|
||||
|
||||
> [§gRPC statuscodes] "there is no fixed list of status codes on which it is appropriate to retry in all applications. As a result, individual applications must make their own determination as to which status codes should cause an RPC to be retried."
|
||||
|
||||
> [§gRPC statuscodes] RPC 는 "a `status` object composed of an integer `code` and a string `message`" 를 반환한다.
|
||||
|
||||
> [§Connect — Error and EndStreamResponse] "An `Error` is a code, an optional message, and an optional array of details."
|
||||
|
||||
> [§Connect — Error and EndStreamResponse] "The simplest form of `Error` contains just a code: `{\"code\": \"unavailable\"}`"
|
||||
|
||||
> [§Connect — Error Codes] "Connect represents categories of errors as codes, and each code maps to a specific HTTP status code."
|
||||
|
||||
> [§Connect — Unary-Response] "Errors are sent with a non-200 **HTTP-Status**."
|
||||
|
||||
> [§Connect — Summary] "The protocol doesn't use HTTP trailers at all, so it works with any networking infrastructure."
|
||||
|
||||
> [§Connect — Streaming-Request] "Note that this is not the same as the gRPC-Web protocol, which uses the _most_ significant bit to mark trailers."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | gRPC status code 는 0(OK)부터 16(UNAUTHENTICATED)까지 17개로 확정되어 있다 | [§표] "OK 0 ... UNAUTHENTICATED 16" | `official-reference` | gRPC 계열 전체 | 각 코드가 어떤 상황에 쓰이는지의 판단 기준 |
|
||||
| C2 | **어떤 코드에서 재시도해야 하는지는 규격이 정하지 않으며 애플리케이션이 스스로 정한다** | [§statuscodes] "individual applications must make their own determination as to which status codes should cause an RPC to be retried." | `official-reference` | 재시도 정책 | 우리가 고른 재시도 집합이 옳다는 것 — 규격은 판단을 위임할 뿐이다 |
|
||||
| C3 | gRPC 의 status 는 code + message 객체이며 transport 와 **별도로** 전달된다 | [§statuscodes] "a `status` object composed of an integer `code` and a string `message`" | `official-reference` | gRPC·gRPC-Web | HTTP 상태 코드와의 구체적 매핑 |
|
||||
| C4 | Connect 의 오류는 `code`(문자열)와 선택적 message·details 로 구성된 JSON 이다 | [§Connect] "An `Error` is a code, an optional message, and an optional array of details." | `official-reference` | Connect unary | 문자열 code 가 gRPC 숫자 code 와 1:1 이라는 것 |
|
||||
| C5 | Connect 는 오류를 **non-200 HTTP status 로 보낸다** | [§Unary-Response] "Errors are sent with a non-200 **HTTP-Status**." | `official-reference` | Connect unary | 스트리밍에서도 같다는 것 |
|
||||
| C6 | Connect 는 HTTP trailer 를 전혀 쓰지 않는다 | [§Summary] "The protocol doesn't use HTTP trailers at all" | `official-reference` | Connect 전체 | gRPC-Web 도 그렇다는 것 — 오히려 반대다 |
|
||||
| C7 | gRPC-Web 은 trailer 를 별도 표시로 구분하며 Connect 와 **다른 프로토콜**이다 | [§Streaming-Request] "this is not the same as the gRPC-Web protocol, which uses the _most_ significant bit to mark trailers." | `official-reference` | 두 프로토콜의 차이 | gRPC-Web 의 trailer 인코딩 세부 |
|
||||
| C8 | Connect 는 각 code 를 특정 HTTP status 로 매핑한다 | [§Error Codes] "each code maps to a specific HTTP status code" | `official-reference` | Connect | 그 매핑이 gRPC-Web 에도 적용된다는 것 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`·`C3`: gRPC 코드 집합과 status 가 transport 와 분리되어 있다는 사실
|
||||
- `C2`: **재시도 가능 판정은 규격이 위임한 애플리케이션 결정**이다
|
||||
- `C4`~`C8`: Connect 와 gRPC-Web 의 오류 표현이 서로 다르다 — Connect 는 non-200 + JSON, gRPC-Web 은 200 + trailer
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- gRPC 코드 → 우리 error kind 매핑표의 구체 내용 — `C2` 가 명시적으로 우리에게 넘긴다
|
||||
- gRPC-Web 의 trailer 파싱 세부 (별도 gRPC-Web 규격 필요)
|
||||
- 어떤 프로토콜을 우리가 실제로 쓸지
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- gRPC-Web 규격 원문 (trailer 인코딩과 `grpc-status` 헤더 위치)
|
||||
- Connect 의 code 문자열 ↔ gRPC 숫자 code 대응표
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C5`+`C7` 은 "gRPC 계열" 이라는 묶음이 구현 관점에서 성립하지 않음을 보여준다. gRPC-Web 은 HTTP 200 이어도 실패일 수 있고, Connect 는 실패면 HTTP 가 200 이 아니다. 성공 판정 코드를 공유하면 한쪽이 반드시 틀린다.
|
||||
- 인용 2 해석 후보 (미검증): `C2` 때문에 매핑표는 "규격 확인 후 채운다" 가 아니라 **"우리가 정하고 근거를 남긴다"** 가 맞다. 규격을 아무리 읽어도 재시도 여부는 나오지 않는다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: gRPC-Web 규격(`PROTOCOL-WEB.md`), Connect 의 code ↔ HTTP status 표 전체
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/graphql-over-http-draft-status-errors]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
title: official-doc / microservices.io — Idempotent Consumer Pattern (Chris Richardson)
|
||||
source_type: official-doc
|
||||
status: raw
|
||||
confidence: medium
|
||||
url: https://microservices.io/patterns/communication-style/idempotent-consumer.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, idempotency]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# Idempotent Consumer Pattern — microservices.io (Chris Richardson)
|
||||
|
||||
> Layer: `raw/official-docs/` — Chris Richardson 의 microservices.io 패턴 카탈로그 중 "Idempotent Consumer" 페이지 verbatim 발췌. at-least-once 재전달로 인한 consumer 중복 실행 문제와 processed-message-ID 기록 기반 해결책의 1차 인용 출처.
|
||||
>
|
||||
> **강도 주의**: microservices.io 는 Chris Richardson 개인의 패턴 카탈로그다. `raw/official-docs/` 에 두되 strength 는 `engineering-blog` (개인 패턴 카탈로그) 로 표기한다 — 벤더 공식 표준(official-standard / official-vendor-doc / official-reference)으로 격상 금지. sibling `[[raw/official-docs/microservices-io-transactional-outbox]]` 와 동일 등급 정책.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton consumer 가 at-least-once 재전달의 중복 실행을 차단하기 위해 처리한 메시지 ID 를 기록하는 inbox(processed-message) 테이블을 비즈니스 write 와 동일 DB 트랜잭션에서 커밋하는 방식을 채택하는 근거, 그리고 "비즈니스 엔티티 자체의 유니크 제약으로 대체" 변형이 언제 성립하는지의 선택 조건 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://microservices.io/patterns/communication-style/idempotent-consumer.html
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Chris Richardson — microservices.io (personal pattern catalog). 별도 vendor 의 공식 문서 아님.
|
||||
- 발행일: rolling docs (페이지에 "Copyright © 2026" 표기, 최초 작성 시점 명시 없음)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-consumer-inbox-contract` branch 가 inbox(PROCESSED_MESSAGE류) 테이블 기반 멱등 consumer 를 채택하는 근거이자, 처리한 메시지 ID 기록을 DB 트랜잭션 경계 안에서 수행해야 duplicate INSERT 가 유니크 제약으로 실패·rollback 되는 메커니즘의 1차 출처. 별도 테이블 vs 비즈니스 엔티티 내장이라는 두 변형 중 어느 쪽을 언제 쓰는지 판단할 근거로 보관한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Context] "One side-effect, however, is that the consumer can be invoked repeatedly for the same message."
|
||||
|
||||
> [§Solution] "Make a consumer idempotent by having it record the IDs of processed messages in the database."
|
||||
|
||||
> [§Solution] "After starting the database transaction, the message handler inserts the message’s ID into the PROCESSED_MESSAGE table."
|
||||
|
||||
> [§Solution] "Since the (subscriberId, messageID) is the PROCESSED_MESSAGE table’s primary key the INSERT will fail if the message has been already processed successfully."
|
||||
|
||||
> [§Solution] "The other option is for the consumer to store the IDs in the business entities that it creates or updates."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MSIO-IDEMPC-C1 | at-least-once 전달을 보장하는 메시지 브로커를 쓰면, 부작용으로 consumer 가 동일 메시지에 대해 반복 호출될 수 있다 | [§Context] "One side-effect, however, is that the consumer can be invoked repeatedly for the same message." | `engineering-blog` | at-least-once delivery 를 보장하는 모든 메시지 브로커(Kafka 포함) 사용 시 재전달 가능성 일반 | 재전달이 얼마나 자주 발생하는지 정량적 근거 없음; Kafka 고유의 rebalance/재시도·offset 커밋 메커니즘과의 상호작용은 별도 근거 필요 |
|
||||
| MSIO-IDEMPC-C2 | 해법의 핵심은 consumer 가 처리한 메시지의 ID 를 DB 에 기록해 멱등성을 확보하는 것 | [§Solution] "Make a consumer idempotent by having it record the IDs of processed messages in the database." | `engineering-blog` | consumer 가 조회 가능한 저장소(RDB 등)에 접근 가능한 아키텍처 | 이 저장소가 반드시 별도 RDB 여야 한다는 뜻은 아님 — 다른 저장 매체(예: 분산 KV)의 적합성은 본문에서 다루지 않음 |
|
||||
| MSIO-IDEMPC-C3 | 메커니즘: message handler 는 DB 트랜잭션을 시작한 뒤 그 트랜잭션 안에서 메시지 ID 를 PROCESSED_MESSAGE 테이블에 INSERT 한다 | [§Solution] "After starting the database transaction, the message handler inserts the message’s ID into the PROCESSED_MESSAGE table." | `engineering-blog` | ID 기록을 DB 트랜잭션 경계 안에서 수행하는 구현 일반 | **미발견**: 이 문장 자체는 "비즈니스 엔티티 갱신도 같은 트랜잭션에 포함되어야 한다"고 명시적으로 말하지 않는다. 하나의 message handler 가 트랜잭션을 하나만 시작한다는 것에서 강하게 시사될 뿐, "동일 트랜잭션 요구"를 문장으로 직접 진술하지는 않음 — ca-skeleton 결정으로 넘어갈 때 이 gap 을 명시해야 함 |
|
||||
| MSIO-IDEMPC-C4 | 중복 탐지 메커니즘은 (subscriberId, messageID) 복합 기본키(primary key) 이며, 이미 처리된 메시지를 다시 INSERT 하면 그 제약 위반으로 실패한다 | [§Solution] "Since the (subscriberId, messageID) is the PROCESSED_MESSAGE table’s primary key the INSERT will fail if the message has been already processed successfully." | `engineering-blog` | RDB 의 기본키/유니크 제약을 dedup 메커니즘으로 쓰는 구현(PostgreSQL 포함) | 이 제약 기반 방식이 분산 락이나 애플리케이션 레벨 사전 조회보다 우월하다는 비교 평가는 없음; 유니크 제약이 없는 저장소(순수 NoSQL 등)에는 그대로 적용되지 않음 |
|
||||
| MSIO-IDEMPC-C5 | 변형: 별도 PROCESSED_MESSAGES 테이블 대신, consumer 가 생성/갱신하는 비즈니스 엔티티 자체에 메시지 ID 를 저장하는 방식도 가능하다 | [§Solution] "The other option is for the consumer to store the IDs in the business entities that it creates or updates." | `engineering-blog` | consumer 가 처리마다 정확히 하나의 특정 business entity 를 생성/갱신하는 경우 (예: `AccountDebited` → `Account` 엔티티) | 이 변형을 **언제** 선택해야 하는지의 판단 기준(예: fan-out 메시지, 엔티티가 없는 처리, 여러 엔티티를 건드리는 처리)은 본문에 없음 — 선택 조건은 이 자료만으로 증명되지 않음 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `MSIO-IDEMPC-C1`: at-least-once 브로커의 재전달 부작용(중복 invocation) 정의
|
||||
- `MSIO-IDEMPC-C2`: 해법의 뼈대 — 처리한 메시지 ID 를 DB 에 기록해 멱등성 확보
|
||||
- `MSIO-IDEMPC-C3`: ID INSERT 가 message handler 의 DB 트랜잭션 안에서 일어난다는 것
|
||||
- `MSIO-IDEMPC-C4`: (subscriberId, messageID) 복합 PK 유니크 제약이 중복 INSERT 를 실패시키는 구체 메커니즘
|
||||
- `MSIO-IDEMPC-C5`: PROCESSED_MESSAGES 별도 테이블의 대안으로 비즈니스 엔티티 자체에 ID 저장이 가능하다는 것(옵션 존재 자체)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- 본 페이지가 **공식 vendor doc 이나 표준**이라는 점 — microservices.io 는 Chris Richardson 의 personal pattern catalog. 어떤 벤더의 공식 채택도 의미하지 않는다. strength 는 전부 `engineering-blog`.
|
||||
- **"메시지 ID 기록과 비즈니스 데이터 갱신이 반드시 같은 트랜잭션이어야 한다"는 명시적 문장** — `C3`의 Does not prove 참고. 원문은 트랜잭션이 하나 시작된다는 것만 말하며, 비즈니스 엔티티 갱신이 그 안에 포함된다는 것은 패턴의 일반 관례로 추정될 뿐 이 페이지에서 직접 진술되지 않는다.
|
||||
- PROCESSED_MESSAGES 별도 테이블 vs 비즈니스 엔티티 내장 중 **어느 쪽이 ca-skeleton 에 더 적합한지의 선택 기준** — `C5`의 Does not prove 참고, 본문은 옵션 존재만 언급
|
||||
- Kafka 특유의 consumer rebalance / `max.poll` / manual ack 커밋 시점과 이 패턴의 상호작용
|
||||
- Eventuate 프레임워크의 실제 구현 코드 세부(이 페이지는 "implements this pattern"이라고만 언급, 코드는 미첨부)
|
||||
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-skeleton 의 `InboxStorePort` 구현이 (subscriberId, messageID) 복합 PK 와 동등한 유니크 제약을 실제로 갖는지 코드 검증
|
||||
- inbox insert 와 비즈니스 write 가 실제로 동일 `@Transactional` 경계 안에서 커밋되는지 — 이 raw 자료만으로는 "그래야 한다"는 관례적 근거이지 ca-skeleton 코드의 검증 결과가 아님
|
||||
- "비즈니스 엔티티 자체에 ID 저장" 변형을 채택할지 여부는 ca-skeleton 의 메시지-엔티티 매핑이 1:1 인 케이스에 한해 별도로 결정해야 함
|
||||
|
||||
## 메모
|
||||
|
||||
- 본 페이지는 Context / Problem / Solution / See also 4개 섹션으로만 구성된 매우 간결한 패턴 카탈로그 페이지이며, 시퀀스 다이어그램 이미지(`/i/IdempotentConsumer/IdempotentConsumer.png`) 하나를 포함하지만 alt-text 나 대체 설명 텍스트는 없다.
|
||||
- "See also" 에 언급된 `Eventuate framework` 와 "blog post about this pattern"(`/post/microservices/patterns/2020/10/16/idempotent-consumer.html`) 은 더 상세한 구현을 담고 있을 가능성이 있음 — 필요 시 별도 raw 로 추가 조사.
|
||||
- 동일 저자의 [[raw/official-docs/microservices-io-transactional-outbox]] 와 짝을 이루는 패턴(producer 측 outbox ↔ consumer 측 idempotent consumer). ca-skeleton 의 producer/consumer 양쪽 계약을 함께 볼 때 두 문서를 같이 참조할 것.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/microservices-io-transactional-outbox]] (동일 저자, producer 측 짝 패턴)
|
||||
- [[raw/official-docs/event-sourcing-vs-outbox-microservices-io]]
|
||||
- [[raw/official-docs/outbox-skip-locked-microservices-io]]
|
||||
- 이 자료를 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-kafka-consumer-inbox-contract]]
|
||||
- 이 자료를 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: official-doc / MongoDB Java Sync Driver — Logging (Logger Names, Levels, maxDocumentLength)
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/drivers/java/sync/current/logging-monitoring/logging/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB Java Sync Driver — Logging (Logger Names, Levels, maxDocumentLength)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — MongoDB Java Sync Driver 공식 문서 (Logging and Monitoring 섹션).
|
||||
|
||||
> 참고: 앞선 dispatch 는 `https://www.mongodb.com/docs/languages/java/sync-driver/current/logging-monitoring/logging/` 로 시도해 404 였다. 본 문서는 `https://www.mongodb.com/docs/drivers/java/sync/current/logging-monitoring/logging/` (200 OK 확인) 를 근거로 한다.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | MongoDB 쿼리 filter·document 값이 로그로 새지 않게 억제한다는 결정(branch 본문 §엣지·실패·의존 표의 "쿼리 filter·document 값이 로그로 유출" 행이 이미 `D18`·`§구현 가이드 8` 로 전방 참조 중 — 본 자료는 그 D18 을 세울 근거) — 어느 logger(`org.mongodb.driver.protocol`/`org.mongodb.driver.protocol.command`)가 command 내용을 어느 레벨(DEBUG)로 남기는지, 로그 레벨을 logger 단위로 어떻게 조정하는지, document 길이를 얼마나(기본 1000자) 제한할 수 있는지의 공식 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/drivers/java/sync/current/logging-monitoring/logging/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: MongoDB, Inc. (공식 Java Sync 드라이버 문서, Logging and Monitoring 섹션)
|
||||
- 발행일: (문서 자체에 명시 없음 — 드라이버 버전별 롤링 업데이트되는 레퍼런스 페이지. 본문 예시 코드는 driver-core 5.9 API 링크를 가리킴)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch가 "MongoDB 쿼리 filter·document 값이 로그로 새지 않게 억제한다"를 결정하는 데 필요한 (1) 드라이버 logger 이름 계층과 각 logger 의 담당 범위, (2) command 로거가 DEBUG 레벨에서 실제로 command/reply 문서 전체를 기록한다는 사실, (3) SLF4J 바인딩 설정으로 logger 이름 단위 레벨 조정이 가능하다는 사실, (4) `maxDocumentLength()` 로 로그 메시지의 문자 길이를 제한할 수 있다는(그러나 필드 마스킹은 아니라는) 사실을 공식 근거로 보관.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Logger Names] "org.mongodb.driver.protocol : Commands sent to and replies received from MongoDB deployments"
|
||||
|
||||
> [§Logger Names] "org.mongodb.driver.connection : Connections and connection pools"
|
||||
|
||||
> [§Connection Settings — Example 로그 출력] "12:14:55.861 [main] DEBUG org.mongodb.driver.protocol.command - Command "find" started on database <database> using a connection with driver-generated ID 3 and server-generated ID 3 to <MongoDB hostname>. The request ID is 5. Command: {"find": "<collection>", "filter": {}, "limit": 1, "singleBatch": true, "$db": "<database>", "lsid": {"id": {"$binary": {"base64": "<_id>", "subType": "04"}}}, "$readPreference": {"mode": "primaryPreferred"}}"
|
||||
|
||||
> [§Connection Settings] "Sets the maximum document length, in characters, of a single log message" — Default: `1000`
|
||||
|
||||
> [§Logger Names — Example - Names] "This example shows how to change the log level for a specific driver logger. The example sets the root logger to OFF and the org.mongodb.driver.connection logger to INFO. This will cause the application to only log messages related to connecting to a MongoDB instance." 설정 예: `<logger name="org.mongodb.driver.connection" level="INFO" additivity="true"/>`
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-JAVA-LOG-C1 | 드라이버는 로깅 이벤트를 조직하기 위해 이름 있는 logger 계층을 정의하며, `org.mongodb.driver.protocol` 은 "MongoDB 배포로 보낸 명령과 그로부터 받은 응답"을 담당한다 | [§Logger Names] "org.mongodb.driver.protocol : Commands sent to and replies received from MongoDB deployments" | `official-vendor-doc` | command/reply 내용을 다루는 logger 가 어느 것인지 식별(값 노출 억제 대상 지정의 근거) | `org.mongodb.driver.protocol.command`(하위 logger)가 실제로 어떤 필드를 기록하는지는 C3·C4 가 별도로 증명 |
|
||||
| MONGO-JAVA-LOG-C2 | `org.mongodb.driver.connection` logger 는 "연결과 connection pool"을 담당하며 command 내용과는 별개 관심사다 | [§Logger Names] "org.mongodb.driver.connection : Connections and connection pools" | `official-vendor-doc` | connection 관련 로거와 command 관련 로거가 서로 다른 이름 공간이라는 것 — 하나를 억제해도 다른 하나는 별도로 다뤄야 함을 보여주는 근거 | connection logger 를 억제하는 것만으로 command 값 노출이 막힌다는 것(별개 logger 계층이므로 이 자료가 그런 인과를 증명하지 않음) |
|
||||
| MONGO-JAVA-LOG-C3 | 실제 로그 예시에서 `org.mongodb.driver.protocol.command` logger 는 DEBUG 레벨로 command 시작/성공 이벤트를 기록한다 | [§Connection Settings 예시 로그] "12:14:55.861 [main] DEBUG org.mongodb.driver.protocol.command - Command \"find\" started on database <database> using a connection with driver-generated ID 3" | `official-vendor-doc` | command 로거가 DEBUG 레벨에서 활성 관측 가능하다는 사실 확인 | 이 레벨이 모든 배포·버전에서 고정 불변이라는 것(레벨 자체는 사용자가 SLF4J 설정으로 조정 가능 — C5 참조) |
|
||||
| MONGO-JAVA-LOG-C4 | 그 DEBUG 로그 라인의 `Command:` 필드에는 command document 전체(예시에서는 `find` command 의 `filter` 키 포함)가 직렬화되어 그대로 남는다 | [§Connection Settings 예시 로그] "Command: {\"find\": \"<collection>\", \"filter\": {}, \"limit\": 1, \"singleBatch\": true, \"$db\": \"<database>\"" | `official-vendor-doc` | "`org.mongodb.driver.protocol.command`를 DEBUG 이상으로 열면 쿼리 filter 값이 그대로 로그에 남는다"는 결정의 직접 근거 | 이 예시의 `filter` 값이 `{}`(빈 값)이라 실제 업무 데이터가 담긴 filter 가 노출된 사례 자체는 아님 — 다만 command document 전체가 직렬화되어 로그 문자열에 포함되는 **메커니즘**은 이 인용이 직접 보여줌. 필드 단위로 선택적 마스킹/redact 되는지는 이 인용만으로 증명 안 됨(C5 의 truncation 과는 별개 메커니즘) |
|
||||
| MONGO-JAVA-LOG-C5 | `LoggerSettings.maxDocumentLength()` 로 단일 로그 메시지의 document 최대 길이(문자 수)를 설정할 수 있으며 기본값은 `1000`이다 | [§Connection Settings] "Sets the maximum document length, in characters, of a single log message" + "Default: 1000" | `official-vendor-doc` | 로그에 남는 document/command 내용의 길이를 제한하는 공식 제어 수단이 존재한다는 것, 그리고 그 기본값 | 이 길이 제한이 민감한 필드를 선택적으로 마스킹(redact)한다는 것 — 이 옵션은 문자 수 기준 **truncation**이지 필드 단위 마스킹 기능이 아니며, 원문에 필드 마스킹 옵션은 존재하지 않는다(부재 확인, self-grep) |
|
||||
| MONGO-JAVA-LOG-C6 | 로그 레벨은 logger 이름 단위로 조정 가능하며, SLF4J 바인딩 프레임워크(logback/log4j2) 설정에서 root logger 를 `OFF` 로 두고 특정 driver logger 만 원하는 레벨로 올리는 방식으로 노출 범위를 좁힐 수 있다 | [§Logger Names — Example - Names] "This example shows how to change the log level for a specific driver logger. The example sets the root logger to OFF and the org.mongodb.driver.connection logger to INFO." + `<logger name="org.mongodb.driver.connection" level="INFO" additivity="true"/>` | `official-vendor-doc` | logger 이름 단위 레벨 억제가 공식적으로 지원되는 설정 방식이라는 것 — "어느 logger 를 어느 레벨로 묶어야 값이 안 새는가"를 SLF4J 설정으로 구현할 수 있다는 메커니즘 근거 | 이 예시는 `org.mongodb.driver.connection` 을 대상으로 시연했을 뿐 `org.mongodb.driver.protocol`/`org.mongodb.driver.protocol.command` 에 동일 패턴을 적용한 예시 로그를 직접 보여주지는 않음(단, 로거 계층 원리는 동일 — §메모 참조) |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, MongoDB 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGO-JAVA-LOG-C1`~`C2`: 드라이버가 정의하는 9개 logger 이름(`org.mongodb.driver.authenticator`/`client`/`cluster`/`connection`/`connection.tls`/`operation`/`protocol`/`uri`/`management`) 각각의 담당 범위 — command/reply 내용은 `org.mongodb.driver.protocol`(하위 `org.mongodb.driver.protocol.command`) 소관, connection pool 은 `org.mongodb.driver.connection` 소관으로 서로 분리됨
|
||||
- `MONGO-JAVA-LOG-C3`~`C4`: command 로거가 DEBUG 레벨에서 command document(filter 포함)를 그대로 직렬화해 로그에 남긴다는 것
|
||||
- `MONGO-JAVA-LOG-C5`: `maxDocumentLength()`(기본 1000자)가 로그 메시지 길이를 제한하는 유일한 공식 옵션이며, 이것은 truncation 이지 필드 마스킹이 아니라는 것
|
||||
- `MONGO-JAVA-LOG-C6`: SLF4J 바인딩 설정에서 root 를 OFF 로 두고 특정 driver logger 만 레벨을 올리는 방식이 공식 예시로 제공된다는 것
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 필드 단위 마스킹/redact 기능의 존재 여부 — 원문에 그런 옵션이 전혀 등장하지 않는다(부재 확인이지 "없다"는 벤더의 명시적 부정 진술은 아님)
|
||||
- `org.mongodb.driver.protocol`(부모)과 `org.mongodb.driver.protocol.command`(자식) 각각을 **개별적으로** OFF/INFO 로 나눠 조정했을 때의 정확한 상속 동작 예시 — 로거 계층 상속 원리("A logger inherits the properties of its ancestor logger")는 일반론으로 서술되나, protocol/protocol.command 조합에 대한 실측 예시 로그는 이 문서에 없음
|
||||
- `org.mongodb.driver.protocol` logger 를 완전히 OFF 로 꺼도 command 관련 다른 경로(예: Command Monitoring 이벤트 리스너, `org.mongodb.driver.operation` 의 재시도 로그)로 filter 값이 별도로 새는지 여부 — 이 문서는 SLF4J 로거 채널만 다루고 Command Monitoring API(별도 기능)는 다루지 않음
|
||||
- Reactive Streams/Async 드라이버 등 다른 MongoDB Java 드라이버 변형에서도 동일 logger 이름·동일 기본값이 적용되는지 — 이 문서는 Sync 드라이버 전용 페이지
|
||||
- MongoDB 서버 측(`mongod`/`mongos`) 로그에 동일한 filter 값 노출 문제가 있는지 — 이 문서는 클라이언트(드라이버) 로깅만 다룸
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 실제로 SLF4J 바인딩(logback 등)을 어떤 레벨로 배포 환경별(dev/staging/prod)로 설정하는지 — 현재 mongo 모듈 설정 여부는 branch note §Audit 참조(연결 자체가 미설정 상태, `A1`)
|
||||
- `org.mongodb.driver.protocol`(부모) 레벨을 낮추는 것과 `org.mongodb.driver.protocol.command`(자식)만 개별 지정하는 것 중 어느 쪽이 ca-skeleton 의 로깅 프레임워크(logback 등)에서 실제로 원하는 억제 범위를 만드는지 로컬 검증 필요
|
||||
- `maxDocumentLength` 축소만으로 민감한 필드(예: 개인정보가 담긴 filter 값)의 앞부분 노출까지 막을 수 있는지 — truncation 은 길이만 제한하므로 앞쪽 N자 안에 민감값이 있으면 여전히 노출됨. 이 gap 은 D18 결정에서 "레벨 자체를 낮춘다"와 "길이만 줄인다"를 혼동하지 않도록 명시가 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- 이 페이지는 React Server Component(Next.js) payload 형태로 서빙되며, WebFetch(AI 요약 도구)는 tab 선택형 UI(Logger 드롭다운) 뒤의 실제 본문을 가져오지 못했다(nav 구조만 반환) — `curl` 로 raw HTML 을 직접 받아 임베딩된 JSON payload 안의 텍스트를 수동 unescape 해서 검증했다. self-grep 은 이 unescape 된 텍스트 파일(`/tmp/source-fetch-1785227947.txt`) 기준.
|
||||
- logger 이름 목록에 `org.mongodb.driver.protocol.command` 자체는 "Logger Names" 목록에 별도 항목으로 나열되지 않는다 — 목록에는 부모인 `org.mongodb.driver.protocol` 만 있고, `protocol.command` 는 예시 로그 출력에서만 등장한다. 즉 `protocol.command` 가 `protocol` 의 자식이라는 것은 문서 앞부분의 "logger 이름은 계층을 이룬다"는 일반 설명("grandparent.parent.child" 예시)과 실제 로그 라인의 이름 문자열로부터 **추론**한 것이며, "protocol.command 는 protocol 의 공식 자식 logger 목록에 있다"는 명시적 1:1 진술은 이 페이지에 없다 — 검증되지 않은 추론이므로 Claims Extracted 에는 포함하지 않았다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `https://www.mongodb.com/docs/drivers/java/sync/current/logging-monitoring/monitoring/` (Command Monitoring API — SLF4J 로거와 별개 채널로 command 이벤트를 구독하는 수단일 가능성, D18 완결 시 확인 필요)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/mongodb-connection-string-options]] (연결 문자열 옵션 — 동일 branch 근거)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,120 @@
|
||||
---
|
||||
title: official-doc / Apache Kafka — Client Security Configuration (SSL / SASL)
|
||||
source_type: official-doc
|
||||
url: https://kafka.apache.org/documentation/#security_configclients
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-producer-runtime-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, security, messaging, kafka]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Apache Kafka — Client Security Configuration (SSL / SASL)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`official-doc` — Apache Kafka 프로젝트가 직접 배포하는 공식 레퍼런스 문서.
|
||||
|
||||
## Parent / 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-producer-runtime-contract]] | ca-skeleton 의 Kafka producer 가 TLS/SASL 설정 키를 어떤 이름으로 갖는지, 그리고 어떤 값이 secret 분류 대상(keystore/truststore 비밀번호, SASL 자격증명)인지를 registry 등록 전에 공식 키 이름으로 확정한다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL (사용자 요청): https://kafka.apache.org/documentation/#security_configclients
|
||||
- **URL 리다이렉트 확인 사항**: 위 URL 은 client-side JS 리다이렉트 stub(`window.location.replace(...)`) 이다. `curl` 로 raw HTML 을 직접 받아 확인한 결과, 과거 단일 대형 페이지(`documentation.html`)의 "Configuring Kafka Clients" 앵커는 Kafka 4.3 문서 재구성(Hugo/Docsy 기반) 이후 다음 3개 세부 페이지로 분리되어 있다. 이 raw 문서의 인용은 모두 이 3개 페이지에서 발췌했다:
|
||||
- https://kafka.apache.org/43/security/listener-configuration/ (`security.protocol` 값 목록)
|
||||
- https://kafka.apache.org/43/security/encryption-and-authentication-using-ssl/ (SSL client `Configuring Kafka Clients` 섹션)
|
||||
- https://kafka.apache.org/43/security/authentication-using-sasl/ (SASL client `Configuring Kafka Clients` 섹션 + mechanism 목록)
|
||||
- 아카이브 URL: (미생성)
|
||||
- 저자 / 조직: Apache Software Foundation — Apache Kafka 프로젝트
|
||||
- 발행일: 각 페이지 하단 "Last modified May 22, 2026: MINOR: Add 4.3 docs and javadoc (#853) (dde051e342)" — Kafka 4.3 문서 빌드 기준
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
**Fetch 방법에 대한 투명성 메모**: `WebFetch` 툴을 원본 URL 과 `/43/documentation.html`(구조 추정) 두 형태로 3회 시도했으나, 매번 페이지 본문이 로드되기 전의 "Redirecting..." stub 만 반환됐다(JS 리다이렉트를 실행하지 않는 fetcher 한계). `curl` 로 raw HTML 을 직접 받아 리다이렉트 목적지(`t="43"`, Hugo 사이드바 nav)를 역추적해 위 3개 실제 문서 페이지를 특정했고, 각 페이지 HTML 을 태그 제거 후 평문으로 저장했다(`/tmp/source-fetch-20260728-170505.txt`, 3개 페이지 연결본). 아래 모든 인용은 이 평문에 대해 `grep -nF` 로 self-grep 검증했다(에이전트 최종 리포트 참조). Confluent fallback(`docs.confluent.io/platform/current/kafka/authentication_ssl.html`)은 사용하지 않았다 — Apache 공식 페이지 원문을 실제로 확보했기 때문.
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton Kafka producer 의 TLS/SASL 설정을 구현하기 전에, Kafka 공식 문서가 실제로 정의하는 설정 키 이름(`security.protocol`, `ssl.truststore.*`, `ssl.keystore.*`, `sasl.mechanism`, `sasl.jaas.config`)과 값 집합을 근거로 고정해 두기 위함. 특히 keystore/truststore 비밀번호와 SASL 자격증명이 어떤 키에 담기는지를 공식 문서로 확정해야 secret 분류(예: env-driven config registry 에서 마스킹 대상 지정)를 임의로 하지 않을 수 있다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Listener Configuration] "Possible options (case-insensitive) for the security protocol are given below:
|
||||
> PLAINTEXT
|
||||
> SSL
|
||||
> SASL_PLAINTEXT
|
||||
> SASL_SSL
|
||||
> The plaintext protocol provides no security and does not require any additional configuration."
|
||||
|
||||
> [§Encryption and Authentication using SSL → Configuring Kafka Clients] "If client authentication is not required in the broker, then the following is a minimal configuration example:
|
||||
> security.protocol=SSL
|
||||
> ssl.truststore.location=/var/private/ssl/client.truststore.jks
|
||||
> ssl.truststore.password=test1234"
|
||||
|
||||
> [§Encryption and Authentication using SSL → Configuring Kafka Clients] "If client authentication is required, then a keystore must be created like in step 1 and the following must also be configured:
|
||||
> ssl.keystore.location=/var/private/ssl/client.keystore.jks
|
||||
> ssl.keystore.password=test1234
|
||||
> ssl.key.password=test1234"
|
||||
|
||||
> [§Authentication using SASL/PLAIN → Configuring Kafka Clients] "The following is an example configuration for a client for the PLAIN mechanism:
|
||||
> sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \
|
||||
> username="alice" \
|
||||
> password="alice-secret";"
|
||||
|
||||
> [§Authentication using SASL/PLAIN → Use of SASL/PLAIN in production] "SASL/PLAIN should be used only with SSL as transport layer to ensure that clear passwords are not transmitted on the wire without encryption. The default implementation of SASL/PLAIN in Kafka specifies usernames and passwords in the JAAS configuration file as shown here. From Kafka version 2.0 onwards, you can avoid storing clear passwords on disk by configuring your own callback handlers that obtain username and password from an external source using the configuration options sasl.server.callback.handler.class and sasl.client.callback.handler.class."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-SEC-C1 | Kafka 의 `security.protocol` 은 정확히 4개 값(`PLAINTEXT`/`SSL`/`SASL_PLAINTEXT`/`SASL_SSL`) 중 하나이며, `PLAINTEXT` 는 보안을 제공하지 않는다 | [§Listener Configuration] "Possible options (case-insensitive) for the security protocol are given below: PLAINTEXT SSL SASL_PLAINTEXT SASL_SSL The plaintext protocol provides no security..." | `official-vendor-doc` | producer/consumer/broker 공통 `security.protocol` 값 선택 | Spring Boot `spring.kafka.*` 바인딩 프로퍼티 이름이 이와 동일하다는 것 — 이 문서는 native Kafka client config 이지 Spring Kafka autoconfiguration 문서가 아님 |
|
||||
| KAFKA-SEC-C2 | client 인증(mTLS) 이 불필요한 경우 SSL client 최소 설정은 `security.protocol=SSL` + `ssl.truststore.location` + `ssl.truststore.password` 3개 키다 | [§SSL → Configuring Kafka Clients] "the following is a minimal configuration example: security.protocol=SSL ssl.truststore.location=... ssl.truststore.password=..." | `official-vendor-doc` | 단방향 TLS(서버 인증만) client 설정 | `ssl.truststore.password` 가 필수라는 것 — 문서 다른 곳(SSL 페이지)에서 "technically optional but highly recommended" 라고 명시. mTLS 여부는 별도 결정 |
|
||||
| KAFKA-SEC-C3 | client 인증(mTLS) 이 필요한 경우 추가로 `ssl.keystore.location` + `ssl.keystore.password` + `ssl.key.password` 3개 키가 필요하다 | [§SSL → Configuring Kafka Clients] "If client authentication is required... ssl.keystore.location=... ssl.keystore.password=... ssl.key.password=..." | `official-vendor-doc` | mTLS(양방향 TLS) client 설정 | ca-skeleton 이 실제로 mTLS 를 요구하는지 — 이는 배포 환경/브랜치 결정이지 이 문서가 증명하는 것이 아님 |
|
||||
| KAFKA-SEC-C4 | SASL/PLAIN client 설정은 `sasl.jaas.config` 값 문자열 안에 `username`/`password` 를 리터럴로 직접 기입한다 | [§SASL/PLAIN → Configuring Kafka Clients] "sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \ username=\"alice\" \ password=\"alice-secret\";" | `official-vendor-doc` | SASL/PLAIN mechanism 의 client-side JAAS 설정 | 이것이 유일한 방법이라는 것 — 같은 문서가 바로 이어서(KAFKA-SEC-C6) callback handler 대안을 언급함 |
|
||||
| KAFKA-SEC-C5 | Kafka 가 공식 지원하는 SASL mechanism 은 정확히 5개: `GSSAPI (Kerberos)`, `PLAIN`, `SCRAM-SHA-256`, `SCRAM-SHA-512`, `OAUTHBEARER` | [§SASL mechanisms] "Kafka supports the following SASL mechanisms: GSSAPI (Kerberos) PLAIN SCRAM-SHA-256 SCRAM-SHA-512 OAUTHBEARER" | `official-vendor-doc` | `sasl.mechanism` 값 선택 범위 | 각 mechanism 이 프로덕션에 동등하게 권장된다는 것 — 문서는 기본 OAUTHBEARER 구현이 non-production 전용이라고 별도 경고함(이 raw 문서 범위 밖 세부사항) |
|
||||
| KAFKA-SEC-C6 | SASL/PLAIN 기본 구현은 자격증명을 JAAS 설정 파일/프로퍼티에 평문으로 저장하며, SSL 전송 없이는 자격증명이 평문으로 네트워크에 전송된다. Kafka 2.0+ 부터는 커스텀 callback handler 로 디스크 평문 저장을 피할 수 있다 | [§SASL/PLAIN → Use of SASL/PLAIN in production] "SASL/PLAIN should be used only with SSL as transport layer to ensure that clear passwords are not transmitted on the wire without encryption. The default implementation... specifies usernames and passwords in the JAAS configuration file..." | `official-vendor-doc` | `sasl.jaas.config` 값(및 그 안의 `username`/`password`)을 secret 으로 분류해야 하는 근거 | 구체적인 vault/secret-manager 연동 방법 — Kafka 문서는 "커스텀 callback handler 로 회피 가능"까지만 말하고 구현체를 권고하지 않음 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, Apache 등 공식 벤더/프로젝트 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `KAFKA-SEC-C1`: `security.protocol` 4개 값과 `PLAINTEXT` 의 무보안 의미
|
||||
- `KAFKA-SEC-C2`/`KAFKA-SEC-C3`: SSL client 설정 키 이름 전체 목록(단방향/양방향 각각)
|
||||
- `KAFKA-SEC-C4`/`KAFKA-SEC-C5`/`KAFKA-SEC-C6`: SASL client 설정 키 이름, 지원 mechanism 목록, 자격증명 평문 저장 위험
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Spring Boot / Spring Kafka 의 `spring.kafka.*` 프로퍼티 바인딩 이름 (native Kafka client config 와 이름이 다를 수 있음 — 별도 Spring Kafka 공식 문서 확인 필요)
|
||||
- ca-skeleton 이 mTLS 를 쓸지, 어떤 SASL mechanism 을 쓸지에 대한 결정 (branch-local 결정 사항)
|
||||
- secret 값을 실제로 어떤 방식(Vault/K8s Secret/env)으로 주입할지 (Kafka 문서 범위 밖)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `feature-env-driven-runtime-configuration` 의 secret 분류 registry 스키마와 이 키 이름들의 실제 매핑
|
||||
- Spring Kafka 사용 시 `spring.kafka.properties.*` prefix 를 통한 raw property 전달 방식(Spring Kafka 공식 문서로 별도 검증)
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- WebFetch 툴이 이 URL(및 `/43/documentation.html` 추정 경로)에서 JS 리다이렉트 stub 만 반환해 3회 실패했다. `curl` 로 raw HTML 을 받아 리다이렉트 목적지를 역추적한 뒤 실제 3개 세부 페이지를 직접 curl 로 재수집했다 — 이 과정과 최종 텍스트는 `/tmp/source-fetch-20260728-170505.txt` (listener-configuration + encryption-and-authentication-using-ssl + authentication-using-sasl 3페이지 연결본)에 보존.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `https://kafka.apache.org/43/security/authorization-and-acls/` (ACL — 이 branch 범위 밖일 가능성 높음), Spring Kafka 공식 문서(`spring.kafka.*` property reference — 별도 raw 자료 필요)
|
||||
|
||||
## 관련
|
||||
|
||||
> 같은 주제의 다른 raw 자료, 또는 이 자료를 인용한 wiki 문서.
|
||||
|
||||
- 같은 벤더 다른 official-doc: `[[raw/official-docs/spring-kafka-sending-messages-kafkatemplate]]`
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시)
|
||||
@@ -0,0 +1,106 @@
|
||||
---
|
||||
title: official-doc / Apache Kafka Consumer Configs — max.poll.interval.ms, max.poll.records, auto-commit, session/heartbeat timeouts, partition.assignment.strategy
|
||||
source_type: official-doc
|
||||
url: https://kafka.apache.org/41/generated/consumer_config.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Apache Kafka Consumer Configs — max.poll.interval.ms, max.poll.records, auto-commit, session/heartbeat timeouts, partition.assignment.strategy
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Apache Kafka 4.1 (`kafka.apache.org/41/generated/consumer_config.html`, HTTP `last-modified: Fri, 12 Dec 2025 04:07:23 GMT`).
|
||||
|
||||
## source_type
|
||||
|
||||
`official-doc` — Apache Software Foundation 이 직접 호스팅하는 Apache Kafka 프로젝트 공식 consumer configuration reference (Gradle 빌드가 매 릴리스마다 자동 생성하는 `ConfigDef` 기반 페이지).
|
||||
|
||||
## URL Fetch 경위
|
||||
|
||||
- 요청된 URL 그대로 성공: `https://kafka.apache.org/41/generated/consumer_config.html` — `curl` 직접 요청 1회차에 HTTP `200`, 87,328 bytes 수신. 이 URL 은 (기존 `raw/official-docs/kafka-producer-configs-delivery-timeout.md` 와 `raw/official-docs/kafka-message-delivery-semantics-design.md` 가 겪은) `kafka.apache.org/documentation/#...` client-side JS redirect 셸이 아니라, 이미 버전 고정된 generated 정적 페이지라 redirect 문제가 없었다.
|
||||
- 저장 원본: `curl` 로 받은 raw HTML 전체를 세션 scratchpad 에 저장(`kafka-consumer-config-raw.html`, 87,328 bytes). 이후 HTML 태그를 제거한 평문(paragraph 단위 `Description:`/`Type:`/`Default:`/`Valid Values:`/`Importance:` 추출)을 별도 파일(`kafka-consumer-config-fetch.txt`)로 만들어 self-grep 대상으로 삼았다.
|
||||
- **이중 검증**: 아래 모든 인용은 (1) 평문 추출 파일에 대한 `grep -nF`, (2) 원본 raw HTML 파일에 대한 `grep -nF` 양쪽 모두 통과를 재확인했다(HTML 태그가 인용 문장 내부 단어를 끊지 않는 문장만 인용 대상으로 선정).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton consumer 가 장시간 처리와 rebalance 안정성을 위해 조정하는 설정 노브(`max.poll.interval.ms`, `max.poll.records`, `enable.auto.commit`/`auto.commit.interval.ms`, `session.timeout.ms`/`heartbeat.interval.ms`, `partition.assignment.strategy`)의 공식 정의와 기본값 기준선 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://kafka.apache.org/41/generated/consumer_config.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Apache Software Foundation (Apache Kafka 프로젝트)
|
||||
- 발행일: 명시 없음 (버전 고정 reference 페이지, HTTP `last-modified: Fri, 12 Dec 2025 04:07:23 GMT`)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-consumer-inbox-contract` 브랜치가 inbound leaf 의 rebalance·backpressure·auto-commit 계약을 설계할 때 임의 수치를 발명하지 않도록, 각 설정 노브의 공식 설명 전문과 기본값을 기준선으로 고정하기 위해 저장. 특히 `max.poll.interval.ms` 초과 시 그룹 이탈·rebalance 가 일어난다는 문장과 `partition.assignment.strategy` 기본값에 `CooperativeStickyAssignor` 가 이미 포함돼 있다는 사실이 본 branch 의 "bounded concurrency·pause/resume backpressure" 및 "rebalance 처리" 결정의 직접 근거가 된다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§max.poll.interval.ms] "The maximum delay between invocations of poll() when using consumer group management." [...] "If poll() is not called before expiration of this timeout, then the consumer is considered failed and the group will rebalance in order to reassign the partitions to another member." (Default: `300000` — 5 minutes)
|
||||
|
||||
> [§max.poll.records] "The maximum number of records returned in a single call to poll()." (Default: `500`)
|
||||
|
||||
> [§enable.auto.commit] "If true the consumer's offset will be periodically committed in the background." (Default: `true`)
|
||||
|
||||
> [§auto.commit.interval.ms] "The frequency in milliseconds that the consumer offsets are auto-committed to Kafka if enable.auto.commit is set to true." (Default: `5000` — 5 seconds)
|
||||
|
||||
> [§session.timeout.ms] "If no heartbeats are received by the broker before the expiration of this session timeout, then the broker will remove this client from the group and initiate a rebalance." (Default: `45000` — 45 seconds)
|
||||
|
||||
> [§heartbeat.interval.ms] "In that case, the value must be set lower than session.timeout.ms, but typically should be set no higher than 1/3 of that value." (Default: `3000` — 3 seconds)
|
||||
|
||||
> [§partition.assignment.strategy] "The default assignor is [RangeAssignor, CooperativeStickyAssignor], which will use the RangeAssignor by default," [...] "but allows upgrading to the CooperativeStickyAssignor with just a single rolling bounce that removes the RangeAssignor from the list." (Default: `class org.apache.kafka.clients.consumer.RangeAssignor,class org.apache.kafka.clients.consumer.CooperativeStickyAssignor`)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-CONSCFG-C1 | `max.poll.interval.ms` 는 consumer group management 사용 시 `poll()` 호출 간 최대 허용 지연이며, 기본값은 300000ms(5분)이다. 이 시간 내에 `poll()` 이 호출되지 않으면 consumer 는 실패한 것으로 간주되어 그룹이 rebalance 되어 파티션이 다른 member 에게 재할당된다 | "The maximum delay between invocations of poll() when using consumer group management." ... "If poll() is not called before expiration of this timeout, then the consumer is considered failed and the group will rebalance in order to reassign the partitions to another member." | `official-vendor-doc` | 장시간 처리(long-running handler)가 있는 consumer 가 `max.poll.interval.ms` 를 초과하면 강제 rebalance 를 유발한다는 근거 — bounded concurrency·pause/resume backpressure 설계의 기본값 기준선 | ca-skeleton 워크로드의 실제 처리 시간이 300000ms 이내인지는 증명하지 않는다. `group.instance.id` 설정 시 즉시 재할당이 아니라 `session.timeout.ms`(또는 consumer 프로토콜의 `group.consumer.session.timeout.ms`) 만료 후 재할당된다는 예외 조건은 이 인용에는 없지만 원문에는 명시돼 있음(§메모 참고) |
|
||||
| KAFKA-CONSCFG-C2 | `max.poll.records` 는 단일 `poll()` 호출에서 반환되는 최대 레코드 수이며 기본값은 500이다 | "The maximum number of records returned in a single call to poll()." | `official-vendor-doc` | consumer 가 한 번에 처리할 배치 크기 상한을 계약값으로 고정하는 근거 | 이 값이 fetch 자체의 크기(`fetch.max.bytes`/`max.partition.fetch.bytes`)에 영향을 준다는 뜻은 아님 — 원문은 "does not impact the underlying fetching behavior" 라고 명시(캐시된 레코드를 incremental 하게만 반환) |
|
||||
| KAFKA-CONSCFG-C3 | `enable.auto.commit` 이 `true`(기본값)이면 consumer offset 이 백그라운드에서 주기적으로 커밋된다 | "If true the consumer's offset will be periodically committed in the background." | `official-vendor-doc` | manual acknowledgement 설계 시 기본값(`true`)을 명시적으로 `false` 로 override 해야 한다는 근거 | 커밋 주기의 정확한 타이밍(다음 `poll()` 호출 시점과의 관계 등)은 이 인용문 자체에는 없음 — `auto.commit.interval.ms`(C4)가 주기 값만 정의 |
|
||||
| KAFKA-CONSCFG-C4 | `auto.commit.interval.ms` 는 `enable.auto.commit=true` 일 때 offset 이 자동 커밋되는 주기(밀리초)이며 기본값은 5000ms(5초)이다 | "The frequency in milliseconds that the consumer offsets are auto-committed to Kafka if enable.auto.commit is set to true." | `official-vendor-doc` | auto-commit 모드를 쓸 경우의 커밋 주기 기본값 기준선 | `enable.auto.commit=false`(manual ack 설계)에서는 이 설정 자체가 무의미해진다는 것 — 원문은 그 상호작용을 명시하지 않음 |
|
||||
| KAFKA-CONSCFG-C5 | `session.timeout.ms` 는 group management 사용 시 client 장애를 탐지하는 타임아웃이며 기본값은 45000ms(45초)이다. 이 시간 내에 broker 가 heartbeat 을 받지 못하면 broker 가 해당 client 를 그룹에서 제거하고 rebalance 를 시작한다 | "If no heartbeats are received by the broker before the expiration of this session timeout, then the broker will remove this client from the group and initiate a rebalance." | `official-vendor-doc` | broker 측 장애 탐지 타임아웃의 기본값 기준선 — `max.poll.interval.ms`(C1, poll 미호출 탐지)와는 별개 메커니즘(heartbeat thread 기반)이라는 근거 | 값 범위가 broker 설정(`group.min.session.timeout.ms`/`group.max.session.timeout.ms`)에 종속된다는 제약과, `group.protocol=consumer` 사용 시 이 client 설정 자체가 무시되고 broker 설정(`group.consumer.session.timeout.ms`)이 대신 적용된다는 것은 인용문에는 없지만 원문 본문에 명시(§메모 참고) |
|
||||
| KAFKA-CONSCFG-C6 | `heartbeat.interval.ms` 는 group management 사용 시 heartbeat 간격이며 기본값은 3000ms(3초)이다. `group.protocol=classic` 인 경우 이 값은 `session.timeout.ms` 보다 반드시 작아야 하고, 통상적으로 `session.timeout.ms` 의 1/3 이하로 설정하는 것이 권장된다 | "In that case, the value must be set lower than session.timeout.ms, but typically should be set no higher than 1/3 of that value." | `official-vendor-doc` | classic rebalance protocol 하에서 `heartbeat.interval.ms` ≤ `session.timeout.ms`/3 비율 기준선(기본값 3000ms vs 45000ms 는 정확히 1/15 비율로 권장 상한보다 여유 있음) | `group.protocol=consumer` 로 설정 시 이 client 설정 자체가 지원되지 않고 broker 설정(`group.consumer.heartbeat.interval.ms`)이 heartbeat 간격을 통제한다는 것은 이 인용문에는 없지만 원문에 명시(§메모 참고) |
|
||||
| KAFKA-CONSCFG-C7 | `partition.assignment.strategy` 기본값은 `[RangeAssignor, CooperativeStickyAssignor]` 이며, 이 기본 목록은 RangeAssignor 를 우선 사용하되 목록에서 RangeAssignor 를 제거하는 단 한 번의 rolling bounce 만으로 CooperativeStickyAssignor 로 업그레이드할 수 있게 해준다 | "The default assignor is [RangeAssignor, CooperativeStickyAssignor], which will use the RangeAssignor by default," ... "but allows upgrading to the CooperativeStickyAssignor with just a single rolling bounce that removes the RangeAssignor from the list." | `official-vendor-doc` | Kafka 4.1 기본값 자체가 이미 `CooperativeStickyAssignor` 로의 무중단 업그레이드 경로를 지원한다는 근거 — cooperative rebalancing 채택 여부를 branch 결정으로 명시할 때의 기준선 | 기본값이 RangeAssignor 를 "우선 사용"한다는 것이지, cooperative(비-eager) rebalancing 이 기본으로 **활성화**되어 있다는 뜻은 아님 — RangeAssignor 는 eager assignor 이며, 순수 cooperative 동작을 얻으려면 목록에서 RangeAssignor 를 제거하는 별도 rolling bounce 가 필요하다는 것이 원문의 요지 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모두 `official-vendor-doc` — Apache Kafka 프로젝트(Apache Software Foundation) 가 직접 게시한 configuration reference. `official-standard`(RFC/표준)는 아니다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `KAFKA-CONSCFG-C1`: `max.poll.interval.ms` 정의·기본값(300000ms)과 초과 시 rebalance 유발
|
||||
- `KAFKA-CONSCFG-C2`: `max.poll.records` 정의·기본값(500)과 fetch 동작과의 독립성
|
||||
- `KAFKA-CONSCFG-C3`: `enable.auto.commit` 정의·기본값(`true`)
|
||||
- `KAFKA-CONSCFG-C4`: `auto.commit.interval.ms` 정의·기본값(5000ms)
|
||||
- `KAFKA-CONSCFG-C5`: `session.timeout.ms` 정의·기본값(45000ms)과 heartbeat 미수신 시 broker 주도 제거·rebalance
|
||||
- `KAFKA-CONSCFG-C6`: `heartbeat.interval.ms` 정의·기본값(3000ms)과 `session.timeout.ms` 대비 권장 비율(≤1/3)
|
||||
- `KAFKA-CONSCFG-C7`: `partition.assignment.strategy` 기본값([RangeAssignor, CooperativeStickyAssignor])과 CooperativeStickyAssignor 로의 단일 rolling-bounce 업그레이드 경로
|
||||
- 이 자료가 증명하지 **않는** 것:
|
||||
- ca-skeleton 실제 워크로드에서 이 기본값들(300000ms poll interval, 500 records/poll, 45000ms session timeout 등)이 목표 처리율·지연·rebalance 빈도를 만족한다는 것 — 벤치마크·측정 필요
|
||||
- `group.protocol=consumer`(새 KIP-848 consumer group protocol) 사용 시 client-side `session.timeout.ms`/`heartbeat.interval.ms` 설정이 무시되고 broker 설정으로 대체된다는 세부 동작의 완전한 구현 검증 — 원문은 이 상호작용을 명시하지만, ca-skeleton 이 `group.protocol=classic`(기본값)을 유지하는지 여부는 별도 branch 결정 필요
|
||||
- manual acknowledgement 구현 시 정확한 커밋 API 호출 시점(예: Spring Kafka `AckMode` 매핑) — 이 문서는 Kafka client 설정값만 제공, Spring Kafka wrapper 동작은 별도 자료 필요
|
||||
- poison message·역직렬화 실패 분류, retry topic·DLT 설계 — 이 문서 범위 밖(별도 branch 결정 + 별도 자료)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 `enable.auto.commit=false`(manual ack)로 override 할 것이 확실하므로, `auto.commit.interval.ms`(C4) 기본값은 실제로는 무관해질 수 있음 — branch `## 구현 가이드` 에서 명시적으로 정리 필요
|
||||
- `max.poll.interval.ms`/`max.poll.records`/bounded concurrency 조합의 실제 계약값(ca-skeleton 이 채택할 숫자) — 이 문서는 기본값만 제공, 구체적 조정값은 branch-note에서 별도 결정 필요
|
||||
- `partition.assignment.strategy` 를 기본값 그대로 둘지, 목록에서 `RangeAssignor` 를 제거해 순수 cooperative 로 고정할지는 별도 branch 결정 필요(이 문서는 두 옵션의 존재와 업그레이드 경로만 증명)
|
||||
|
||||
## 메모
|
||||
|
||||
- 원문에서 self-grep 은 통과했지만 위 Claims Extracted 표의 "Does not prove" 열에 요약 인용 없이 언급한 문장들(예: `group.instance.id` static member 예외, `group.protocol=consumer` 대체 동작)은 각각 raw HTML §max.poll.interval.ms, §session.timeout.ms, §heartbeat.interval.ms 문단 안에 그대로 존재함(`kafka-consumer-config-raw.html` line 284, 84, 63) — 별도 Claim 으로 분리하지 않고 인접 Claim 의 "Does not prove"에 원문 존재 사실만 남겼다. 필요 시 별도 Claim 으로 승격 가능.
|
||||
- `partition.assignment.strategy` 의 `RangeAssignor`/`RoundRobinAssignor`/`StickyAssignor`/`CooperativeStickyAssignor` 4종 개별 설명 문장도 raw HTML 에 존재(self-grep 가능)하나, 본 문서에서는 기본값 조합(C7)만 인용 — 개별 assignor 알고리즘 비교가 필요해지면 별도 인용 추가.
|
||||
- 저장 원본: 세션 scratchpad `kafka-consumer-config-raw.html`(원본 HTML, 87,328 bytes) + `kafka-consumer-config-fetch.txt`(평문 추출본, self-grep 대상).
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 branch 의 자매 관심사(Sources 미등록 — 별도 branch 소유): [[raw/branch-notes/feature-kafka-producer-runtime-contract]] 의 [[raw/official-docs/kafka-producer-configs-delivery-timeout]] — producer 측 delivery/retry/idempotence 설정
|
||||
- 같은 topic 인접 관심사: [[raw/official-docs/kafka-message-delivery-semantics-design]] — at-least-once/exactly-once 정의, idempotent/transactional semantics
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: official-doc / Apache Kafka — KafkaConsumer Javadoc (offset commit semantics — auto vs manual, commitSync/commitAsync)
|
||||
source_type: official-doc
|
||||
url: https://kafka.apache.org/40/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, idempotency]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Apache Kafka — KafkaConsumer Javadoc (offset commit semantics — auto vs manual, commitSync/commitAsync)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**. 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton 의 Kafka consumer 가 "application use case 성공 + inbox/비즈니스 트랜잭션 커밋 이후에만 offset 을 커밋(ack)" 하는 계약을 채택하고 `enable.auto.commit` 자동 커밋을 기각하는 근거 — 자동 커밋의 at-least-once 전제조건(poll 이후 전량 소비 필요)과 수동 커밋의 중복 창(commit 직전 crash → 재소비) 메커니즘을 공식 문서 수준에서 확정한다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://kafka.apache.org/40/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html
|
||||
- 아카이브 URL: (미제공 — 사용자 입력 없음)
|
||||
- 저자 / 조직: Apache Software Foundation — Apache Kafka 프로젝트 공식 Javadoc (`clients` 모듈, `org.apache.kafka.clients.consumer.KafkaConsumer`). URL 경로 `/40/` = Kafka 4.0 라인 Javadoc.
|
||||
- 발행일: 명시 없음 (Kafka 4.0.x 라인 최신 Javadoc)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-consumer-inbox-contract` branch 가 "application 성공 이후에만 manual ack" 계약을 채택하고 `enable.auto.commit`(자동 커밋)을 기각하려면, 자동 커밋이 실제로 무엇을 보장/전제하는지와 수동 커밋에서도 남는 중복 창이 공식 API 문서 수준에서 확정되어야 한다. 이 문서는 `KafkaConsumer` Javadoc의 "Usage Examples" 절(Automatic Offset Committing / Manual Offset Control)과 `commitSync`/`commitAsync` 메서드 계약에서 그 근거를 직접 인용한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [line 번호는 `/tmp/source-fetch-20260728171542.txt` 기준 — HTML→텍스트 변환 후 줄 번호. 원문은 여러 물리 줄로 개행되어 있어(Javadoc 소스 주석의 원 줄바꿈 보존), 인용 안 문장이 줄바꿈을 가로지르는 경우 그 사실을 명시한다.]
|
||||
|
||||
1. [line 147-148, §Automatic Offset Committing] "Setting enable.auto.commit means that offsets are committed automatically with a frequency controlled by the config auto.commit.interval.ms."
|
||||
|
||||
2. [line 190-193, §Automatic Offset Committing, Note] "Using automatic offset commits can also give you "at-least-once" delivery, but the requirement is that you must consume all data returned from each call to poll(Duration) before any subsequent calls, or before closing the consumer. If you fail to do either of these, it is possible for the committed offset to get ahead of the consumed position, which results in missing records."
|
||||
|
||||
3. [line 156, §Manual Offset Control] "a message should not be considered as consumed until it is completed processing."
|
||||
|
||||
4. [line 185-189, §Manual Offset Control] "the process could fail in the interval after the insert into the database but before the commit (even though this would likely just be a few milliseconds, it is a possibility). In this case the process that took over consumption would consume from last committed offset and would repeat the insert of the last batch of data. Used in this way Kafka provides what is often called "at-least-once" delivery guarantees, as each record will likely be delivered one time but in failure cases could be duplicated."
|
||||
|
||||
5. [line 879-881, `commitSync()` 메서드 설명] "This is a synchronous commit and will block until either the commit succeeds, an unrecoverable error is encountered (in which case it is thrown to the caller), or the timeout specified by default.api.timeout.ms expires (in which case a TimeoutException is thrown to the caller)." — 그리고 [line 1069-1070, `commitAsync(OffsetCommitCallback)` 메서드 설명] "This is an asynchronous call and will not block. Any errors encountered are either passed to the callback (if provided) or discarded."
|
||||
|
||||
> 총 5개 topic, 6개 원문 문장(인용 5는 commitSync/commitAsync 대조를 위해 2개 문장을 함께 인용). 전부 self-grep 통과(아래 Self-Grep 결과 참조 — 물리 줄바꿈을 포함하는 인용은 `grep -Pzo`로 개행 포함 패턴 매칭해 검증).
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-OFFSET-C1 | `enable.auto.commit=true` 는 offset 을 `auto.commit.interval.ms` 로 제어되는 주기로 자동 커밋한다 | [line 147-148] "Setting enable.auto.commit means that offsets are committed automatically with a frequency controlled by the config auto.commit.interval.ms." | official-reference | `enable.auto.commit=true` 채택 시 커밋 타이밍 메커니즘 일반 | 이 자동 커밋 방식이 안전(무손실)함을 증명하지 않는다 — 그 전제조건은 별도 `KAFKA-OFFSET-C2` |
|
||||
| KAFKA-OFFSET-C2 | 자동 오프셋 커밋으로도 "at-least-once" 전달을 얻을 수 있지만, 전제조건은 매 `poll(Duration)` 호출이 반환한 데이터를 다음 poll 또는 close 이전에 **전부 소비**해야 한다는 것이며, 이를 어기면 committed offset 이 consumed position 보다 앞서가 레코드 유실이 발생할 수 있다 | [line 190-193] "Using automatic offset commits can also give you "at-least-once" delivery, but the requirement is that you must consume all data returned from each call to poll(Duration) before any subsequent calls, or before closing the consumer. If you fail to do either of these, it is possible for the committed offset to get ahead of the consumed position, which results in missing records." | official-reference | `enable.auto.commit=true` 채택을 기각하는 리스크 근거 — 비동기 처리/bounded concurrency/backpressure(pause-resume) 를 쓰는 handler 는 이 "poll 마다 전량 즉시 소비" 전제를 지키기 어렵다 | ca-skeleton 의 실제 handler 실행 모델이 이 전제조건을 위반하는지 여부는 별도 구현 사실 확인이 필요 — 이 문서는 일반 리스크 조건만 서술하고 특정 구현을 언급하지 않는다 |
|
||||
| KAFKA-OFFSET-C3 | consumption 이 별도 processing logic 과 결합된 경우, 메시지는 그 처리가 완료되기 전까지 consumed 로 간주되면 안 된다 — 이것이 Manual Offset Control(수동 오프셋 제어, `enable.auto.commit=false`)을 쓰는 이유로 제시된다 | [line 156] "a message should not be considered as consumed until it is completed processing." | official-reference | manual commit 채택 근거 — "application/inbox 트랜잭션 커밋 이후에만 offset 을 ack" 하는 설계의 공식 문장 근거 | 정확히 어느 지점(같은 DB 트랜잭션 내부인지, 그 이후 별도 스텝인지)에서 commit 해야 하는지는 규정하지 않는다 — 그 구체 메커니즘은 project/branch 자체 결정(inbox 패턴) 소관 |
|
||||
| KAFKA-OFFSET-C4 | 수동 커밋을 쓰더라도 "DB insert 이후, commit 이전" 구간에 프로세스가 실패하면, 이어받은 프로세스가 마지막 committed offset 부터 재소비하여 마지막 batch 의 insert 를 반복하게 되고, 이것이 Kafka 가 "at-least-once" 라 부르는 전달 보증이며 레코드가 중복될 수 있다 | [line 185-189] "the process could fail in the interval after the insert into the database but before the commit (even though this would likely just be a few milliseconds, it is a possibility). In this case the process that took over consumption would consume from last committed offset and would repeat the insert of the last batch of data. Used in this way Kafka provides what is often called "at-least-once" delivery guarantees, as each record will likely be delivered one time but in failure cases could be duplicated." | official-reference | 수동 커밋을 채택해도 exactly-once 가 아니라 at-least-once + 재소비로 인한 중복 가능성이 남는다는 근거 — 별도 멱등(inbox/idempotency) 계층이 필요하다는 project 결정의 정당화 | 그 중복을 제거하는 멱등 메커니즘(unique key, owner token, inbox 테이블 등) 자체는 규정하지 않는다 — 이는 `DEC-CA-SKELETON-OPERATIONAL-CONTRACT-IDEMPOTENCY-OWNERSHIP-001` project 결정 소관이며 이 문서는 "중복이 가능하다"는 사실만 진술한다 |
|
||||
| KAFKA-OFFSET-C5 | `commitSync()` 는 커밋 성공, 복구 불가능한 에러(호출자에게 throw), 또는 `default.api.timeout.ms` 만료(`TimeoutException` throw) 중 하나가 발생할 때까지 블로킹하는 동기 호출이다. 반대로 `commitAsync(OffsetCommitCallback)` 은 블로킹하지 않는 비동기 호출이며, 발생한 에러는 콜백으로 전달되거나(콜백 없으면) 버려진다 | [line 879-881] "This is a synchronous commit and will block until either the commit succeeds, an unrecoverable error is encountered (in which case it is thrown to the caller), or the timeout specified by default.api.timeout.ms expires (in which case a TimeoutException is thrown to the caller)." / [line 1069-1070] "This is an asynchronous call and will not block. Any errors encountered are either passed to the callback (if provided) or discarded." | official-reference | manual ack 구현 시 `commitSync`/`commitAsync` 중 선택 기준 — 블로킹 허용 여부와 에러 가시성(throw vs callback/discard)의 근거 | 어느 것을 ca-skeleton 이 실제로 채택해야 하는지는 규정하지 않는다(트레이드오프만 서술) — 그 선택은 branch-local 결정(§구현 가이드) 소관. `commitAsync` 에러가 "discard" 될 수 있다는 것은 콜백 미제공 시의 위험을 알려줄 뿐, ca-skeleton 이 콜백을 반드시 제공해야 한다는 규범적 문장은 아니다 |
|
||||
|
||||
### Strength 근거
|
||||
|
||||
모든 claim 은 `official-reference` — Apache Kafka 프로젝트가 배포하는 공식 API Javadoc(`kafka.apache.org/40/javadoc/...`)이며 RFC/표준 수준(`official-standard`)은 아니지만 벤더 자체의 공식 reference 문서다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `enable.auto.commit=true` 의 커밋 타이밍 메커니즘 — `KAFKA-OFFSET-C1`
|
||||
- 자동 커밋으로 at-least-once 를 얻기 위한 전제조건과 그 위반 시 레코드 유실 리스크 — `KAFKA-OFFSET-C2`
|
||||
- Manual Offset Control 채택 근거("처리 완료 전까지 consumed 간주 금지") — `KAFKA-OFFSET-C3`
|
||||
- 수동 커밋에서도 남는 중복(재소비) 창의 메커니즘 — `KAFKA-OFFSET-C4`
|
||||
- `commitSync`/`commitAsync` 의 블로킹·에러 전달 방식 대조 — `KAFKA-OFFSET-C5`
|
||||
- 이 자료가 증명하지 **않는** 것 (중요 — branch 결정에 그대로 쓰면 안 됨):
|
||||
- **rebalance·`max.poll.interval.ms`·`max.poll.records`·pause/resume backpressure 의 상세 계약.** 이 raw 문서는 offset commit semantics(§핵심 인용 5개)만 발췌했다 — 같은 Javadoc 페이지 안에 rebalance/liveness 관련 문장이 존재하는 것은 확인했으나(§핵심 인용에는 미포함), branch 범위의 "rebalance 처리, poison/역직렬화 실패 분류, retry topic/DLT" 결정에는 **이 raw 문서를 근거로 쓸 수 없다** — 별도 raw 문서(또는 이 문서의 후속 확장) 필요
|
||||
- inbox 테이블 스키마·owner token 프로토콜 등 프로젝트 자체 멱등 메커니즘의 구체 설계 — 이 문서는 "중복이 가능하다"는 사실만 증명하고 그 해결책은 규정하지 않는다(`KAFKA-OFFSET-C4` 한계 참조)
|
||||
- `commitSync(Map)`/`commitAsync(Map, callback)` 의 partition-단위 세밀 commit 이 rebalance 중 재시도 시 offset 파라미터를 어떻게 갱신해야 하는지의 상세 — 이 문서에서 해당 오버로드의 `RebalanceInProgressException` 설명은 확인했으나 이번 5개 인용에는 포함하지 않음(필요 시 추가 인용으로 보강)
|
||||
- "어느 commit 방식이 ca-skeleton 에 더 적합한가"에 대한 규범적 권고 — 이 문서는 트레이드오프만 서술하고 prescriptive 하지 않음
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton Kafka consumer adapter 가 실제로 `enable.auto.commit=false` + 명시적 commit 호출 지점(inbox 트랜잭션 커밋 이후)을 코드로 구현했는지 — 코드 레벨 검증 필요
|
||||
- rebalance·backpressure·DLT 관련 결정에 쓸 별도 공식 근거(같은 Javadoc 페이지의 나머지 절 또는 `ConsumerRebalanceListener`/`ConsumerConfig` Javadoc) 추가 수집
|
||||
|
||||
## 메모
|
||||
|
||||
> 검증되지 않은 추론은 여기에만 둔다. wiki/concepts 로 옮길 때 재검토.
|
||||
|
||||
- WebFetch 도구의 1차 결과(요약 모델 처리본)는 "Automatic Offset Committing"/"Manual Offset Control" 문단을 paraphrase 했고 self-grep 검증이 불가능해 이 문서 작성에 **사용하지 않았다.** `curl` 로 raw HTML 을 받아 stdlib `re`/`html.unescape` 로 직접 텍스트를 추출(블록 태그 경계에서만 줄바꿈 삽입)한 뒤, 그 텍스트에서만 인용을 뽑았다 — `raw/official-docs/kafka-producer-javadoc-acks-idempotence.md` 작성 시 확립된 동일 절차.
|
||||
- 원문 문단이 Javadoc 소스 주석의 원래 줄바꿈을 그대로 보존하고 있어(브라우저 렌더링과 달리 raw text 추출본은 문장 중간에 개행이 남음), 인용 2/4/5 는 물리적으로 2줄 이상에 걸쳐 있다. Self-Grep 은 `grep -Pzo`(null-delimited, 개행 포함 패턴)로 수행했다 — 아래 Self-Grep Verification 참조.
|
||||
- 다음 작업 후보: 같은 `KafkaConsumer` Javadoc 페이지의 rebalance(`ConsumerRebalanceListener`)/backpressure(`pause`/`resume`) 절을 별도 인용 세트로 추가하거나 새 raw 문서로 분리해, branch 범위의 "rebalance·backpressure·DLT" 결정 근거를 보강해야 한다.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 vendor(Apache Kafka) 다른 raw 문서: [[raw/official-docs/kafka-producer-javadoc-acks-idempotence]] — producer 측 idempotence/ordering/flush 계약 (다른 branch, 같은 Javadoc 패밀리)
|
||||
- 같은 branch 인접 개념: [[raw/official-docs/kafka-message-delivery-semantics-design]] — at-least-once/exactly-once 정의, idempotent producer 의 broker-ID+sequence-number 중복 제거 (producer branch 근거지만 delivery semantics 용어 정의를 공유)
|
||||
- 이 자료를 인용한 wiki 요약: 아직 없음 (생성 시 링크)
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: official-doc / KIP-429 — Kafka Consumer Incremental Cooperative Rebalance Protocol
|
||||
source_type: official-doc
|
||||
url: https://cwiki.apache.org/confluence/display/KAFKA/KIP-429%3A+Kafka+Consumer+Incremental+Rebalance+Protocol
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# KIP-429 — Kafka Consumer Incremental Cooperative Rebalance Protocol
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 종류: Apache Kafka 공식 Kafka Improvement Proposal (KIP), Apache Software Foundation Confluence wiki (cwiki.apache.org) 호스팅.
|
||||
|
||||
## URL Fetch 기록
|
||||
|
||||
- 최초 시도 URL(요청됨): `https://cwiki.apache.org/confluence/display/KAFKA/KIP-429%3A+Kafka+Consumer+Incremental+Rebalance+Protocol` — WebFetch 도구는 HTTP 200을 반환했으나, 그 출력은 소형 모델이 재구성한 **paraphrase 요약**(예: "KIP-429 introduces an incremental cooperative rebalancing protocol...")이었고 원문 바이트와 일치하지 않아 Self-Grep 검증 대상으로 사용할 수 없었다.
|
||||
- 대체 조치: 동일 URL을 `curl` 로 직접 fetch(HTTP 200, 127,631 bytes)한 뒤, HTML 태그를 제거하는 결정론적 변환 스크립트로 순수 텍스트를 추출해 `/tmp/.../scratchpad/source-fetch-kip429.txt` (50,212 chars, 481 lines)에 저장. 이 파일을 Self-Grep 검증의 단일 기준으로 사용했다.
|
||||
- 인코딩 이슈 재시도는 불필요했다 — 원 URL(`%3A` 인코딩 그대로)이 curl 직접 fetch에서 정상 동작함.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton consumer 가 파티션 할당 전략으로 incremental cooperative rebalance(`cooperative-sticky`)를 채택할지의 근거 — eager 프로토콜은 rebalance 마다 소유한 모든 파티션을 revoke 하지만 cooperative 는 소유 파티션을 유지해 피해 범위를 줄이며, 그 결과 `onPartitionsRevoked` 가 호출되지 않을 수도 있어 revoke 시점 커밋(manual ack / inbox 트랜잭션 경계) 로직을 유일한 체크포인트로 설계하면 안 된다는 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://cwiki.apache.org/confluence/display/KAFKA/KIP-429%3A+Kafka+Consumer+Incremental+Rebalance+Protocol
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Boyang Chen (작성, Confluence 표기), A. Sophie Blee-Goldman (최종 수정, 2021-05-11) — Apache Kafka 프로젝트, Apache Software Foundation
|
||||
- 발행일: Confluence 표기에 최초 생성일은 없음 — "Created by Boyang Chen, last updated by A. Sophie Blee-Goldman on May 11, 2021" 만 명시
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-consumer-inbox-contract` 브랜치가 파티션 할당 전략(eager vs cooperative-sticky)을 결정하고 `ConsumerRebalanceListener` 콜백(특히 revoke 시점 커밋 로직) 설계를 재검토하기 위해서는, "cooperative 프로토콜에서 `onPartitionsRevoked` 가 호출되지 않을 수 있다"는 사실이 공식 문서로 뒷받침되어야 한다. 이 KIP 은 그 사실을 규정하는 원 사양 문서다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Status, line 44] "Current state :" [...] "Accepted (2.4.0)" (원문은 콜론 뒤 non-breaking space + 일반 space 조합이라 정확한 원본 공백 문자와 자간을 그대로 재현하지 않고 elide 표기로 분리 인용함 — 두 fragment 모두 grep 검증됨)
|
||||
|
||||
> [§Public Interfaces — `ConsumerPartitionAssignor.RebalanceProtocol` javadoc, line 312] "The {@link RebalanceProtocol#EAGER} rebalance protocol requires a consumer to always revoke all its owned" [...] (line 313) "partitions before participating in a rebalance event. It therefore allows a complete reshuffling of the assignment."
|
||||
|
||||
> [§Public Interfaces — 동 javadoc, line 315] "{@link RebalanceProtocol#COOPERATIVE} rebalance protocol allows a consumer to retain its currently owned" [...] (line 316) "partitions before participating in a rebalance event. The assignor should not reassign any owned partitions"
|
||||
|
||||
> [§ConsumerRebalanceListener and ConsumerPartitionAssignor Semantics, line 210] "Listener#onPartitionsLost : if the member has missed a rebalance and fallen out of the group, this new callback will be invoked on the set of all owned partitions (unless empty). The member will then rejoin the group."
|
||||
|
||||
> [§ConsumerRebalanceListener and ConsumerPartitionAssignor Semantics, line 224] "it is possible for #onPartitionsRevoked to never be invoked at all during a rebalance, and should not be relied on to signal that a rebalance has started."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KIP429-C1 | KIP-429 는 Apache Kafka 프로젝트가 공식 채택(Accepted)한 제안이며 Kafka 2.4.0 에 반영되었다 | [§Status, line 44] "Current state :" [...] "Accepted (2.4.0)" | `official-vendor-doc` | Kafka client 2.4.0 이상에서 incremental cooperative rebalance protocol 이 공식 기능으로 존재한다는 것 | ca-skeleton 이 실제로 Kafka client 2.4.0 이상 버전을 사용한다는 것(별도 의존성 버전 확인 필요), Spring Kafka wrapper 가 이 기능을 그대로 노출한다는 것 |
|
||||
| KIP429-C2 | EAGER rebalance protocol 은 consumer 가 rebalance event 에 참여하기 전 소유한 모든 파티션을 항상 revoke 하도록 요구하며, 그 결과 할당의 완전한 재구성(complete reshuffling)을 허용한다 | [§Public Interfaces, line 312-313] "The {@link RebalanceProtocol#EAGER} rebalance protocol requires a consumer to always revoke all its owned" [...] "partitions before participating in a rebalance event. It therefore allows a complete reshuffling of the assignment." | `official-vendor-doc` | Kafka consumer 의 (변경 전) 기본/전통적 rebalance protocol 동작(RangeAssignor·RoundRobinAssignor 등 EAGER 전용 assignor 사용 시) | EAGER 프로토콜이 ca-skeleton 환경에서 실측 downtime 을 유발한다는 것 — 이는 로컬/dev 측정으로 별도 검증 필요 |
|
||||
| KIP429-C3 | COOPERATIVE rebalance protocol 은 consumer 가 rebalance event 참여 전 현재 소유한 파티션을 유지하도록 허용하며, assignor 는 소유 파티션을 즉시 재할당하지 않고 다음 rebalance 에서 revoke 가 필요함을 표시만 한다 | [§Public Interfaces, line 315-316] "{@link RebalanceProtocol#COOPERATIVE} rebalance protocol allows a consumer to retain its currently owned" [...] "partitions before participating in a rebalance event. The assignor should not reassign any owned partitions" | `official-vendor-doc` | `cooperative-sticky` assignor(Kafka 2.4+)를 사용하는 plain consumer | 모든 커스텀 assignor 가 cooperative 를 지원한다는 것 — 문서는 오히려 기존 StickyAssignor 는 Cooperative 를 지원하도록 만들어지지 않았다고 별도 서술한다(§메모 참조, 이 Claim 의 인용 범위 밖) |
|
||||
| KIP429-C4 | cooperative protocol 에서 `onPartitionsLost` 콜백은 멤버가 rebalance 를 놓치고 그룹에서 이탈했을 때 소유하고 있던 모든 파티션 집합(비어있지 않은 경우)에 대해 호출되며, 이후 멤버는 그룹에 재가입한다 | [§ConsumerRebalanceListener and ConsumerPartitionAssignor Semantics, line 210] "Listener#onPartitionsLost : if the member has missed a rebalance and fallen out of the group, this new callback will be invoked on the set of all owned partitions (unless empty). The member will then rejoin the group." | `official-vendor-doc` | cooperative rebalance listener 구현 시 "멤버십 손실"(그룹 이탈) 경로의 파티션 정리 로직 설계 | `onPartitionsLost` 이후 `InboxStorePort` 의 커밋/롤백을 어떤 방식으로 처리해야 하는지 — 그 메커니즘 자체는 이 KIP 가 아니라 본 branch 의 별도 설계 결정 |
|
||||
| KIP429-C5 | cooperative protocol 에서 `onPartitionsRevoked` 콜백은 rebalance 도중 전혀 호출되지 않을 수 있으며, rebalance 가 시작되었음을 알리는 신호로 신뢰해서는 안 된다 | [§ConsumerRebalanceListener and ConsumerPartitionAssignor Semantics, line 224] "it is possible for #onPartitionsRevoked to never be invoked at all during a rebalance, and should not be relied on to signal that a rebalance has started." | `official-vendor-doc` | revoke 시점 커밋/flush 로직을 rebalance 감지의 유일한 체크포인트로 설계하면 안 된다는 근거 — manual ack·inbox 커밋 시점을 `onPartitionsRevoked` 호출에만 의존하도록 설계해서는 안 됨 | `onPartitionsAssigned` 이 이 상황에서 ca-skeleton 의 안전한 대체 체크포인트로 항상 충분하다는 것 — 원문은 "always be called"만 말할 뿐, ca-skeleton `InboxStorePort` 트랜잭션 경계 설계 자체는 이 KIP 로 증명되지 않고 별도 구현·검증 필요 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `KIP429-C1`: KIP-429 가 공식 채택되어 Kafka 2.4.0 에 반영된 사실
|
||||
- `KIP429-C2`: EAGER protocol 의 "항상 전체 revoke 후 재참여" 정의
|
||||
- `KIP429-C3`: COOPERATIVE protocol 의 "소유 파티션 유지, 필요한 것만 다음 rebalance 에서 revoke" 정의
|
||||
- `KIP429-C4`: `onPartitionsLost` 가 그룹 이탈(멤버십 손실) 시에만 호출된다는 것
|
||||
- `KIP429-C5`: `onPartitionsRevoked` 가 cooperative protocol 에서 호출되지 않을 수 있다는 것 — rebalance 시작 신호로 신뢰 불가
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- ca-skeleton 이 실제로 `cooperative-sticky` assignor 를 설정했다는 것(구현 사실) — 이 문서는 프로토콜 사양일 뿐 프로젝트 구현 증거가 아님
|
||||
- Spring Kafka 의 `ConcurrentKafkaListenerContainerFactory`/`ContainerProperties` 가 이 콜백 시맨틱을 그대로 노출하는지 — Spring Kafka 자체의 공식 문서로 별도 확인 필요
|
||||
- `InboxStorePort` 트랜잭션 커밋을 어느 콜백(assigned/lost/revoked)에 바인딩해야 하는지의 **구현 방법** — 이 KIP 은 원칙(revoked 가 신뢰 불가능한 신호)만 규정하며 ca-skeleton 의 구체적 커밋 바인딩은 branch 의 별도 설계 결정
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 사용하는 Kafka client 실제 버전이 2.4.0 이상인지(빌드 의존성 확인)
|
||||
- Spring Kafka `ConsumerRebalanceListener` 래퍼가 `onPartitionsLost` 를 KIP-429 원문 시맨틱 그대로 전달하는지(Spring Kafka 공식 문서 별도 인용 필요)
|
||||
|
||||
## 메모
|
||||
|
||||
- 확인됨(grep 검증, line 360, 이번 5개 인용에는 미포함): "The existing StickyAssignor was not made to support Cooperative to ensure users follow the smooth upgrade path outlined below..." — 기존 `StickyAssignor` 는 cooperative 미지원. ca-skeleton 이 `cooperative-sticky` 가 아닌 다른 sticky 계열 assignor 를 검토할 경우 이 문장을 별도 Claim 으로 승격해 인용할 것.
|
||||
- 업그레이드 경로(2회 rolling bounce) 관련 문장도 line 369 부근에서 grep 확인됨 — ca-skeleton 은 신규 프로젝트라 rolling upgrade 시나리오 자체가 branch 결정 범위 밖일 가능성이 높음(운영 중인 기존 컨슈머 그룹의 무중단 업그레이드가 아니라면). 필요 시 별도 Claim 화.
|
||||
- WebFetch 도구의 출력이 verbatim 이 아니라 paraphrase 라는 것을 이번에 직접 확인함 — 향후 동일 유형 작업에서는 curl 직접 fetch + 결정론적 HTML→text 변환을 기본 경로로 삼는 것이 안전하다.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 branch 의 다른 official-doc: [[raw/official-docs/kafka-message-delivery-semantics-design.md]] — delivery semantics(at-least-once/exactly-once) 정의, 인접 관심사
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: official-doc / Kafka Message Delivery Semantics — At-Least-Once, Idempotent Producer, Transactional Delivery Scope
|
||||
source_type: official-doc
|
||||
url: https://docs.confluent.io/kafka/design/delivery-semantics.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-producer-runtime-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, idempotency]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Kafka Message Delivery Semantics — At-Least-Once, Idempotent Producer, Transactional Delivery Scope
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Confluent Documentation "Kafka Message Delivery Guarantees" (Apache Kafka Design 섹션) — 특정 개정일 미표기.
|
||||
|
||||
## source_type
|
||||
|
||||
`official-doc` — 이 페이지는 하단에 "This website includes content developed at the Apache Software Foundation under the terms of the Apache License v2" 고지를 명시한다. 즉 Confluent 문서 사이트가 호스팅하지만, 인용 대상 내용 자체는 Apache Kafka 프로젝트의 공식 semantics 설계 서술이다. **원 요청 URL은 접근 불가했다** — 아래 "URL Fetch 실패 기록" 참조.
|
||||
|
||||
## URL Fetch 실패 기록 (원 요청 URL)
|
||||
|
||||
- 요청된 URL: `https://kafka.apache.org/documentation/#semantics`
|
||||
- 시도 1 (WebFetch, `#semantics` 앵커 포함): nav/redirect 셸만 반환, "Message Delivery Semantics" 섹션 본문 없음
|
||||
- 시도 2 (WebFetch, 앵커 제거 `https://kafka.apache.org/documentation/`): 동일 — 네비게이션 메뉴·릴리스 목록·footer만 포함
|
||||
- 시도 3 (`curl` 직접 fetch, `kafka.apache.org/documentation.html` 및 `/40/`, `/39/`, `/38/`, `/37/`, `/36/documentation.html` 버전별 순회): 전부 `<title>Documentation Redirect Legacy | Apache Kafka</title>` 셸 페이지(19,985 bytes 동일), "Message Delivery" 문자열 0건
|
||||
- **판정**: 현재 `kafka.apache.org` 문서 사이트는 JS 렌더링 SPA로 전환되어 정적 fetch로는 본문에 도달 불가. 사용자가 사전 승인한 fallback 절차에 따라 `https://docs.confluent.io/kafka/design/delivery-semantics.html` 사용.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-producer-runtime-contract]] | ca-skeleton 이 producer 측 보증을 "idempotent producer 로 파티션 단위 중복 제거된 at-least-once" 로 표현하고, **DB 와 broker 를 걸친 exactly-once 는 주장하지 않는다**는 상속 결정(`DEC-CA-SKELETON-OPERATIONAL-CONTRACT-DELIVERY-SEMANTICS-001@1`)의 경계를 공식 문장으로 고정한다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL (요청됨, 접근 불가): https://kafka.apache.org/documentation/#semantics
|
||||
- 실제 인용 URL (fallback, 사용됨): https://docs.confluent.io/kafka/design/delivery-semantics.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Confluent, Inc. (문서 콘텐츠는 Apache Software Foundation 개발분 재게시, Apache License v2 고지)
|
||||
- 발행일: 명시 없음 (footer: "Copyright © Confluent, Inc. 2014-", 개정일 미표기)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-producer-runtime-contract` 브랜치가 상속한 project 결정(`DEC-CA-SKELETON-OPERATIONAL-CONTRACT-DELIVERY-SEMANTICS-001@1`: "end-to-end 메시징 보증은 at-least-once 전달과 멱등 consumer·inbox로 표현하고 DB와 broker를 걸친 exactly-once를 주장하지 않는다")의 경계를 Kafka 공식 semantics 개념(at-least-once/exactly-once 정의, idempotent producer, transactional producer, exactly-once 적용 범위)으로 뒷받침하기 위함. **단, "그 보장이 파티션 단위"라는 정확한 스코프 표현은 본 페이지 어디에도 없다** — Self-Grep 결과 및 §메모 참조.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Semantic guarantees] "At least once: This means messages are delivered one or more times. If there is a system failure, messages are never lost, but they may be delivered more than once."
|
||||
|
||||
> [§Semantic guarantees] "Exactly once: This is the preferred behavior in that each message is delivered once and only once. Messages are never lost or read twice even if some part of the system fails."
|
||||
|
||||
> [§Producer delivery — idempotent 옵션] "The idempotent delivery option guarantees that resending a message will not result in duplicate entries in the log, and that log order is maintained. To achieve this, the broker assigns each producer an ID and deduplicates messages using a sequence number that is sent by the producer with every message."
|
||||
|
||||
> [§Producer delivery — Exactly once (transactional)] "Exactly once - Starting with version 0.11.0.0, producers can utilize transactional delivery. This means a producer can request acknowledgment that messages were received and successfully replicated, and if it resends a message, it resends with idempotency, meaning existing messages are overwritten rather than duplicated."
|
||||
|
||||
> [§Exactly once support] "Kafka supports exactly-once delivery in Kafka Streams and uses transactional producers and consumers to provide exactly-once delivery when transferring and processing data between Kafka topics." [...] "Otherwise, by default Kafka guarantees at-least-once delivery."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-SEM-C1 | at-least-once 는 "메시지가 1회 이상 전달됨: 실패 시 유실되지 않지만 중복 전달될 수 있음" 으로 정의된다 | "At least once: This means messages are delivered one or more times. If there is a system failure, messages are never lost, but they may be delivered more than once." | `official-vendor-doc` | Kafka broker/producer/consumer 간 semantic guarantee 용어 정의 일반 | ca-skeleton 이 실제로 at-least-once 를 달성한다는 것(설정·구현 사실)은 증명하지 않는다 — 이는 용어 정의일 뿐 프로젝트 구현 증거가 아니다 |
|
||||
| KAFKA-SEM-C2 | exactly-once 는 "각 메시지가 정확히 한 번만 전달됨: 시스템 일부가 실패해도 유실되거나 두 번 읽히지 않음" 으로 정의되며 "선호되는(preferred) 동작" 으로 서술된다 | "Exactly once: This is the preferred behavior in that each message is delivered once and only once. Messages are never lost or read twice even if some part of the system fails." | `official-vendor-doc` | exactly-once 라는 용어 자체의 정의 | Kafka 가 **모든** 상황에서 exactly-once 를 실제로 제공한다는 것은 증명하지 않는다 — 페이지 자체가 바로 다음 문장에서 "many systems claim to provide exactly once ... but this might not always what you think it is" 라고 경고하며, 적용 범위는 KAFKA-SEM-C5 로 별도 한정된다 |
|
||||
| KAFKA-SEM-C3 | idempotent producer 옵션은 "메시지 재전송이 로그에 중복 항목을 만들지 않고, 로그 순서가 유지됨"을 보장하며, 이는 broker가 producer마다 ID를 부여하고 매 메시지에 동봉되는 sequence number로 중복 제거하는 방식으로 달성된다 | "The idempotent delivery option guarantees that resending a message will not result in duplicate entries in the log, and that log order is maintained. To achieve this, the broker assigns each producer an ID and deduplicates messages using a sequence number that is sent by the producer with every message." | `official-vendor-doc` | Kafka producer의 idempotent delivery(0.11.0.0+) 재시도 시 broker측 중복 제거 메커니즘 | **"파티션 단위(per-partition)" 라는 단어를 이 페이지는 전혀 쓰지 않는다.** "log" 라는 표현만 사용하며, Kafka의 로그가 topic-partition 단위로 존재한다는 사실은 이 문서 밖의 일반 지식으로 추론될 뿐 본문이 명시적으로 진술하지 않는다 — Self-Grep 실패 항목으로 아래 보고. 또한 producer 재시작(새 PID 발급) 시 이전 세션의 중복 제거 상태가 유지되는지도 이 문서는 언급하지 않는다 |
|
||||
| KAFKA-SEM-C4 | 0.11.0.0부터 producer는 transactional delivery를 쓸 수 있으며, 메시지 수신·복제 완료 ack를 요청할 수 있고, 재전송 시 idempotency로 기존 메시지를 덮어써 중복시키지 않는다(더 높은 지연 대신 더 높은 내구성) | "Exactly once - Starting with version 0.11.0.0, producers can utilize transactional delivery. This means a producer can request acknowledgment that messages were received and successfully replicated, and if it resends a message, it resends with idempotency, meaning existing messages are overwritten rather than duplicated." | `official-vendor-doc` | Kafka 내부(브로커/토픽/파티션)에 대한 transactional producer 동작 | **DB 트랜잭션과의 원자성은 증명하지 않는다.** 이 인용은 Kafka 브로커에 대한 producer의 ack/재전송 동작만 서술하며, 외부 데이터베이스 write 와 Kafka publish 를 하나의 원자적 단위로 묶는다는 진술은 어디에도 없다 |
|
||||
| KAFKA-SEM-C5 | Kafka Streams는 transactional producer/consumer를 이용해 "Kafka 토픽 간(between Kafka topics) 데이터를 옮기고 처리할 때" exactly-once를 제공하며, 그 외 consumer/producer 시스템은 Kafka Connect API의 자동 offset 관리를 써야 하고, 그렇지 않으면 Kafka는 기본적으로 at-least-once를 보장한다 | "Kafka supports exactly-once delivery in Kafka Streams and uses transactional producers and consumers to provide exactly-once delivery when transferring and processing data between Kafka topics." [...] "Otherwise, by default Kafka guarantees at-least-once delivery." | `official-vendor-doc` | Kafka 내장 exactly-once의 적용 범위(intra-Kafka topic-to-topic) vs 그 밖의 시스템 | **이 문장은 "DB와 broker를 걸친 exactly-once가 불가능하다"를 명시적으로 부정하지 않는다.** "between Kafka topics" 로 범위를 한정하고, 외부 시스템은 Connect API의 offset 관리에 위임한다고만 말할 뿐이며, 임의의 DB write 가 Kafka publish 와 원자적으로 결합될 수 있는지/없는지에 대한 직접 진술은 이 페이지에 없다 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모두 `official-vendor-doc` — RFC/표준(`official-standard`)이 아니라 Kafka 프로젝트(Apache Software Foundation 개발분, Confluent 재게시)의 공식 설계 문서 서술이다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `KAFKA-SEM-C1`: at-least-once 의 정의(1회 이상 전달, 실패 시 무손실이나 중복 가능)
|
||||
- `KAFKA-SEM-C2`: exactly-once 의 정의(정확히 1회, 무손실·무중복) 및 이 페이지가 이를 "선호되는 동작"으로 규정한다는 것
|
||||
- `KAFKA-SEM-C3`: idempotent producer 가 broker-assigned ID + per-message sequence number 로 재전송 시 로그 중복을 막는다는 메커니즘
|
||||
- `KAFKA-SEM-C4`: 0.11.0.0+ transactional producer 가 ack 요청 + idempotent 재전송(overwrite, not duplicate)을 제공한다는 것
|
||||
- `KAFKA-SEM-C5`: Kafka 내장 exactly-once 의 명시적 적용 범위가 "Kafka 토픽 간"이며, 그 밖의 시스템은 Connect API offset 관리에 의존한다는 것, 기본값은 at-least-once 라는 것
|
||||
- 이 자료가 증명하지 **않는** 것:
|
||||
- **idempotent producer 보장이 "파티션 단위"라는 정확한 스코프 문구.** 이 페이지는 그 단어를 쓰지 않는다(`KAFKA-SEM-C3` 참조) — branch-note 나 project 결정문에서 "파티션 단위"라는 표현을 쓰려면 이 문서만으로는 근거가 부족하고, Kafka producer configs 공식 문서(`enable.idempotence` 항목, Javadoc)로 별도 보강이 필요하다
|
||||
- Kafka transactional producer 가 DB write 와 원자적으로 결합된다는 것(`KAFKA-SEM-C4`, `KAFKA-SEM-C5`) — 이 문서 범위 안에서는 Kafka 내부(토픽/파티션) transactional write만 다루며, DB-broker 이중 쓰기(dual-write) 원자성에 대한 진술은 전혀 없다
|
||||
- Kafka 가 모든 상황에서 exactly-once 를 보장한다는 일반 주장 — 페이지 스스로 "many systems claim to provide exactly once delivery semantics, but this might not always be what you think it is" 라고 경고하며 범위를 한정한다
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- "파티션 단위 중복 제거"의 정확한 공식 문구 — Kafka producer configs 문서(`enable.idempotence`) 또는 Javadoc 별도 인용 필요
|
||||
- `acks=all` + idempotent producer 조합이 실제로 ca-skeleton 설정에 반영되는지 — 별도 구현 증거(코드/설정) 필요
|
||||
- Kafka Connect API 의 offset 관리가 실제로 임의 외부 DB(비-Connect 커스텀 consumer)에도 원자성을 제공하는지 — 이 페이지는 Connect 커넥터 예시(HDFS)만 들며 일반화하지 않는다
|
||||
|
||||
## 메모
|
||||
|
||||
- Self-Grep 실패 보고: "파티션 단위" 관련 원문 문구를 이 페이지(Confluent fallback)에서 찾지 못했다. 추측으로 채우지 않고 `KAFKA-SEM-C3`의 "Does not prove"에 명시했다. 필요 시 Apache Kafka producer configs 공식 문서(`enable.idempotence`)를 별도 raw 문서로 수집해 이 gap 을 메워야 한다.
|
||||
- 원 요청 URL(`kafka.apache.org/documentation/#semantics`)은 현재 정적 fetch로 접근 불가한 JS 렌더링 nav 셸이다 — 향후 재시도 시 브라우저 렌더링 기반 도구가 필요할 수 있다.
|
||||
- 같은 branch 의 다른 raw 문서(`raw/official-docs/spring-kafka-sending-messages-kafkatemplate.md`)가 이미 "acks=all + enable.idempotence 설정 자체"를 다루는 Kafka producer configs 공식 문서 보강이 필요하다고 지적한 바 있다 — 동일 gap.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 branch 의 다른 official-doc: [[raw/official-docs/spring-kafka-sending-messages-kafkatemplate.md]] — `KafkaTemplate.send()` 반환 타입/동기 대기 메커니즘 (같은 branch, 인접 관심사)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: official-doc / Apache Kafka Producer Configs — delivery.timeout.ms, retries, acks, enable.idempotence
|
||||
source_type: official-doc
|
||||
url: https://kafka.apache.org/documentation/#producerconfigs
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-producer-runtime-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, idempotency]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Apache Kafka Producer Configs — delivery.timeout.ms, retries, acks, enable.idempotence
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Apache Kafka 4.3 (`kafka.apache.org/43/configuration/producer-configs/`, `last-modified: 2026-07-11`) — 아래 "URL Fetch 경위" 참조.
|
||||
|
||||
## source_type
|
||||
|
||||
`official-doc` — Apache Software Foundation 이 직접 호스팅하는 Apache Kafka 프로젝트 공식 configuration reference.
|
||||
|
||||
## URL Fetch 경위 (원 요청 URL은 client-side JS redirect 셸)
|
||||
|
||||
- 요청된 URL: `https://kafka.apache.org/documentation/#producerconfigs`
|
||||
- 시도 1 (WebFetch): nav/release-dropdown/footer만 반환. "Redirecting..." 표시. producer config 본문 없음.
|
||||
- 시도 2 (WebFetch, `documentation.html#producerconfigs`): 동일 실패.
|
||||
- **원인 규명**: `curl` 로 원본 HTML 을 직접 받아보니 `<title>Documentation Redirect Legacy | Apache Kafka</title>` 셸이며, 내부 `<script>` 에 해시 앵커 → 실제 경로 매핑 테이블이 있다: `"#producerconfigs":"configuration/producer-configs/"`, 그리고 현재 버전 변수 `t="43"`. 즉 브라우저가 `window.location.replace("/43/configuration/producer-configs/")` 로 client-side redirect 하는 구조이며, 정적 WebFetch/curl 은 이 JS를 실행하지 못해 셸만 받는다.
|
||||
- **판정**: fallback(Confluent 미러) 없이 **원 저작자(Apache Kafka) 사이트의 실제 목적지 페이지**를 직접 특정해 fetch 가능했다. 사용한 실제 URL: `https://kafka.apache.org/43/configuration/producer-configs/` (HTTP 200, 146,845 bytes). 이 페이지를 `curl` 로 받아 HTML 태그 제거 후 텍스트로 저장, 아래 모든 인용은 이 저장 텍스트에 대해 `grep -nF` 로 self-grep 검증했다.
|
||||
- 저장 텍스트: `/tmp/claude-*/scratchpad/kafka-43-producer-configs.txt` (세션별 scratchpad, 1150 lines)
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-producer-runtime-contract]] | ca-skeleton 의 Kafka producer 가 재시도 예산을 `retries` 횟수가 아니라 bounded `delivery.timeout.ms` 로 표현하고, 압축·배치·요청 크기 상한을 계약값으로 고정한다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL (요청됨, 앵커): https://kafka.apache.org/documentation/#producerconfigs
|
||||
- 실제 인용 URL (client-side redirect 목적지, 버전 고정): https://kafka.apache.org/43/configuration/producer-configs/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Apache Software Foundation (Apache Kafka 프로젝트)
|
||||
- 발행일: 명시 없음 (페이지 자체는 버전 문서, HTTP `last-modified: Sat, 11 Jul 2026 01:38:20 GMT`)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-producer-runtime-contract` 브랜치가 "재시도 예산을 `retries` 횟수가 아니라 bounded `delivery.timeout.ms` 로 표현"하는 결정과 "압축·배치·요청 크기 상한을 계약값으로 고정"하는 결정의 공식 기준값(기본값·제약 문장)을 확보하기 위해 저장. `acks`/`enable.idempotence`/`max.in.flight.requests.per.connection` 의 상호 제약도 idempotent producer 설정을 검증 계약 test 로 만들 때 그대로 근거가 된다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§delivery.timeout.ms] "An upper bound on the time to report success or failure after a call to send() returns." [...] "The value of this config should be greater than or equal to the sum of request.timeout.ms and linger.ms." (Default: `120000` — 2 minutes)
|
||||
|
||||
> [§retries] "Requests will be retried this many times until they succeed, fail with a non-transient error, or the delivery.timeout.ms expires." [...] "Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior." (Default: `2147483647`)
|
||||
|
||||
> [§acks] "Note that enabling idempotence requires this config value to be 'all'." (Default: `all`)
|
||||
|
||||
> [§enable.idempotence] "Note that enabling idempotence requires max.in.flight.requests.per.connection to be less than or equal to 5 (with message ordering preserved for any allowable value), retries to be greater than 0, and acks must be 'all'." [...] "Idempotence is enabled by default if no conflicting configurations are set." (Default: `true`)
|
||||
|
||||
> [§max.in.flight.requests.per.connection] "Note that if this configuration is set to be greater than 1 and enable.idempotence is set to false, there is a risk of message reordering after a failed send due to retries (i.e., if retries are enabled); if retries are disabled or if enable.idempotence is set to true, ordering will be preserved." [...] "Additionally, enabling idempotence requires the value of this configuration to be less than or equal to 5, because broker only retains at most 5 batches for each producer." (Default: `5`)
|
||||
|
||||
> [§buffer.memory / §max.block.ms] "If records are sent faster than they can be delivered to the server the producer will block for max.block.ms after which it will fail with an exception." (buffer.memory Default: `33554432`, max.block.ms Default: `60000` — 1 minute)
|
||||
|
||||
> [§batch.size / §linger.ms] "This linger.ms setting defaults to 5, which means the producer will wait for 5ms or until the record batch is of batch.size (whichever happens first) before sending the record batch." (batch.size Default: `16384`, linger.ms Default: `5`)
|
||||
|
||||
> [§compression.type] "The default is none (i.e. no compression)." [...] "Compression is of full batches of data, so the efficacy of batching will also impact the compression ratio (more batching means better compression)."
|
||||
|
||||
> 참고 (self-grep 검증됨, 상기 인용 문맥 안): `max.request.size` Default: `1048576` — "The maximum size of a request in bytes." (§max.request.size)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-PRODCFG-C1 | `delivery.timeout.ms` 는 `send()` 반환 후 성공/실패를 보고하기까지의 상한이며, 기본값은 120000(2분)이다. 이 값은 `request.timeout.ms` + `linger.ms` 의 합보다 크거나 같아야 한다는 제약이 있다 | "An upper bound on the time to report success or failure after a call to send() returns." ... "The value of this config should be greater than or equal to the sum of request.timeout.ms and linger.ms." | `official-vendor-doc` | `request.timeout.ms`·`linger.ms` 를 override 할 때 `delivery.timeout.ms` 를 재계산해야 한다는 설정 제약 일반 | ca-skeleton 워크로드에 120000ms 가 적절한 SLA 인지는 증명하지 않는다. 위반 시 어떤 예외가 던져지는지(타입)도 이 인용에는 없다 |
|
||||
| KAFKA-PRODCFG-C2 | `retries` 는 사실상 무제한 기본값(2147483647)이며, 재시도 루프는 명시적 횟수가 아니라 `delivery.timeout.ms` 만료로 종료된다. 공식 문서는 `retries` 를 직접 조정하지 말고 `delivery.timeout.ms` 로 재시도 예산을 통제하라고 권고한다 | "Requests will be retried this many times until they succeed, fail with a non-transient error, or the delivery.timeout.ms expires." ... "Users should generally prefer to leave this config unset and instead use delivery.timeout.ms to control retry behavior." | `official-vendor-doc` | producer 재시도 예산을 카운트 기반이 아니라 시간 기반(`delivery.timeout.ms`)으로 설계하는 근거 | 단일 재시도 attempt 간 backoff 간격(`retry.backoff.ms`)이나, `delivery.timeout.ms` 만료 전 실제 몇 회 재시도가 일어나는지는 증명하지 않는다 |
|
||||
| KAFKA-PRODCFG-C3 | `enable.idempotence` 가 활성화되려면 `max.in.flight.requests.per.connection` ≤ 5, `retries` > 0, `acks` = `'all'` 이어야 하고, 상충 설정이 없으면 idempotence 는 **기본값으로 활성화**된다(`enable.idempotence` Default: `true`, `acks` Default: `all`) | "Note that enabling idempotence requires max.in.flight.requests.per.connection to be less than or equal to 5 (with message ordering preserved for any allowable value), retries to be greater than 0, and acks must be 'all'." ... "Idempotence is enabled by default if no conflicting configurations are set." | `official-vendor-doc` | ca-skeleton 이 별도 override 없이 producer 기본 설정만 써도 idempotent producer 계약이 성립한다는 근거(세 조건의 기본값이 이미 요건을 만족: acks=all, retries=2147483647>0, max.in.flight=5) | acks=1 등으로 명시적으로 override 하면서 `enable.idempotence` 를 명시하지 않을 경우 idempotence 가 **조용히 비활성화**된다는 것 외의 상세 오류 처리(예외 메시지 등)는 증명하지 않는다 |
|
||||
| KAFKA-PRODCFG-C4 | `max.in.flight.requests.per.connection` 기본값은 5이며, idempotence 활성 시 이 값은 5 이하로 제한된다(broker가 producer당 최대 5개 배치만 유지하기 때문). `enable.idempotence=false` 이면서 이 값이 1 초과이고 retries 가 활성화된 경우 메시지 순서가 바뀔 위험이 있고, retries 비활성 또는 idempotence 활성 시에는 순서가 보존된다 | "Note that if this configuration is set to be greater than 1 and enable.idempotence is set to false, there is a risk of message reordering after a failed send due to retries (i.e., if retries are enabled); if retries are disabled or if enable.idempotence is set to true, ordering will be preserved." ... "Additionally, enabling idempotence requires the value of this configuration to be less than or equal to 5, because broker only retains at most 5 batches for each producer." | `official-vendor-doc` | idempotent producer 하에서 in-flight 요청 수를 5(기본값)로 두어도 순서 보장이 유지된다는 근거 — branch 범위의 "key/partition 순서 보장 경계" 결정에 직접 적용 | 5보다 큰 값을 idempotence 활성 상태에서 설정했을 때 정확히 어떤 예외/오류가 발생하는지는 "previous batches may be removed on broker side" 이상으로 구체화하지 않는다 |
|
||||
| KAFKA-PRODCFG-C5 | `buffer.memory`(기본값 33554432 byte)가 소진되면 producer 는 `max.block.ms`(기본값 60000ms, 1분) 동안 블로킹한 뒤 예외로 실패한다 — 즉 producer 의 블로킹은 무한이 아니라 두 설정값으로 상한이 걸린 계약이다 | "If records are sent faster than they can be delivered to the server the producer will block for max.block.ms after which it will fail with an exception." | `official-vendor-doc` | `buffer.memory` + `max.block.ms` 조합을 "bounded backpressure" 계약값으로 고정하는 근거 | 실패 시 던져지는 예외의 정확한 타입(`org.apache.kafka.common.errors.TimeoutException` 등)은 이 인용문 자체에는 명시되지 않는다 |
|
||||
| KAFKA-PRODCFG-C6 | `batch.size`(기본값 16384 byte)는 파티션당 배치 크기의 상한이고, `linger.ms`(기본값 5ms, Kafka 4.0에서 0→5로 기본값 변경)는 그 상한에 도달하지 않았을 때 대기하는 시간 상한이다 — 배치 크기 도달 또는 linger 만료 중 먼저 오는 조건에 전송된다 | "This linger.ms setting defaults to 5, which means the producer will wait for 5ms or until the record batch is of batch.size (whichever happens first) before sending the record batch." | `official-vendor-doc` | `batch.size` + `linger.ms` 를 배치·지연 상한 계약값으로 고정하는 근거 | ca-skeleton 실제 topic/partition 개수·처리량에서 이 기본값 조합이 처리율/지연 목표를 만족하는지는 증명하지 않는다(벤치마크 필요) |
|
||||
| KAFKA-PRODCFG-C7 | `compression.type` 기본값은 `none`(무압축)이며, 압축은 배치 단위로 이뤄지므로 배치 효율이 압축률에도 영향을 준다 | "The default is none (i.e. no compression)." ... "Compression is of full batches of data, so the efficacy of batching will also impact the compression ratio (more batching means better compression)." | `official-vendor-doc` | 압축을 계약값으로 고정할 때 `batch.size`/`linger.ms` 와 함께 고려해야 한다는 근거(배치가 작으면 압축 이득도 작음) | 어떤 `compression.type`(gzip/snappy/lz4/zstd) 이 ca-skeleton 워크로드에 최적인지는 증명하지 않는다 — 그 비교는 이 문서 범위 밖 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모두 `official-vendor-doc` — Apache Kafka 프로젝트(Apache Software Foundation) 가 직접 게시한 configuration reference. `official-standard`(RFC/표준)는 아니다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `KAFKA-PRODCFG-C1`: `delivery.timeout.ms` 정의·기본값·제약(≥ request.timeout.ms + linger.ms)
|
||||
- `KAFKA-PRODCFG-C2`: `retries` 기본값(사실상 무제한) + `delivery.timeout.ms` 가 재시도 예산을 지배한다는 권고
|
||||
- `KAFKA-PRODCFG-C3`: idempotence 활성 요건(max.in.flight≤5, retries>0, acks=all) + 상충 없으면 기본 활성화
|
||||
- `KAFKA-PRODCFG-C4`: `max.in.flight.requests.per.connection` 기본값(5)과 idempotence 하 상한(5)·순서 보존 조건
|
||||
- `KAFKA-PRODCFG-C5`: `buffer.memory`/`max.block.ms` 조합이 bounded blocking 계약이라는 것
|
||||
- `KAFKA-PRODCFG-C6`: `batch.size`/`linger.ms` 조합이 배치 상한·지연 상한 계약이라는 것 (Kafka 4.0 에서 linger.ms 기본값 0→5 변경 포함)
|
||||
- `KAFKA-PRODCFG-C7`: `compression.type` 기본값(none)과 압축-배치 효율 관계
|
||||
- 이 자료가 증명하지 **않는** 것:
|
||||
- ca-skeleton 실제 워크로드에서 이 기본값들이 SLA(지연/처리율)를 만족한다는 것 — 벤치마크·측정 필요
|
||||
- idempotent producer 의 중복 제거가 "파티션 단위"라는 정확한 스코프 문구 — 본 문서(`enable.idempotence`, `max.in.flight.requests.per.connection`, `retries` 항목)는 "a single partition"(§retries 문맥, ordering 논의)이라는 표현은 쓰지만, idempotent 중복 제거 메커니즘 자체를 "per-partition" 이라고 명시하지 않는다. `[[raw/official-docs/kafka-message-delivery-semantics-design.md]]` 가 이미 지적한 동일 gap이 이 문서로도 메워지지 않는다 — 필요 시 Kafka Javadoc(`KafkaProducer`) 별도 인용 필요
|
||||
- `delivery.timeout.ms` 초과 시 던져지는 예외의 정확한 클래스명
|
||||
- 여러 `compression.type` 값 간 성능 비교
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 실제로 `acks`/`enable.idempotence`/`max.in.flight.requests.per.connection` 을 기본값 그대로 두는지, 아니면 override 하는지 — 별도 구현 증거(코드/설정) 필요
|
||||
- `delivery.timeout.ms`/`request.timeout.ms`/`linger.ms` 3개 값의 실제 계약값(ca-skeleton 이 채택할 숫자) — 이 문서는 기본값과 제약식만 제공, 구체적 조정값은 branch-note `## 구현 가이드` 에서 별도 결정 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- 원 요청 URL(`kafka.apache.org/documentation/#producerconfigs`)은 client-side JS redirect 셸이라 WebFetch 로는 본문에 도달 불가했다. `curl` 로 셸 HTML 을 받아 내부 `<script>` 의 해시→경로 매핑 테이블(`t="43"`, `"#producerconfigs":"configuration/producer-configs/"`)을 읽어 실제 목적지(`/43/configuration/producer-configs/`)를 특정했다. 동일 branch 의 `[[raw/official-docs/kafka-message-delivery-semantics-design.md]]` 는 같은 문제(`#semantics` 앵커)를 만나 Confluent fallback 을 썼는데, 본 문서는 redirect 매핑을 직접 풀어 **Apache 원문**에 도달했다 — 더 강한 출처.
|
||||
- HTML → 텍스트 변환은 직접 작성한 스크립트(태그 제거, `<td>`/`<th>` 를 ` | ` 로 표시)로 했다. Self-Grep 은 원본 HTML 문자열이 그대로 남아있는 문장 단위 인용에 대해서만 수행했고, 표 서식 기호(` | `) 자체는 인용에 포함하지 않았다.
|
||||
- `retries` 필드의 "Enabling idempotence requires this config value to be greater than 0." 문장(§retries, self-grep 가능)은 KAFKA-PRODCFG-C3 에서 이미 `enable.idempotence` 항목 인용으로 커버되므로 중복 인용하지 않았다.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 branch 의 다른 official-doc: [[raw/official-docs/kafka-message-delivery-semantics-design.md]] — at-least-once/exactly-once 정의, idempotent/transactional producer semantics (같은 branch, 인접 관심사, "파티션 단위" 표현 gap 공유)
|
||||
- 같은 branch 의 다른 official-doc: [[raw/official-docs/spring-kafka-sending-messages-kafkatemplate.md]] — `KafkaTemplate.send()` 반환 타입/동기 대기 메커니즘
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: official-doc / Apache Kafka — KafkaProducer Javadoc (acks, idempotent producer, ordering, flush/close)
|
||||
source_type: official-doc
|
||||
url: https://kafka.apache.org/40/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-producer-runtime-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, idempotency, ordering]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Apache Kafka — KafkaProducer Javadoc (acks, idempotent producer, ordering, flush/close)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**. 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-producer-runtime-contract]] | ca-skeleton 의 Kafka producer 런타임 계약에서 `acks=all` + idempotent producer 를 기본값으로 못박고, 순서 보장 경계(파티션 단위)와 graceful flush/close 의 의미를 메커니즘 수준(L1+)으로 확정하는 근거. **단, in-flight 상한(`max.in.flight.requests.per.connection`)에 대한 근거는 이 URL 범위 밖 — 아래 Usage Boundaries 참조.** |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://kafka.apache.org/40/javadoc/org/apache/kafka/clients/producer/KafkaProducer.html
|
||||
- 아카이브 URL: (미제공 — 사용자 입력 없음)
|
||||
- 저자 / 조직: Apache Software Foundation — Apache Kafka 프로젝트 공식 Javadoc (`clients` 모듈, `org.apache.kafka.clients.producer.KafkaProducer`). 페이지 타이틀 `"KafkaProducer (kafka 4.0.2 API)"` 로 버전 확인.
|
||||
- 발행일: 명시 없음 (Kafka 4.0.x 라인 최신 Javadoc — URL 경로 `/40/` = 4.0 branch)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-producer-runtime-contract` branch 가 `acks=all` + idempotent producer 를 기본값으로 못박으려면, acks 의 완료 판정 기준·idempotence 의 정확한 보장 범위와 전제조건·순서 보장 경계·flush/close 의 blocking semantics 가 공식 API 문서 수준에서 확정되어야 한다. 이 문서는 KafkaProducer 클래스 Javadoc 원문에서 그 근거를 직접 인용한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [source: `KafkaProducer` class-level Javadoc, 클래스 상단 설명 문단] (line 번호는 `/tmp/source-fetch-20260728-165959.txt` 기준 — HTML→텍스트 변환 후 줄 번호)
|
||||
|
||||
1. [line 34] "The acks config controls the criteria under which requests are considered complete." [...] "The default setting "all" will result in blocking on the full commit of the record, the slowest but most durable setting."
|
||||
|
||||
2. [line 40] "The idempotent producer strengthens Kafka's delivery semantics from at least once to exactly once delivery." [...] "In particular producer retries will no longer introduce duplicates."
|
||||
|
||||
3. [line 41] "From Kafka 3.0, the enable.idempotence configuration defaults to true." [...] "When enabling idempotence, retries config will default to Integer.MAX_VALUE and the acks config will default to all."
|
||||
|
||||
4. [line 42] "the producer can only guarantee idempotence for messages sent within a single session."
|
||||
|
||||
5. [line 257, `send(ProducerRecord, Callback)` 메서드 설명] "Callbacks for records being sent to the same partition are guaranteed to execute in order."
|
||||
|
||||
6. [line 261, `send(ProducerRecord, Callback)` 메서드 설명] "it is possible to continue sending after receiving an OutOfOrderSequenceException, but doing so can result in out of order delivery of pending messages." [...] "To ensure proper ordering, you should close the producer and create a new instance."
|
||||
|
||||
7. [line 279, `flush()` 메서드 설명] "Invoking this method makes all buffered records immediately available to send (even if linger.ms is greater than 0) and blocks on the completion of the requests associated with these records." — 그리고 "A request is considered completed when it is successfully acknowledged according to the acks configuration you have specified or else it results in an error."
|
||||
|
||||
8. [line 346, `close()` 메서드 설명] "Close this producer. This method blocks until all previously sent requests complete. This method is equivalent to close(Long.MAX_VALUE, TimeUnit.MILLISECONDS)."
|
||||
|
||||
9. [line 347, `close()` 메서드 설명] "If close() is called from Callback, a warning message will be logged and close(0, TimeUnit.MILLISECONDS) will be called instead."
|
||||
|
||||
10. [line 38, 클래스 상단 설명 문단] "The buffer.memory controls the total amount of memory available to the producer for buffering." [...] "When the buffer space is exhausted additional send calls will block. The threshold for time to block is determined by max.block.ms after which it throws a TimeoutException."
|
||||
|
||||
> 총 10개 인용 (요청된 5개 우선순위 topic 을 모두 커버하기 위해 3~5개 권장 범위를 초과 — 각 topic 이 "보장 내용 + 전제조건/한계"의 2개 문장을 요구했기 때문). 전부 self-grep 통과.
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| KAFKA-PROD-JD-C1 | `acks=all`(기본값)은 요청 완료 판정 기준을 "레코드의 full commit 대기"로 설정하며, 가장 느리지만 가장 durable 한 설정이다 | [line 34] "The default setting "all" will result in blocking on the full commit of the record, the slowest but most durable setting." | official-reference | KafkaProducer 의 `acks` 설정값별 완료 판정 기준 일반 | 이 문서는 "full commit" 이 정확히 몇 개의 in-sync replica(ISR)를 의미하는지, `min.insync.replicas` 와 `acks=all` 의 관계를 설명하지 않는다(ISR 단어 자체가 이 페이지에 없음). 또한 "항상 acks=all 을 써야 한다"는 권고문도 아니다 — 트레이드오프(느림) 서술만 있음. |
|
||||
| KAFKA-PROD-JD-C2 | idempotent producer 는 Kafka 의 전달 시맨틱을 at-least-once 에서 exactly-once 로 강화하며, producer 재시도가 더 이상 중복을 유발하지 않는다 | [line 40] "The idempotent producer strengthens Kafka's delivery semantics from at least once to exactly once delivery. In particular producer retries will no longer introduce duplicates." | official-reference | `enable.idempotence=true` 일 때 producer 재전송으로 인한 duplicate 제거 보장 | DB write 등 producer 바깥의 side effect 까지 포함한 end-to-end exactly-once 를 주장하지 않는다. 이 문서 자체가 이 범위를 명시하지 않으므로 확대 해석 금지 — project 결정(`DEC-CA-SKELETON-OPERATIONAL-CONTRACT-DELIVERY-SEMANTICS-001@1`, at-least-once + 멱등 consumer)과 병기해야 함. |
|
||||
| KAFKA-PROD-JD-C3 | Kafka 3.0+ 부터 `enable.idempotence` 기본값이 `true`이며, idempotence 활성 시 `retries`는 `Integer.MAX_VALUE`, `acks`는 `all` 로 자동 기본 설정된다 | [line 41] "From Kafka 3.0, the enable.idempotence configuration defaults to true. When enabling idempotence, retries config will default to Integer.MAX_VALUE and the acks config will default to all." | official-reference | idempotent producer 활성 시 `acks`/`retries` 자동 강제값의 근거 | `max.in.flight.requests.per.connection` 상한과 idempotence 의 관계는 **이 URL(KafkaProducer 클래스 Javadoc)에 언급이 없다** — self-grep 결과 0건(negative control 확인됨). 순서 보장을 위한 in-flight 상한 수치는 별도 `ProducerConfig` 공식 문서로 보강 필요(`UNSUPPORTED_DECISION` 후보 — 아래 Usage Boundaries 참조). |
|
||||
| KAFKA-PROD-JD-C4 | producer 는 단일 세션(session) 내에서 전송된 메시지에 대해서만 idempotence 를 보장한다 | [line 42] "the producer can only guarantee idempotence for messages sent within a single session." | official-reference | producer 재시작/재생성 시 idempotence 보장이 리셋됨을 아는 근거 | "세션"의 정확한 경계(예: producer epoch 변경 시점, transactional.id fencing 과의 관계)를 상세히 정의하지 않는다. |
|
||||
| KAFKA-PROD-JD-C5 | 같은 파티션으로 전송되는 레코드의 콜백은 전송 순서대로 실행됨이 보장된다 | [line 257] "Callbacks for records being sent to the same partition are guaranteed to execute in order." | official-reference | 파티션 단위(partition-level) 순서 보장의 1차 근거 | 파티션을 넘나드는 전역 순서를 보장한다고 말하지 않는다. 또한 이 순서 보장이 `max.in.flight.requests.per.connection` 값에 어떻게 의존하는지는 이 페이지에 없다(negative control 확인됨). |
|
||||
| KAFKA-PROD-JD-C6 | idempotence 활성 상태에서 `OutOfOrderSequenceException` 수신 후 재전송을 계속하면 pending 메시지의 out-of-order 전달이 발생할 수 있으며, 순서를 보장하려면 producer 를 close 하고 새 인스턴스를 만들어야 한다 | [line 261] "it is possible to continue sending after receiving an OutOfOrderSequenceException, but doing so can result in out of order delivery of pending messages. To ensure proper ordering, you should close the producer and create a new instance." | official-reference | idempotent producer 에서 순서가 깨질 수 있는 구체적 실패 경로와 복구 절차(재발급) | 이 실패가 `max.in.flight.requests.per.connection > 5` 조합에서만 발생하는지, 혹은 다른 조건에서도 발생하는지는 이 페이지가 설명하지 않는다. |
|
||||
| KAFKA-PROD-JD-C7 | `flush()`는 buffer 에 있는 모든 레코드를 즉시 전송 가능 상태로 만들고, 그 요청들이 acks 설정에 따라 성공 ack 되거나 에러가 날 때까지 블로킹한다 | [line 279] "Invoking this method makes all buffered records immediately available to send (even if linger.ms is greater than 0) and blocks on the completion of the requests associated with these records." / "A request is considered completed when it is successfully acknowledged according to the acks configuration you have specified or else it results in an error." | official-reference | graceful flush 의 완료 판정 기준(acks 설정에 연동됨) | `flush()`에는 timeout 파라미터가 없다(무기한 대기)는 것 외의 취소/중단 메커니즘은 설명하지 않는다. send 콜백 안에서 호출하면 데드락이라는 점은 별도 문장(같은 메서드 doc 후반)에 있으나 본 claim 인용에는 포함하지 않음. |
|
||||
| KAFKA-PROD-JD-C8 | `close()`는 기본적으로 이전에 전송된 모든 요청이 완료될 때까지 블로킹하며 `close(Long.MAX_VALUE, TimeUnit.MILLISECONDS)`와 동일하다. 콜백 내부에서 `close()`가 호출되면 sender thread 의 self-join 데드락을 피하기 위해 `close(0, TimeUnit.MILLISECONDS)`로 대체 실행된다 | [line 346] "Close this producer. This method blocks until all previously sent requests complete. This method is equivalent to close(Long.MAX_VALUE, TimeUnit.MILLISECONDS)." / [line 347] "If close() is called from Callback, a warning message will be logged and close(0, TimeUnit.MILLISECONDS) will be called instead." | official-reference | graceful shutdown 시 `close()` 기본 blocking 동작과 콜백 내 호출의 예외적 non-blocking 동작 | `close(Duration timeout)` 오버로드에서 "timeout 초과 시 unsent/unacknowledged record 를 즉시 fail 처리한다"는 별도 동작은 이 두 quote 에 포함되지 않는다(문서 내 다른 위치, 별도 claim 필요 시 추가 인용). |
|
||||
| KAFKA-PROD-JD-C9 | `buffer.memory`가 소진되면 추가 `send()` 호출이 블로킹되며, 블로킹 대기 시간의 상한은 `max.block.ms`이고 이를 초과하면 `TimeoutException`이 발생한다 | [line 38] "The buffer.memory controls the total amount of memory available to the producer for buffering." / "When the buffer space is exhausted additional send calls will block. The threshold for time to block is determined by max.block.ms after which it throws a TimeoutException." | official-reference | producer buffer backpressure 메커니즘(client-side back-pressure)의 근거 | `max.block.ms` 초과 시 애플리케이션이 취해야 할 재시도/서킷브레이커 전략까지 규정하지 않는다 — 그건 애플리케이션 책임이라는 것도 명시하지 않는다(단순히 예외 발생만 서술). |
|
||||
|
||||
### Strength 근거
|
||||
|
||||
모든 claim 은 `official-reference` — Apache Kafka 프로젝트가 배포하는 공식 API Javadoc(`kafka.apache.org/40/javadoc/...`)이며 RFC/표준 수준(`official-standard`)은 아니지만 벤더 자체의 공식 reference 문서다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `acks=all`의 완료 판정 기준(full commit 대기, 가장 durable, 가장 느림) — `KAFKA-PROD-JD-C1`
|
||||
- idempotent producer 의 exactly-once(중복 제거) 보장과 그 세션 스코프 한계 — `KAFKA-PROD-JD-C2`, `C4`
|
||||
- idempotence 활성 시 `acks=all`/`retries=MAX_VALUE` 자동 기본값 — `KAFKA-PROD-JD-C3`
|
||||
- 파티션 단위 콜백 순서 보장과 idempotence 하에서 순서가 깨질 수 있는 구체적 실패 경로(`OutOfOrderSequenceException`) — `C5`, `C6`
|
||||
- `flush()`/`close()`의 기본 blocking 의미론 — `C7`, `C8`
|
||||
- `buffer.memory`/`max.block.ms` backpressure 메커니즘 — `C9`
|
||||
- 이 자료가 증명하지 **않는** 것 (중요 — branch 결정에 그대로 쓰면 안 됨):
|
||||
- **`max.in.flight.requests.per.connection` 관련 어떤 문장도 이 URL 에 없다.** self-grep negative control로 확인(`grep -nF 'max.in.flight.requests.per.connection'` → 0 매치). 따라서 "idempotence 활성 시 in-flight 상한이 5로 제한되어 순서가 보장된다"는 흔히 알려진 사실은 **이 raw 문서만으로는 뒷받침되지 않는다.** branch 의 §구현 가이드에서 in-flight 상한 수치를 명시하려면 `ProducerConfig` 공식 문서(별도 URL, 예: `kafka.apache.org/.../ProducerConfig.html` 또는 Kafka 운영 가이드의 `max.in.flight.requests.per.connection` 설명)를 별도로 수집해 `UNSUPPORTED_DECISION` 라벨을 해소해야 한다.
|
||||
- ISR(in-sync replica) 개수·`min.insync.replicas`와 `acks=all`의 정확한 상호작용(이 페이지에 "in-sync"/"ISR" 단어 자체가 없음 — grep 확인, `min.insync.replicas`는 트랜잭션 섹션에서만 "should be set to 2"로 1회 등장하며 acks=all 의 일반 정의와 직접 연결되어 서술되지 않음)
|
||||
- "항상 `acks=all`을 써야 한다"는 규범적 권고 — 이 문서는 트레이드오프만 서술하고 prescriptive 하지 않음
|
||||
- DB write 와 Kafka publish 를 아우르는 end-to-end exactly-once
|
||||
- 압축(compression)·배치 상한·TLS/SASL·스키마 호환성 등 branch 범위의 다른 항목 (이 URL 은 다루지 않음 — 별도 자료 필요)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton Kafka producer adapter 의 실제 `application.yml`/`ProducerConfig` 설정값이 이 문서의 기본값과 일치하는지 (`acks=all`, `enable.idempotence=true`) — 코드 레벨 검증 필요
|
||||
- `max.in.flight.requests.per.connection` 상한값 — 위 한계 참조, 별도 공식 문서 수집 필요
|
||||
|
||||
## 메모
|
||||
|
||||
> 검증되지 않은 추론은 여기에만 둔다. wiki/concepts 로 옮길 때 재검토.
|
||||
|
||||
- HTML→텍스트 변환은 stdlib `html.parser` 로 직접 작성한 스크립트를 사용(환경에 `bs4`/`html2text`/`pandoc`/`lynx` 부재 확인). 블록 태그 경계에서만 줄바꿈을 넣고, 텍스트 노드 내부 개행은 공백으로 접어서 문장이 잘리지 않게 함 — self-grep 안정성을 위한 선택.
|
||||
- 다음 작업 후보: `ProducerConfig` Javadoc 또는 Kafka 공식 "Configuration" 레퍼런스 페이지에서 `max.in.flight.requests.per.connection` 원문을 별도 raw 문서로 수집해 이 문서의 `KAFKA-PROD-JD-C3`/`C5`/`C6` 한계를 해소.
|
||||
- WebFetch 도구의 1차 결과(요약 모델 처리본)는 이 문서 작성에 **사용하지 않음** — self-grep 검증이 불가능한 paraphrase 였기 때문에 폐기하고 `curl` raw HTML → 자체 텍스트 추출로 재작업함.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 vault 내 관련 official-doc: 현재 없음(Kafka producer 관련 최초 official-doc). `raw/official-docs/spring-kafka-sending-messages-kafkatemplate.md` 는 Spring Kafka(벤더 다름, `KafkaTemplate` wrapper) — 겹치는 영역은 있으나 별개 자료.
|
||||
- 추가 수집 후보: Kafka `ProducerConfig` 공식 문서(`max.in.flight.requests.per.connection` 상세), Kafka 공식 Design 문서(`acks`/ISR 상호작용 상세)
|
||||
- 이 자료를 인용한 wiki 요약: 아직 없음 (생성 시 링크)
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: official-doc / Liquibase MongoDB Pro — Platform Support & Drift Report Scope
|
||||
source_type: official-doc
|
||||
url: https://docs.liquibase.com/pro/integration-guide/what-support-does-liquibase-mongodb-pro-have-with-mongodb-platforms
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, liquibase]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Liquibase MongoDB Pro — Platform Support & Drift Report Scope
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — Liquibase 공식 통합 가이드(Pro 문서 트리, `docs.liquibase.com/pro/...`).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | "MongoDB index drift 감지를 Liquibase 로 얻지 않고 자체 러너로 구현한다" 결정의 근거 — Liquibase 의 MongoDB drift report 접근이 **Liquibase MongoDB Pro extension**(유료) 기능 목록에 명시되어 있다는 사실 확보. 단, 이 페이지만으로 "무료(OSS) extension 에는 drift 가 전혀 없다"는 부정 명제까지 증명되지는 않음 — 아래 Usage Boundaries 참조 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.liquibase.com/pro/integration-guide/what-support-does-liquibase-mongodb-pro-have-with-mongodb-platforms
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Liquibase (Datical/Liquibase Inc. 공식 문서)
|
||||
- 발행일: (문서 자체에 명시 없음 — 버전별 롤링 업데이트되는 통합 가이드 페이지)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch가 "MongoDB index drift 감지는 Liquibase 대신 자체 러너로 구현한다"를 결정하려 할 때, 그 근거인 "Liquibase 의 MongoDB drift report 는 Pro(유료) extension 기능"이라는 사실을 원문 그대로 확보하기 위해 보관.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Core Capabilities / Features] "Policy Checks, Secrets Management, Structured Logging, and MongoDB Pro extension-only commands and access to Drift reports."
|
||||
|
||||
> [§Reporting & Policy Checks / Reports] "The Drift report is the only report supported for use with the MongoDB Pro extension."
|
||||
|
||||
> [§Core Capabilities] "Liquibase MongoDB Pro enables use of Liquibase Pro features with MongoDB databases."
|
||||
|
||||
> [§Supported Platforms] "- MongoDB 5.x, 6.x, 7.x, 8.x
|
||||
- MongoDB Atlas (versions 5, 6, 7, 8x)
|
||||
- AWS DocumentDB (versions 4, 5)"
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| LIQUIBASE-MONGO-PRO-C1 | Liquibase MongoDB **Pro** extension 의 기능 목록에 "access to Drift reports" 가 명시적으로 포함되어 있다 — 즉 drift report 접근은 Pro extension 기능 세트의 일부다 | [§Features] "Policy Checks, Secrets Management, Structured Logging, and MongoDB Pro extension-only commands and access to Drift reports." | `official-vendor-doc` | Liquibase Pro 구독 + MongoDB Pro extension 사용자가 drift report 에 접근 가능하다는 사실 | **무료(OSS) Liquibase MongoDB extension 에 drift report 기능이 전혀 없다는 것은 이 문장이 직접 말하지 않는다** — 이 문장은 "Pro 기능 목록에 drift 가 포함된다"는 긍정 명제만 서술하며, "free tier 에는 없다"는 부정 명제는 다루지 않음 |
|
||||
| LIQUIBASE-MONGO-PRO-C2 | MongoDB Pro extension 을 사용할 때 지원되는 report 종류는 Drift report 하나뿐이다(다른 report 타입은 MongoDB Pro extension 범위에서 지원되지 않음) | [§Reports] "The Drift report is the only report supported for use with the MongoDB Pro extension." | `official-vendor-doc` | MongoDB Pro extension 범위 내 report 기능의 전체 목록(=drift 단독) | 이 문장은 "MongoDB Pro extension 안에서 report 타입이 drift 하나뿐"이라는 것만 말한다. **free/OSS extension 이 drift 자체를 지원하는지 여부는 이 문장의 주어(subject)가 아니므로 증명되지 않음** |
|
||||
| LIQUIBASE-MONGO-PRO-C3 | "Liquibase MongoDB Pro" 는 Liquibase **Pro** 기능을 MongoDB 데이터베이스에서 쓸 수 있게 해주는 별도 유료 제품/extension 이다 (Pro 기능이 별도로 존재한다는 gating 구조를 전제) | [§Core Capabilities] "Liquibase MongoDB Pro enables use of Liquibase Pro features with MongoDB databases." | `official-vendor-doc` | "Liquibase MongoDB Pro" 라는 제품명 자체가 Pro 전용 기능 게이팅 구조를 전제한다는 프레이밍 근거 | 무료 extension 과 Pro extension 의 **기능 항목별 비교표**는 이 페이지에 없음 — 이 문장만으로 "drift 가 free tier 에서 빠진다"를 결론 내리는 것은 추론이며 원문 직접 서술이 아님 |
|
||||
| LIQUIBASE-MONGO-PRO-C4 | Liquibase MongoDB Pro extension 이 지원하는 플랫폼/버전 범위는 MongoDB 5.x/6.x/7.x/8.x, MongoDB Atlas(5/6/7/8x), AWS DocumentDB(4/5) 이며, 최소 요구 버전은 Liquibase Pro 4.20.0+ 와 MongoDB Pro extension 1.0.0+ 이다 | [§Supported Platforms] "MongoDB 5.x, 6.x, 7.x, 8.x" / "MongoDB Atlas (versions 5, 6, 7, 8x)" / "AWS DocumentDB (versions 4, 5)"; [§Key Limitations] "Minimum version: Liquibase Pro 4.20.0+ with MongoDB Pro extension 1.0.0+" | `official-vendor-doc` | ca-skeleton 이 채택하려는 MongoDB 배포 버전이 이 지원 범위(5.x~8.x, Atlas, DocumentDB)에 해당하는지 확인하는 근거 | MongoDB 4.x 이하 self-hosted 버전, 또는 Atlas/DocumentDB 가 아닌 제3자 매니지드 MongoDB 호환 서비스에 대한 지원 여부는 이 페이지에 없음 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, Liquibase 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `LIQUIBASE-MONGO-PRO-C1`: Liquibase MongoDB **Pro** extension 의 공식 기능 목록에 "drift report 접근"이 포함되어 있다
|
||||
- `LIQUIBASE-MONGO-PRO-C2`: MongoDB Pro extension 범위에서 지원되는 report 타입은 drift 하나뿐이다
|
||||
- `LIQUIBASE-MONGO-PRO-C3`: "Liquibase MongoDB Pro" 자체가 Pro 전용 기능을 여는 유료 제품/extension 이라는 프레이밍
|
||||
- `LIQUIBASE-MONGO-PRO-C4`: 지원 플랫폼/버전 범위(MongoDB 5.x~8.x, Atlas, DocumentDB)와 최소 요구 버전(Liquibase Pro 4.20.0+, extension 1.0.0+)
|
||||
- **이 자료가 증명하지 않는 것 (중요 — branch 결정에 직접 영향)**:
|
||||
- **무료(OSS) Liquibase MongoDB extension 이 drift 감지를 전혀 지원하지 않는다는 명시적 부정 서술이 이 페이지에 없다.** 이 페이지는 "Liquibase MongoDB Pro" 전용 지원 매트릭스 문서이며, 그 대비군인 free/OSS MongoDB extension 의 기능 목록·제약을 별도로 서술하지 않는다. 따라서 "무료 티어로는 drift 요구사항을 충족하지 못한다"는 branch 결론은 이 페이지의 **직접 인용만으로는 완전히 닫히지 않는다** — free extension 쪽 공식 문서(또는 Liquibase pricing/edition 비교 페이지)의 별도 확인이 필요하다. 이 gap 은 branch-note 의 Decision Evidence Map 에 `needs-confirmation` 또는 별도 dispatch 필요 항목으로 명시해야 한다
|
||||
- 무료(OSS)와 Pro extension 의 기능 항목별(feature-by-feature) 비교표
|
||||
- drift report 의 구체적 산출물 형태(스키마 diff 포맷, CLI 명령 이름 등) — 이 페이지는 "지원 여부"만 서술하고 사용법은 다루지 않음
|
||||
- Liquibase Pro 라이선스 가격/구매 조건
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- Liquibase 무료(OSS) MongoDB extension 공식 문서(또는 edition 비교 페이지)에서 drift report 가 명시적으로 빠져 있는지 별도 확인 (1 dispatch = 1 URL 원칙에 따라 본 자료에는 포함하지 않음)
|
||||
- ca-skeleton 이 실제 채택할 MongoDB 배포 버전이 §Supported Platforms 범위(5.x~8.x/Atlas/DocumentDB) 안에 있는지 — Pro 를 도입할 경우에만 의미 있는 확인 항목
|
||||
|
||||
## 메모
|
||||
|
||||
- 사용자가 요청한 "(1)(2) 가 이 dispatch 의 핵심" 문장은 확보했다 — `C1`("access to Drift reports" 가 Pro 기능 목록에 포함) 과 `C2`("Drift report 가 MongoDB Pro extension 에서 지원되는 유일한 report") 둘 다 self-grep 통과.
|
||||
- 다만 "무료 extension 에는 drift 가 없다"는 **부정 명제**는 이 페이지가 직접 말하지 않는다 — 추론을 사실로 승격시키지 않기 위해 Claims Extracted 의 `Does not prove` 칸과 Usage Boundaries 에 명시적으로 gap 을 남겼다. branch-note 작성 시 이 gap 을 근거 문장에서 "그러므로 무료 티어는 지원하지 않는다"처럼 단정하지 말 것.
|
||||
- 추가로 봐야 할 동일 벤더 페이지: Liquibase MongoDB(무료/OSS) extension 공식 문서 또는 Liquibase edition/feature 비교 페이지 (별도 dispatch 필요).
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 프로젝트 내 다른 migration 관련 official-doc: `[[raw/official-docs/migration-liquibase-official-changelog-xml-yaml]]` (Liquibase changelog 형식 — RDBMS 중심, Pro/MongoDB 범위 아님)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: MDN — BroadcastChannel / Window storage event
|
||||
source_type: official-doc
|
||||
url: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-cache-tier-cross-tab-invalidation-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, cross-tab, broadcastchannel, web-storage, browser]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# MDN — BroadcastChannel / Window storage event
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract]]` | 발신 컨텍스트 자기 수신 불가 처리(`D4`), **대체 경로를 두지 않는다는 결정**(`D5` — `#C4` 의 지원 범위와 `#C10` 의 `sessionStorage` 미도달), 메시지 봉투의 직렬화 제약(`D2`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
|
||||
- 보조 URL: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel/postMessage , https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: MDN Web Docs (Mozilla)
|
||||
- 발행일: (문서 지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`FE-D028` 이 "BroadcastChannel 우선 + `storage` event fallback" 을 정했는데, 그 두 transport 의 **실제 전달 범위**를 확인하지 않은 채 adapter 를 설계하면 두 가지를 놓친다. (a) 두 transport 모두 **발신자에게는 이벤트가 가지 않는다** — 로컬 무효화를 채널 왕복에 의존하면 발신 탭만 stale 로 남는다. (b) `storage` event 는 `sessionStorage` 에서도 발생하지만 그 경우 **다른 탭에는 도달하지 않는다** — fallback backend 를 명시하지 않으면 조용히 동작하지 않는 fallback 이 된다. 추가로 BroadcastChannel 의 Baseline 상태가 fallback 유지 근거를 좌우한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§BroadcastChannel] "The `BroadcastChannel` interface represents a named channel that any browsing context of a given origin can subscribe to."
|
||||
|
||||
> [§BroadcastChannel] "It allows communication between different documents (in different windows, tabs, frames or iframes) of the same origin."
|
||||
|
||||
> [§BroadcastChannel] "Messages are broadcasted via a `message` event fired at all `BroadcastChannel` objects listening to the channel, except the object that sent the message."
|
||||
|
||||
> [§BroadcastChannel — Note] "This feature is available in Web Workers."
|
||||
|
||||
> [§BroadcastChannel — Baseline] "**Baseline Widely available** — This feature is well established and works across many devices and browser versions. It's been available across browsers since March 2022."
|
||||
|
||||
> [§BroadcastChannel.close] "Closes the channel object, indicating it won't get any new messages, and allowing it to be, eventually, garbage collected."
|
||||
|
||||
> [§BroadcastChannel.postMessage — Parameters] "The data is serialized using the structured clone algorithm."
|
||||
|
||||
> [§BroadcastChannel.postMessage — Exceptions] "`DataCloneError` `DOMException` — Thrown if any part of the input data is not serializable."
|
||||
|
||||
> [§BroadcastChannel.postMessage — Exceptions] "`InvalidStateError` `DOMException` — Thrown if the `BroadcastChannel` has already been closed."
|
||||
|
||||
> [§Window: storage event] "The `storage` event of the `Window` interface fires when another document that shares the same storage area (either `localStorage` or `sessionStorage`) as the current window updates that storage area. The event is _not_ fired on the window that made the change."
|
||||
|
||||
> [§Window: storage event] "For `localStorage`, the event is fired in all other browsing contexts that are in the same origin as the initiating document. This includes other tabs with the same origin."
|
||||
|
||||
> [§Window: storage event] "For `sessionStorage`, the event is fired in all other browsing contexts that are in the same origin and the same top-level browsing context as the initiating document. This only includes embedded iframes, if any, in the same tab, and not other tabs."
|
||||
|
||||
> [§Window: storage event — Event properties] "The `key` attribute is `null` when the change is caused by the storage `clear()` method."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | BroadcastChannel 은 **같은 origin** 의 서로 다른 문서(창·탭·frame·iframe) 사이 통신 수단이다 | [§BroadcastChannel] "communication between different documents (in different windows, tabs, frames or iframes) of the same origin" | `official-reference` | 같은 origin 의 browsing context | cross-origin 또는 다른 프로필/브라우저 간 전달 |
|
||||
| C2 | 메시지는 채널을 듣는 모든 BroadcastChannel **객체**에 전달되되 **보낸 객체만 제외**된다 | [§BroadcastChannel] "fired at all `BroadcastChannel` objects listening to the channel, except the object that sent the message" | `official-reference` | 모든 수신 객체 | 발신 *문서* 전체가 제외된다는 뜻은 아님 — 제외 단위는 **객체**이므로 같은 문서의 다른 채널 객체는 수신한다 |
|
||||
| C3 | BroadcastChannel 은 Web Worker 에서도 사용할 수 있다 | [§Note] "This feature is available in Web Workers." | `official-reference` | worker 컨텍스트 | service worker 에서의 생명주기 보장 |
|
||||
| C4 | BroadcastChannel 은 Baseline Widely available 이며 2022년 3월부터 브라우저 전반에서 제공된다 | [§Baseline] "It's been available across browsers since March 2022." | `official-reference` | Baseline 기준 브라우저 집합 | 이 프로젝트의 **대상 브라우저 매트릭스**가 그 집합 안이라는 뜻은 아님(`FE-Q-007` 미확정). private 모드·파티션 환경의 동작도 증명하지 않음 |
|
||||
| C5 | `postMessage` 는 structured clone 으로 직렬화하며 직렬화 불가 데이터는 `DataCloneError` 를 던진다 | [§postMessage] "serialized using the structured clone algorithm" / "Thrown if any part of the input data is not serializable" | `official-reference` | 모든 BroadcastChannel 메시지 | 직렬화 가능하면 크기 제한이 없다는 뜻은 아님 |
|
||||
| C6 | 닫힌 채널에 `postMessage` 하면 `InvalidStateError` 가 발생한다 | [§postMessage — Exceptions] "Thrown if the `BroadcastChannel` has already been closed." | `official-reference` | `close()` 이후의 발행 | unmount 시 close 를 강제해야 한다는 정책까지 증명하지는 않음 |
|
||||
| C7 | `close()` 는 더 이상 메시지를 받지 않음을 표시하고 GC 를 허용한다 | [§close] "indicating it won't get any new messages, and allowing it to be, eventually, garbage collected" | `official-reference` | 채널 객체 수명 | close 를 빠뜨렸을 때의 누수 규모 |
|
||||
| C8 | `storage` event 는 **변경을 만든 window 에서는 발생하지 않는다** | [§storage event] "The event is _not_ fired on the window that made the change." | `official-reference` | `localStorage`·`sessionStorage` 양쪽 | 다른 문서에서는 반드시 발생한다는 타이밍 보장 |
|
||||
| C9 | `localStorage` 변경의 `storage` event 는 같은 origin 의 **다른 모든 탭**에 도달한다 | [§storage event] "For `localStorage`, the event is fired in all other browsing contexts that are in the same origin as the initiating document. This includes other tabs with the same origin." | `official-reference` | `localStorage` | 값이 동일할 때도 발생하는지 |
|
||||
| C10 | `sessionStorage` 변경의 `storage` event 는 **같은 탭 안**(same top-level browsing context)에만 도달하고 다른 탭에는 도달하지 않는다 | [§storage event] "This only includes embedded iframes, if any, in the same tab, and not other tabs." | `official-reference` | `sessionStorage` | `sessionStorage` 를 캐시 tier 로 쓰는 것 자체가 잘못이라는 뜻은 아님 — 잘못은 이를 **탭 간 신호**로 쓰는 것 |
|
||||
| C11 | `clear()` 로 인한 변경이면 `StorageEvent.key` 는 `null` 이다 | [§Event properties] "The `key` attribute is `null` when the change is caused by the storage `clear()` method." | `official-reference` | `storage` event 수신부 | `null` key 를 어떻게 처리해야 하는지의 정책 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`~`C7`: BroadcastChannel 의 전달 범위·자기 제외·직렬화·수명
|
||||
- `C8`~`C11`: `storage` event 의 발신자 제외, backend 별 도달 범위, `clear()` 시 key 형태
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `storage` event 가 **같은 값으로 덮어썼을 때도** 발생하는지 — MDN 이 이 페이지에서 말하지 않는다. 신호 key 를 매번 다른 값으로 써야 하는지는 실측 필요
|
||||
- 두 transport 의 **전달 지연**이나 순서 보장 — 어느 쪽도 명시하지 않는다
|
||||
- BroadcastChannel 이 사용 불가한 실제 환경의 목록 — `C4` 의 Baseline 은 그 반대 방향의 증거다
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 대상 브라우저 매트릭스(`FE-Q-007`)에 BroadcastChannel 미지원 행이 실제로 있는지 — 없다면 `FE-D028` 의 `storage` event fallback 은 근거 없는 복잡도다
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C2` 의 제외 단위가 *객체*라는 점 때문에, adapter 가 컨텍스트당 채널 객체를 1개만 유지하면 "발신 탭은 수신하지 않는다" 가 성립한다. 반대로 무심코 객체를 2개 만들면 자기 메시지를 자기가 받는 경로가 생긴다.
|
||||
- 인용 2 해석 후보 (미검증): `C8`+`C2` 를 합치면 **두 transport 모두 발신자에게 echo 하지 않는다**. 따라서 로컬 무효화는 채널을 거치지 않고 직접 수행해야 한다는 설계 제약이 된다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `Window.localStorage` 의 저장 한도, `StorageEvent` 인터페이스 자체
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/mdn-storage-quotas-eviction-persistence]]`, `[[raw/official-docs/tanstack-query-persistence-hydration-official]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
title: MDN — HTTP Range requests / Fetch 취소·스트림 소비
|
||||
source_type: official-doc
|
||||
url: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-large-object-transfer-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, http, range, fetch, streaming, transfer]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# MDN — HTTP Range requests / Fetch 취소·스트림 소비
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-large-object-transfer-contract]]` | `StreamingDownloadPort` 의 재개 프로토콜(`D4`), part 재시도가 stream 재사용이 아니라 재slice 여야 하는 이유(`D6`), 취소의 error 매핑(`D7`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Range_requests
|
||||
- 보조 URL: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: MDN Web Docs (Mozilla)
|
||||
- 발행일: (문서 지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
branch 의 `resumeStrategy: range` 와 "part 재시도 상한 2회" 가 **어떤 프로토콜 사실 위에 서 있는지** 확인하지 않은 상태였다. 특히 세 가지가 필요했다. (a) 재개가 서버 지원에 의존한다면 미지원을 어떻게 감지하는가, (b) 재개 중 원본이 바뀌면 무엇이 깨지는가, (c) 실패한 part 를 재시도할 때 이미 소비한 body 를 다시 쓸 수 있는가. (c) 는 재시도 구현이 조용히 빈 body 를 보내는 고전적 버그의 원인이다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Checking if a server supports partial requests] "If an HTTP response includes the `Accept-Ranges` header with any value other than `none`, the server supports range requests."
|
||||
|
||||
> [§Checking if a server supports partial requests] "If responses omit the `Accept-Ranges` header, it indicates the server doesn't support partial requests."
|
||||
|
||||
> [§Requesting a specific range from a server] "If the server supports range requests, you can specify which part (or parts) of the document you want the server to return by including the `Range` header in a HTTP request."
|
||||
|
||||
> [§Single part ranges] "The `Content-Range` response header indicates where this partial message belongs within the full resource."
|
||||
|
||||
> [§Partial request responses] "A range request that is out of bounds will result in a `416` `Requested Range Not Satisfiable` status, meaning that none of the range values overlap the extent of the resource."
|
||||
|
||||
> [§Partial request responses] "If range requests are not supported, an `200` `OK` status is sent back and the entire response body is transmitted."
|
||||
|
||||
> [§Conditional range requests] "When resuming to request more parts of a resource, you need to guarantee that the stored resource has not been modified since the last fragment has been received."
|
||||
|
||||
> [§Conditional range requests] "The `If-Range` HTTP request header makes a range request conditional: if the condition is fulfilled, the range request will be issued and the server sends back a `206` `Partial Content` answer with the appropriate body. If the condition is not fulfilled, the full resource is sent back, with a `200` `OK` status."
|
||||
|
||||
> [§Using Fetch — Canceling a request] "To make a request cancelable, create an `AbortController`, and assign its `AbortSignal` to the request's `signal` property."
|
||||
|
||||
> [§Using Fetch — Canceling a request] "To cancel the request, call the controller's `abort()` method. The `fetch()` call will reject the promise with an `AbortError` exception."
|
||||
|
||||
> [§Using Fetch — Canceling a request] "If the request is aborted after the `fetch()` call has been fulfilled but before the response body has been read, then attempting to read the response body will reject with an `AbortError` exception."
|
||||
|
||||
> [§Using Fetch — Streaming the response body] "Request and response bodies are actually `ReadableStream` objects, and whenever you read them, you're streaming the content."
|
||||
|
||||
> [§Using Fetch — Locked and disturbed streams] "This means it's not possible to read the same response (or request) body more than once"
|
||||
|
||||
> [§Using Fetch — Locked and disturbed streams] "if any content has been read from the stream, then the stream is _disturbed_, and nothing else can read from the stream."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 서버의 range 지원 여부는 `Accept-Ranges` 헤더로 판별하며 `none` 또는 헤더 부재는 미지원을 뜻한다 | [§Checking] "with any value other than `none`, the server supports range requests" / "If responses omit the `Accept-Ranges` header, it indicates the server doesn't support partial requests." | `official-reference` | 모든 HTTP 응답 | 특정 스토리지 vendor 가 이 헤더를 보내는지 |
|
||||
| C2 | 부분 응답의 위치는 `Content-Range` 가 알려주며 상태 코드는 `206` 이다 | [§Single part ranges] "The `Content-Range` response header indicates where this partial message belongs within the full resource." | `official-reference` | `206` 응답 | 클라이언트가 요청한 범위와 서버가 준 범위가 항상 같다는 것 |
|
||||
| C3 | 범위가 리소스 밖이면 `416 Requested Range Not Satisfiable` 이 온다 | [§Partial request responses] "will result in a `416` `Requested Range Not Satisfiable` status" | `official-reference` | 잘못된 재개 위치 | 416 이 재시도로 회복 가능한지 |
|
||||
| C4 | range 를 지원하지 않으면 `200 OK` 와 **전체 본문**이 온다 — 조용히 성공한 것처럼 보인다 | [§Partial request responses] "an `200` `OK` status is sent back and the entire response body is transmitted." | `official-reference` | 재개 시도 | 200 응답을 받은 클라이언트가 자동으로 이를 알아챈다는 것 — 상태 코드를 검사해야만 안다 |
|
||||
| C5 | 재개 시에는 마지막 조각 수신 이후 원본이 변경되지 않았음을 **보장해야 한다** | [§Conditional range requests] "you need to guarantee that the stored resource has not been modified since the last fragment has been received." | `official-reference` | 모든 이어받기 | 어떤 validator(ETag vs Last-Modified)를 써야 하는지 |
|
||||
| C6 | `If-Range` 는 조건 충족 시 `206`, 불충족 시 `200` 과 전체 리소스를 돌려준다 | [§Conditional range requests] "If the condition is not fulfilled, the full resource is sent back, with a `200` `OK` status." | `official-reference` | 조건부 재개 | 불충족을 오류로 취급해야 하는지 — 프로토콜상 정상 응답이다 |
|
||||
| C7 | `AbortController` 의 `signal` 로 요청을 취소하며 `fetch()` 는 `AbortError` 로 reject 된다 | [§Canceling a request] "The `fetch()` call will reject the promise with an `AbortError` exception." | `official-reference` | 모든 fetch 요청 | 이미 전송된 바이트가 서버에서 취소된다는 것 |
|
||||
| C8 | 응답 수신 후 body 읽기 전에 취소하면 **body 읽기가** `AbortError` 로 reject 된다 | [§Canceling a request] "attempting to read the response body will reject with an `AbortError` exception." | `official-reference` | 취소 타이밍이 늦은 경우 | 두 경로의 error 를 구분해야 하는지 |
|
||||
| C9 | request·response body 는 `ReadableStream` 이며 읽는 순간 스트리밍된다 | [§Streaming the response body] "Request and response bodies are actually `ReadableStream` objects" | `official-reference` | fetch body | 요청 body 로 스트림을 넘길 때의 추가 요구사항(`duplex` 등) — 이 페이지는 다루지 않음 |
|
||||
| C10 | 같은 body 를 **두 번 읽을 수 없다**. 한 번 읽힌 스트림은 disturbed 상태가 되어 누구도 다시 읽지 못한다 | [§Locked and disturbed streams] "it's not possible to read the same response (or request) body more than once" / "the stream is _disturbed_, and nothing else can read from the stream." | `official-reference` | 모든 fetch body | 재시도가 불가능하다는 뜻은 아님 — **새 body 를 만들면** 가능하다 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`~`C6`: 이어받기의 지원 판별·위치 표현·실패 코드·조건부 재개
|
||||
- `C7`·`C8`: 취소의 오류 표면
|
||||
- `C9`·`C10`: body 스트림의 1회 소비 제약
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **업로드** 스트리밍(요청 body 를 `ReadableStream` 으로 주는 경우)의 요구사항 — `duplex` 옵션과 HTTP/2 요구를 이 페이지는 언급하지 않는다
|
||||
- part 재시도 횟수의 적정값 — 프로토콜은 횟수를 말하지 않는다
|
||||
- 어떤 validator(`ETag`/`Last-Modified`)를 `If-Range` 에 써야 하는지
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- 선택할 object storage 가 `Accept-Ranges` 와 `If-Range` 를 실제로 지원하는지(`FE-Q-012`)
|
||||
- 스트리밍 업로드가 필요한지, 필요하다면 `duplex` 지원 매트릭스
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C4` 는 조용한 실패의 원천이다. 이어받기를 요청했는데 `200` 이 오면 클라이언트는 **처음부터 다시 받고 있는 중**인데도 "재개 성공" 으로 착각하기 쉽다. adapter 는 `206` 을 명시적으로 확인해야 한다.
|
||||
- 인용 2 해석 후보 (미검증): `C10` 때문에 part 재시도는 **원본 `Blob` 을 다시 slice** 해야 한다. 첫 시도에서 만든 body 를 보관했다가 재사용하면 disturbed 스트림을 보내게 된다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `Request.duplex`, `If-Range`, `Accept-Ranges`, `AbortSignal.timeout()`
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/aws-s3-multipart-upload-limits]]`, `[[raw/official-docs/mdn-referrer-policy]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: MDN — Referrer-Policy
|
||||
source_type: official-doc
|
||||
url: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-large-object-transfer-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, http, security, referrer, transfer]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# MDN — Referrer-Policy
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-large-object-transfer-contract]]` | presigned URL 유출 경로 중 **`Referer` 는 브라우저 기본값이 이미 막고 있고 남은 위험은 우리 로그·telemetry** 라는 `D2` 의 초점 이동 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy
|
||||
- 보조 URL:
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: MDN Web Docs (Mozilla)
|
||||
- 발행일: (문서 지속 갱신)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
branch 의 `D2` 가 "presigned URL 은 telemetry·로그·`Referrer` 어디에도 남기지 않는다" 로 세 경로를 나란히 놓았다. 세 경로의 **위험 크기가 같은지** 확인이 필요했다. 결과적으로 `Referer` 경로는 2020년 11월 이후 브라우저 기본 정책이 cross-origin 에서 path·query 를 이미 제거하고 있어 우리가 추가로 할 일이 적고, 실질 위험은 우리가 직접 쓰는 로그·telemetry 에 몰려 있다는 것이 드러났다. 세 경로를 같은 무게로 다루면 정작 위험한 쪽의 fixture 가 얇아진다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Directives — no-referrer] "The `Referer` header will be omitted: sent requests do not include any referrer information."
|
||||
|
||||
> [§Directives — same-origin] "Send the origin, path, and query string when performing a same-origin request. Don't send the `Referer` header for cross-origin requests."
|
||||
|
||||
> [§Directives — strict-origin-when-cross-origin] "Send the origin, path, and query string when performing a same-origin request. For cross-origin requests send the origin (only) when the protocol security level stays same (HTTPS→HTTPS). Don't send the `Referer` header to less secure destinations (HTTPS→HTTP)."
|
||||
|
||||
> [§Directives — strict-origin-when-cross-origin, Note] "This is the default policy if no policy is specified, or if the provided value is invalid"
|
||||
|
||||
> [§Directives — unsafe-url] "Send the origin, path, and query string when performing any request, regardless of security."
|
||||
|
||||
> [§Directives — unsafe-url, Warning] "This policy will leak potentially-private information from HTTPS resource URLs to insecure origins. Carefully consider the impact of this setting."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 지정이 없거나 값이 잘못되면 기본 정책은 `strict-origin-when-cross-origin` 이다 | [§Note] "This is the default policy if no policy is specified, or if the provided value is invalid" | `official-reference` | 정책 미설정 문서 | 모든 브라우저가 이 기본값을 구현한다는 실측 |
|
||||
| C2 | 그 기본값에서 **cross-origin 요청에는 origin 만** 가고 path·query 는 가지 않는다 | [§strict-origin-when-cross-origin] "For cross-origin requests send the origin (only) when the protocol security level stays same" | `official-reference` | cross-origin 요청 | same-origin 요청에서도 path 가 가려진다는 것 — same-origin 에는 path·query 가 그대로 간다 |
|
||||
| C3 | `no-referrer` 는 `Referer` 를 완전히 생략한다 | [§no-referrer] "sent requests do not include any referrer information." | `official-reference` | 명시 설정 시 | 생략이 부작용 없이 안전하다는 것 (일부 서버는 referer 를 요구) |
|
||||
| C4 | `unsafe-url` 은 보안 수준과 무관하게 origin·path·query 를 모두 보내며 MDN 은 이를 경고한다 | [§unsafe-url, Warning] "This policy will leak potentially-private information from HTTPS resource URLs to insecure origins." | `official-reference` | `unsafe-url` 설정 문서 | 기본값에서도 같은 유출이 일어난다는 것 — 오히려 반대다 |
|
||||
| C5 | `same-origin` 정책은 cross-origin 요청에 `Referer` 를 아예 보내지 않는다 | [§same-origin] "Don't send the `Referer` header for cross-origin requests." | `official-reference` | 명시 설정 시 | 이 정책이 기본값이라는 것 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`+`C2`: **정책을 설정하지 않아도** cross-origin 으로 나가는 요청에는 path·query 가 붙지 않는다
|
||||
- `C3`~`C5`: 더 강한 정책의 선택지와 `unsafe-url` 의 위험
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- presigned URL 이 `Referer` 로 유출될 수 있다는 것 — `Referer` 는 **요청을 유발한 문서의 URL** 이지 요청 대상 URL 이 아니다. presigned URL 이 우리 페이지 주소에 들어가 있지 않다면 이 경로로는 새지 않는다
|
||||
- 로그·telemetry·에러 객체를 통한 유출 — 이 문서의 범위가 아니며, 그쪽이 실제 위험이다
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- presigned URL 이 주소창·`history`·`document.referrer` 에 들어가는 코드 경로가 있는지 (있다면 그때부터 `Referer` 가 실제 유출 경로가 된다)
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C1`+`C2` 는 `D2` 의 세 경로 중 `Referrer` 항목의 **우선순위를 낮춘다**. 브라우저 기본값이 이미 방어하므로, 이 branch 가 추가로 할 일은 "presigned URL 을 페이지 URL 에 넣지 않는다" 는 금지 규칙 하나다.
|
||||
- 인용 2 해석 후보 (미검증): 반대로 **로그·telemetry 경로에는 어떤 브라우저 기본 방어도 없다.** fixture 밀도를 그쪽에 몰아야 한다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `Referer` 헤더, `Request.referrerPolicy`
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/owasp-content-security-policy-cheat-sheet]]`, `[[raw/official-docs/mdn-http-range-fetch-transfer]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
title: official-doc / Mongock — Migration Tool, Multi-Instance Lock & Maintenance Status
|
||||
source_type: official-doc
|
||||
url: https://docs.mongock.io/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, distributed-lock]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Mongock — Migration Tool, Multi-Instance Lock & Maintenance Status
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | "index manifest 적용 + drift 감지를 Mongock 이 아니라 Spring Data `IndexOperations` 기반 자체 러너로 구현한다" 결정의 양면 근거 — Mongock 을 선택하지 않는 근거(유지보수 상태: 신규 개발이 후속 프로젝트 Flamingock 으로 이동, critical bug fix/security update 만 지속)와, 선택했다면 얻었을 이점(멀티 인스턴스 동시 실행을 막는 DB 영속 pessimistic lock 내장)을 모두 verbatim 으로 확보 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.mongock.io/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Mongock (Flamingock 이 관리하는 OSS 프로젝트, Apache License 2.0)
|
||||
- 발행일: 불명 (문서 사이트, 지속 갱신형 — 페이지 자체에 발행일 명시 없음)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch 는 index manifest 적용 + drift 감지를 기성 migration 도구(Mongock) 대신 Spring Data `IndexOperations` 기반 자체 러너로 구현하기로 결정하려 한다. 이 결정은 "Mongock 이 EOL(신규 개발 중단, 후속 프로젝트 Flamingock 으로 이전)이라 채택하지 않는다"는 근거와, "Mongock 을 채택했다면 멀티 인스턴스 동시 실행 방지용 DB 영속 pessimistic lock 을 별도 구현 없이 얻었을 것이다"라는 trade-off 를 모두 인지한 상태에서 내려야 한다. 이 문서는 그 양면을 모두 verbatim 으로 뒷받침한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [Introduction] "Mongock is a Java based migration tool as part of your application code for Distributed environments. It allows developers to execute safer migrations by having ownership and control over data migrations during the Application deployment process as code and data changes are shipped together."
|
||||
|
||||
> [경고 배너, 페이지 최상단] "Mongock will continue receiving critical bug fixes and security updates only. All innovation is happening in Flamingock." (원문에서 "critical bug fixes and security updates only" 부분은 `<b>` 태그로 강조되어 있었음 — 볼드 마크업만 제거, 문구는 원문 그대로)
|
||||
|
||||
> [How it works → 3. The persistent layer] "As more than one instance of the client-service may be running simultaneusly [원문 그대로, typo 포함] in the environment, it will try to execute the same migration on startup. To prevent this, Mongock uses a pesimistic lock [원문 그대로, typo 포함] that is persisted in database."
|
||||
|
||||
> [How it works → 3. The persistent layer] "Mongock needs to track the ChangeUnits that have been executed, so the client-service doesn't execute them twice."
|
||||
|
||||
> [How it works → 2. Your migration changes] "Note: From version 5, ChangeLog annotation is deprecated (though remains for backwards compatibility). It's been replaced by @ChangeUnit."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGOCK-C1 | Mongock 은 애플리케이션 코드에 통합되어 분산 환경에서 안전한 DB migration 을 실행하는 Java 기반 도구다 | "Mongock is a Java based migration tool as part of your application code for Distributed environments. [...] as code and data changes are shipped together." | `official-vendor-doc` | Mongock 의 정의·목적 범위(코드-DB 변경 동시 배포) | ca-skeleton 의 index manifest / drift 감지 요구사항과의 기능 적합성 |
|
||||
| MONGOCK-C2 | Mongock 은 신규 기능 개발이 후속 프로젝트 Flamingock 으로 이전되었고, Mongock 자체는 critical bug fix 와 security update 만 계속 받는다 | "Mongock will continue receiving critical bug fixes and security updates only. All innovation is happening in Flamingock." | `official-vendor-doc` | 2026-07-28 확인 시점 기준 Mongock 의 유지보수 상태(사실상 maintenance-mode/EOL 방향) — "기성 도구 대신 자체 러너를 택한다" 결정의 반대 방향 근거 | 정확한 EOL 날짜, critical bug fix 지원이 얼마나 오래 지속될지, Flamingock 이 ca-skeleton 에 더 적합한지 여부 — 이 페이지는 판단하지 않음 |
|
||||
| MONGOCK-C3 | 멀티 인스턴스 환경에서 동시 실행되는 여러 client-service 인스턴스가 같은 migration 을 중복 실행하지 않도록, Mongock 은 DB 에 영속되는 pessimistic lock 을 사용한다 | "As more than one instance of the client-service may be running simultaneusly in the environment, it will try to execute the same migration on startup. To prevent this, Mongock uses a pesimistic lock that is persisted in database." | `official-vendor-doc` | Mongock 을 채택했을 경우 얻는 이점(멀티 인스턴스 락 내장) — "Mongock 을 선택하지 않는다"는 결정에 대한 trade-off 인지 근거 | 이 pessimistic lock 의 timeout·lease·재시도 세부 메커니즘(이 페이지는 존재만 언급, 구현 detail 은 별도 `/v5/lock/` 섹션 — 미조사) — 자체 러너로 이 lock 을 어떻게 대체할지는 이 자료가 답하지 않음 |
|
||||
| MONGOCK-C4 | Mongock 은 실행된 ChangeUnit(구 ChangeLog, v5 부터 `@ChangeUnit` 으로 대체, `@ChangeLog` 는 하위호환만 유지)을 DB 에 추적해 client-service 가 동일 migration 을 두 번 실행하지 않도록 한다 | "Mongock needs to track the ChangeUnits that have been executed, so the client-service doesn't execute them twice." / "From version 5, ChangeLog annotation is deprecated (though remains for backwards compatibility). It's been replaced by @ChangeUnit." | `official-vendor-doc` | Mongock 의 changelog/변경 이력 추적 메커니즘 존재 및 명명 변화(ChangeLog→ChangeUnit) | **선언한 index manifest 와 실제 DB 인덱스 상태를 대조하는 drift 감지 기능은 이 페이지(https://docs.mongock.io/ 홈)에서 언급되지 않음 — "확인되지 않음"이며, 이는 그런 기능이 Mongock 에 없다는 증거가 아니다(부재의 증거 아님, 별도 페이지 확인 필요)** |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGOCK-C1`: Mongock 의 정의(Java 기반, 코드 통합형, 분산 환경 대상 migration 도구)
|
||||
- `MONGOCK-C2`: 2026-07-28 확인 시점 기준 Mongock 은 신규 개발이 중단되고 critical bug fix/security update 만 이어지는 유지보수 상태이며, 후속 프로젝트는 Flamingock
|
||||
- `MONGOCK-C3`: Mongock 이 멀티 인스턴스 동시 실행을 막기 위해 DB 영속 pessimistic lock 을 사용한다는 사실 자체
|
||||
- `MONGOCK-C4`: Mongock 이 실행된 ChangeUnit 을 DB 에 추적해 중복 실행을 막는다는 메커니즘 존재, `@ChangeLog`→`@ChangeUnit` 명명 변화(v5)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- index manifest 선언값과 실제 DB 인덱스 상태를 비교하는 **drift 감지** 기능 — 이 페이지에서 확인되지 않음(부재 확인일 뿐 미지원 확정 아님)
|
||||
- pessimistic lock 의 timeout/lease/재시도 구현 세부 — 별도 `/v5/lock/` 섹션 미조사
|
||||
- Mongock 이 MongoDB 외 CosmosDB/DocumentDB/Couchbase/DynamoDB 등에서 각각 어떤 수준으로 동작하는지의 세부 비교
|
||||
- "Mongock 대신 자체 러너를 만드는 것이 더 낫다"는 가치 판단 — 이 자료는 사실(유지보수 상태·락 메커니즘)만 제공, 선택은 branch 의 trade-off 판단 몫
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 의 index manifest·drift 감지 요구사항을 자체 `IndexOperations` 러너로 구현할 때, Mongock 의 pessimistic lock 이 제공하던 멀티 인스턴스 보호를 어떤 메커니즘(예: 별도 advisory lock, 배포 전략 등)으로 대체할지는 별도 branch-local 결정 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- Q1(유지보수 상태) 문구는 원문 HTML 에서 `<b>` 태그로 감싸인 강조 표시였음 — 강조 마크업만 제거하고 문구는 그대로 옮김.
|
||||
- Q3 의 "simultaneusly"와 "pesimistic"은 원문 사이트의 오탈자로, 그대로 보존함(교정하지 않음).
|
||||
- 이 페이지는 Mongock v5 "How it works" 개요 페이지이며, lock 메커니즘·drift 감지 여부에 대한 상세는 사이드바의 `/v5/lock/`, `/v5/technical-overview` 등 하위 페이지에 있을 수 있음 — 미조사, 추후 필요 시 별도 dispatch.
|
||||
- WebFetch 도구의 1차 결과는 소형 모델이 "원문 그대로"라 표시했음에도 실제로는 문장이 재구성(paraphrase)되어 있었음(예: "Existing deployments can migrate seamlessly" vs 실제 원문 "Existing Mongock deployments migrate automatically"). 이에 따라 `curl` 로 raw HTML 을 별도 확보해 tag 만 제거한 텍스트를 self-grep 대상으로 사용했고, 본 문서의 모든 인용은 그 raw HTML 대조본 기준.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (Mongock lock/technical-overview 하위 페이지 미조사 — 필요 시 추가 dispatch)
|
||||
- 이 자료를 인용한 wiki 요약: (아직 없음, `/ingest` 이후 생성 시 추가)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "official-doc / MongoDB — Connection String Options (timeout / pool / tls / retry)"
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/reference/connection-string-options/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, connection-pool]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Connection String Options (timeout / pool / tls / retry)
|
||||
|
||||
> Layer: `raw/official-docs/` — MongoDB 공식 Manual reference (`connection-string-options`) 원문 발췌.
|
||||
> `feature-mongo-runtime-baseline-contract` 의 "Mongo 연결의 timeout·pool·TLS 파라미터를 드라이버 기본값에 맡기지 않고 명시하고 env registry 로 노출한다" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | Mongo 연결의 timeout(`serverSelectionTimeoutMS`/`connectTimeoutMS`/`socketTimeoutMS`)·pool(`maxPoolSize`/`minPoolSize`/`maxIdleTimeMS`)·TLS(`tls`)·retry(`retryWrites`/`retryReads`) 파라미터를 드라이버 기본값에 맡기지 않고 명시하고 env registry 로 노출한다 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-skeleton 은 MongoDB 를 optional adapter(문서 저장소 축)로 채택할 수 있다. 본 branch 는 연결 문자열의 timeout·pool·TLS·retry 파라미터를 드라이버 기본값에 맡길지, 명시적으로 설정하고 env registry 로 노출할지를 결정해야 한다. 이 판단에는 각 옵션이 실제로 무엇을 의미하는지, 그리고 드라이버/서버가 어떤 값을 기본값으로 쓰는지에 대한 공식 정의가 필요하다 — "기본값에 의존하지 않는다"는 결정을 정당화하려면 그 기본값이 무엇인지, 왜 운영 환경에 부적합할 수 있는지 먼저 알아야 한다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/reference/connection-string-options/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: MongoDB, Inc.
|
||||
- 발행일: MongoDB Manual (현재 버전)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§`serverSelectionTimeoutMS`] "Specifies how long (in milliseconds) to block for server selection before throwing an exception. _Default_: 30,000 milliseconds."
|
||||
|
||||
> [§`connectTimeoutMS`] "The time in milliseconds to attempt a connection before timing out. The default is 10,000 milliseconds, but specific drivers might have a different default."
|
||||
|
||||
> [§`socketTimeoutMS`] "The time in milliseconds to attempt a send or receive on a socket before the attempt times out. The default is no timeout, though different drivers might vary."
|
||||
|
||||
> [§`maxPoolSize`] "The maximum number of connections in the connection pool. The default value is `100`."
|
||||
|
||||
> [§`minPoolSize`] "The minimum number of connections in the connection pool. The default value is `0`."
|
||||
|
||||
> [§`maxIdleTimeMS`] "The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed." — "This option is not supported by all drivers."
|
||||
|
||||
> [§`tls`] "true`: Initiates the connection with TLS/SSL. Default for [SRV Connection Format.]" / "false`: Initiates the connection without TLS/SSL. Default for [Standard Connection String Format.]"
|
||||
|
||||
> [§`retryWrites` / `retryReads`] "Official MongoDB drivers default to `true`." (두 옵션 모두 동일 문장으로 기본값 명시)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-CONNSTR-C1 | `serverSelectionTimeoutMS` 는 서버 선택을 위해 얼마나 오래 대기할지(밀리초)를 지정하며, 초과 시 예외를 던진다. 기본값은 30,000ms 이다 | [§`serverSelectionTimeoutMS`] "Specifies how long (in milliseconds) to block for server selection before throwing an exception. _Default_: 30,000 milliseconds." | `official-vendor-doc` | 공식 MongoDB 드라이버가 이 URI 옵션을 지원하는 배포 전반 | 특정 드라이버(예: Spring Data MongoDB 가 사용하는 Java driver) 가 이 기본값을 그대로 상속하는지는 개별 드라이버 문서 확인 필요 |
|
||||
| MONGO-CONNSTR-C2 | `connectTimeoutMS` 는 연결 시도를 timeout 처리하기 전까지 걸리는 시간(밀리초)이며, 기본값은 10,000ms 이지만 드라이버마다 다를 수 있다 | [§`connectTimeoutMS`] "The time in milliseconds to attempt a connection before timing out. The default is 10,000 milliseconds, but specific drivers might have a different default." | `official-vendor-doc` | 연결 수립 단계의 timeout 일반 의미 | "특정 드라이버가 다른 기본값을 가질 수 있다"는 문장 자체가 그 드라이버가 무엇인지는 밝히지 않음 — 실제 채택 드라이버의 기본값은 별도 확인 필요 |
|
||||
| MONGO-CONNSTR-C3 | `socketTimeoutMS` 는 소켓에서 송수신을 시도하는 시간(밀리초)이며, 기본값은 "no timeout"(무제한)이다 | [§`socketTimeoutMS`] "The time in milliseconds to attempt a send or receive on a socket before the attempt times out. The default is no timeout, though different drivers might vary." | `official-vendor-doc` | 연결 수립 이후 개별 read/write 소켓 작업의 timeout 일반 의미 | 드라이버가 실제로 이 "no timeout" 기본값을 그대로 두는지, 아니면 자체 기본값으로 override 하는지는 드라이버별 확인 필요 — 본 인용은 "different drivers might vary" 라고만 명시 |
|
||||
| MONGO-CONNSTR-C4 | `maxPoolSize` 는 connection pool 의 최대 연결 수를 의미하며, 기본값은 `100` 이다 | [§`maxPoolSize`] "The maximum number of connections in the connection pool. The default value is `100`." | `official-vendor-doc` | connection pool 크기 설정 URI 옵션을 지원하는 드라이버 전반 | `100` 이 모든 배포·워크로드에 적정한 값이라는 것은 이 문장이 증명하지 않음 — 값 산정(sizing) 기준은 별도 자료 필요 |
|
||||
| MONGO-CONNSTR-C5 | `minPoolSize` 는 connection pool 의 최소 연결 수를 의미하며, 기본값은 `0` 이다 | [§`minPoolSize`] "The minimum number of connections in the connection pool. The default value is `0`." | `official-vendor-doc` | connection pool 최소 유지 연결 수 개념 | 모든 드라이버가 이 옵션을 지원하는 것은 아님(자료 원문에 별도 명시) — 채택 드라이버의 지원 여부 확인 필요 |
|
||||
| MONGO-CONNSTR-C6 | `maxIdleTimeMS` 는 연결이 idle 상태로 pool 에 남아있다가 제거·종료되기까지의 최대 시간(밀리초)을 의미한다. 원문에는 이 옵션의 **명시적 숫자 기본값이 기재되어 있지 않다**(재확인 fetch로 "No default value is explicitly stated" 확인) | [§`maxIdleTimeMS`] "The maximum number of milliseconds that a connection can remain idle in the pool before being removed and closed." | `official-vendor-doc` | idle connection 회수 개념 자체 | 이 옵션의 실제 기본 동작(예: 무제한 유지)이 무엇인지는 이 인용만으로 증명되지 않음 — "모든 드라이버가 지원하는 것은 아니다"라고만 명시되어 있어, 채택 드라이버 문서에서 실제 기본 동작 확인 필요 |
|
||||
| MONGO-CONNSTR-C7 | `tls`(=`ssl`) 옵션의 기본값은 연결 문자열 형식에 따라 다르다 — SRV 형식(`mongodb+srv://`)에서는 `true` 가 기본, Standard 형식(`mongodb://`)에서는 `false` 가 기본이다 | [§`tls`] "true`: Initiates the connection with TLS/SSL. Default for [SRV Connection Format.]" / "false`: Initiates the connection without TLS/SSL. Default for [Standard Connection String Format.]" | `official-vendor-doc` | `mongodb://` 또는 `mongodb+srv://` 형식의 연결 문자열을 사용하는 모든 공식 드라이버 | TLS 미사용(Standard 형식 + `tls` 미지정) 상태에서 실제 운영 환경에 어떤 위험이 있는지는 이 문서가 규정하지 않음 — 위험 판단은 별도 보안 근거 필요 |
|
||||
| MONGO-CONNSTR-C8 | `retryWrites`/`retryReads` 는 공식(official) MongoDB 드라이버에서 기본값이 `true` 이다(재시도 가능 write/read 가 기본 활성화) | [§`retryWrites`/`retryReads`] "Official MongoDB drivers default to `true`." | `official-vendor-doc` | "공식" MongoDB 드라이버(자체 명시)를 사용하는 연결 | 서드파티/커스텀 드라이버 또는 `mongosh` 는 이 기본값 범위 밖 — 원문에 "`mongosh` does not support retryable reads" 라고 별도 명시(이번 raw 에는 별도 claim 미등록, 참고용으로만 기재) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `MONGO-CONNSTR-C1`: `serverSelectionTimeoutMS` 기본값 30,000ms
|
||||
- `MONGO-CONNSTR-C2`: `connectTimeoutMS` 기본값 10,000ms(드라이버별 상이 가능)
|
||||
- `MONGO-CONNSTR-C3`: `socketTimeoutMS` 기본값 "no timeout"(드라이버별 상이 가능)
|
||||
- `MONGO-CONNSTR-C4`~`C5`: `maxPoolSize` 기본값 100, `minPoolSize` 기본값 0
|
||||
- `MONGO-CONNSTR-C6`: `maxIdleTimeMS` 의미는 정의되지만 **명시적 숫자 기본값은 이 문서에 없음**
|
||||
- `MONGO-CONNSTR-C7`: `tls` 기본값이 연결 문자열 형식(SRV vs Standard)에 따라 갈린다는 사실
|
||||
- `MONGO-CONNSTR-C8`: 공식 드라이버 기준 `retryWrites`/`retryReads` 기본값 `true`
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- ca-skeleton 이 실제로 채택할 특정 드라이버(예: Spring Data MongoDB 가 감싸는 MongoDB Java Driver)가 이 문서의 기본값을 정확히 그대로 상속하는지 — 원문 곳곳에 "specific drivers might have a different default" / "different drivers might vary" 라고 반복 명시되어 있어, 채택 드라이버 자체 문서 교차 확인이 필요함
|
||||
- `maxPoolSize: 100`, `serverSelectionTimeoutMS: 30000` 등 기본값이 ca-skeleton 의 실제 트래픽·SLA 에 적정한지 — 이 문서는 정의와 기본값만 제공, sizing 판단 근거 아님
|
||||
- `w`/`j`/`wtimeout` 등 write concern 관련 옵션 — 별도 자료 [[raw/official-docs/write-concern-mongodb-official]] 로 이미 수집됨, 본 자료 범위 밖
|
||||
- read concern / change streams / replica-set 트랜잭션 요건 — 본 branch 완료 조건에 포함되나 이 자료는 다루지 않음(잔여 수집 대상)
|
||||
- **ca-skeleton 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- Spring Data MongoDB(또는 채택할 드라이버)가 이 URI 옵션들을 어떤 설정 property(예: `spring.data.mongodb.uri` vs 개별 property)로 노출하는지 — 별도 official-doc 필요
|
||||
- env registry 에 노출할 실제 값(예: `serverSelectionTimeoutMS` 를 30,000 그대로 둘지 낮출지)은 이 자료가 아니라 branch 의 §구현 가이드에서 운영 요구사항 기반으로 결정 — 본 자료는 "기본값이 무엇이고 왜 명시가 필요한지"까지만 증명
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 최초 WebFetch 결과는 소형 모델이 각 옵션을 새 markdown 표(재구성)로 요약해 반환했다 — paraphrase 위험이 있어 폐기하고, "Do NOT summarize or reformat" 지시를 명시한 재요청으로 verbatim 인용을 다시 받았다. `maxIdleTimeMS` 의 명시적 기본값 유무는 3차 재요청으로 별도 확인("No default value is explicitly stated")했다.
|
||||
- `tls`/`ssl` 인용은 원문의 markdown 링크 문법(`[SRV Connection Format.](url)`)이 포함되어 있어, self-grep 매칭을 위해 링크 텍스트까지만(`[SRV Connection Format.]`) 부분 문자열로 인용했다 — 원문 자체의 하이퍼링크 마크업이며 내용 왜곡 아님.
|
||||
- 잔여 수집 대상(이번 raw 범위 밖): read concern 공식 정의, change streams resume token 공식 정의, replica-set 트랜잭션 요건 공식 문서.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/write-concern-mongodb-official]] (write concern `w`/`j`/`wtimeout` — 본 자료와 상보적)
|
||||
- 인용하는 branch: [[raw/branch-notes/feature-mongo-runtime-baseline-contract]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: "official-doc / MongoDB — Read Concern (Levels & Guarantees)"
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/reference/read-concern/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, consistent-read]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Read Concern (Levels & Guarantees)
|
||||
|
||||
> Layer: `raw/official-docs/` — MongoDB 공식 Manual reference (`read-concern`) 원문 발췌.
|
||||
> `feature-mongo-runtime-baseline-contract` 의 "MongoDB 조회는 read concern 을 명시하고 기본값에 의존하지 않는다" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | MongoDB 조회는 read concern 을 명시하고 기본값에 의존하지 않는다 — `local`/`available`/`majority`/`linearizable`/`snapshot` 5개 level 이 각각 무엇을 보장/비보증하는지, `majority` 가 replica-set·multi-document transaction 과 맺는 관계, level 별 topology·operation 제약(예: `linearizable` 은 primary 전용, `snapshot` 은 트랜잭션 밖에서 `find`/`aggregate`/`distinct`(unsharded 전용)로만 제한)의 verbatim 근거 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-skeleton 은 MongoDB 를 optional adapter(문서 저장소 축)로 채택할 수 있다. 본 branch 는 조회(read) 시 read concern 을 애플리케이션/드라이버 레벨에서 **명시**할지, 서버 implicit default 에 의존할지를 결정해야 한다. 이 판단에는 (1) 5개 level 각각이 어떤 데이터를 반환하고 무엇을 보증/비보증하는지, (2) `majority` read concern 이 multi-document transaction 안에서 write concern 과 맺는 관계, (3) 각 level 이 어떤 topology(standalone/replica set/sharded cluster)·operation 에서만 쓸 수 있는지에 대한 공식 정의가 필요하다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/reference/read-concern/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: MongoDB, Inc.
|
||||
- 발행일: MongoDB Manual (현재 버전 — level별 상세 하위 페이지 `read-concern-local`/`read-concern-available`/`read-concern-majority`/`read-concern-linearizable`/`read-concern-snapshot` 를 링크하는 최신 manual 구조로 판단)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§read-concern-local / §read-concern-available — 두 level 이 동일 문장을 공유] "The query returns data from the instance with no guarantee that the data has been written to a majority of the replica set members. Data may be rolled back."
|
||||
|
||||
> [§read-concern-local] "Default for reads against the primary and secondaries."
|
||||
|
||||
> [§read-concern-available] "However, this comes at the expense of consistency as "available" read concern can return orphaned documents when reading from a sharded collection."
|
||||
|
||||
> [§read-concern-majority] "The query returns the data acknowledged by a majority of the replica set members. Returned documents are durable, even if a failure occurs."
|
||||
|
||||
> [§read-concern-majority — Requirements] "Replica sets must use the WiredTiger storage engine."
|
||||
|
||||
> [§read-concern-majority — Transactions] "For multi-document transactions, read concern "majority" provides its guarantees only if the transaction commits with write concern "majority"." [...] "Otherwise, "majority" provides no guarantees about data read in transactions."
|
||||
|
||||
> [§read-concern-linearizable] "You can only specify linearizable read concern for read operations on the primary."
|
||||
|
||||
> [§read-concern-linearizable] "Linearizable read concern guarantees only apply if read operations specify a query filter that uniquely identifies a single document."
|
||||
|
||||
> [§read-concern-snapshot] "A query with read concern "snapshot" returns majority-committed data as it appears across shards from a specific single point in time in the recent past."
|
||||
|
||||
> [§read-concern-snapshot — Transactions] "Read concern "snapshot" provides its guarantees only if the transaction commits with write concern "majority"."
|
||||
|
||||
> [§read-concern-snapshot — Supported Operations] "The following methods outside of multi-document transactions:" ... "distinct on unsharded collections only"
|
||||
|
||||
> [§Read Concern and the local Database] "The local database does not support read concerns. MongoDB silently ignores any configured read concern for operations on collections in the local database."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-READCONCERN-C1 | `local` read concern 은 인스턴스가 가진 데이터를 반환하며, 그 데이터가 replica set 과반수에 기록되었다는 보장이 없고 rollback 될 수 있다 | [§read-concern-local] "The query returns data from the instance with no guarantee that the data has been written to a majority of the replica set members. Data may be rolled back." | `official-vendor-doc` | `local` read concern 을 사용하는 primary/secondary read 전반 | 어느 정도 stale 할 수 있는지(지연 시간)는 이 문장이 규정하지 않음 |
|
||||
| MONGO-READCONCERN-C2 | `local` 은 primary·secondary 조회 모두에 대해 기본(default) read concern 이다 | [§read-concern-local] "Default for reads against the primary and secondaries." | `official-vendor-doc` | read concern 을 명시하지 않은 모든 조회(단, causally consistent session/transaction 등 다른 기본값이 적용되는 맥락은 제외) | 모든 driver/client 설정에서 예외 없이 항상 `local` 이 적용된다는 것까지는 이 한 문장만으로 보장하지 않음 — 세션/트랜잭션별 기본값은 별도 확인 필요 |
|
||||
| MONGO-READCONCERN-C3 | `available` read concern 은 sharded collection 조회 시 orphaned document(잘못 라우팅되어 남아있는 document)를 반환할 수 있다 | [§read-concern-available] "However, this comes at the expense of consistency as "available" read concern can return orphaned documents when reading from a sharded collection." | `official-vendor-doc` | `available` read concern, sharded cluster topology | non-sharded replica set 에서도 동일한 orphaned document 위험이 있다는 뜻은 아님 — orphaned document 는 sharding 특유 현상 |
|
||||
| MONGO-READCONCERN-C4 | `majority` read concern 은 replica set 과반수에 승인(acknowledge)된 데이터를 반환하며, 실패가 발생해도 반환된 문서는 durable 하다 | [§read-concern-majority] "The query returns the data acknowledged by a majority of the replica set members. Returned documents are durable, even if a failure occurs." | `official-vendor-doc` | `majority` read concern 을 사용하는 replica set 조회 전반 | 성능이 다른 level 과 항상 동일하다는 것을 이 문장 자체가 수치로 증명하지는 않음 |
|
||||
| MONGO-READCONCERN-C5 | `majority` read concern 을 사용하려면 replica set 이 WiredTiger storage engine 을 써야 한다 | [§read-concern-majority — Requirements] "Replica sets must use the WiredTiger storage engine." | `official-vendor-doc` | `majority` read concern 채택 여부 판단 | 다른 storage engine 에서 정확히 어떤 에러/폴백이 발생하는지는 이 문장이 설명하지 않음 — "요구사항"만 명시 |
|
||||
| MONGO-READCONCERN-C6 | multi-document transaction 안에서 `majority` read concern 은 트랜잭션이 write concern `"majority"` 로 커밋될 때만 보장을 제공하며, 그렇지 않으면 트랜잭션 내에서 읽은 데이터에 대해 어떤 보장도 제공하지 않는다 | [§read-concern-majority — Transactions] "For multi-document transactions, read concern "majority" provides its guarantees only if the transaction commits with write concern "majority". Otherwise, "majority" provides no guarantees about data read in transactions." | `official-vendor-doc` | multi-document transaction 내부에서 `majority` read concern 을 사용하는 모든 read | 트랜잭션 밖의 단일 read operation 에서의 `majority` 보장 여부는 별도(C4 가 이미 커버) — 이 claim 은 트랜잭션 맥락에 한정 |
|
||||
| MONGO-READCONCERN-C7 | `linearizable` read concern 은 replica set 의 primary 멤버에서 수행하는 read operation 에만 지정할 수 있다 | [§read-concern-linearizable] "You can only specify linearizable read concern for read operations on the primary." | `official-vendor-doc` | `linearizable` read concern, replica set topology(primary 한정) | sharded cluster 에서 `linearizable` 사용 가능 여부를 직접 서술하지 않음 — primary member 제약만 명시 |
|
||||
| MONGO-READCONCERN-C8 | `linearizable` read concern 의 보장은 read operation 이 단일 document 를 고유하게 식별하는 query filter 를 지정할 때만 적용된다 | [§read-concern-linearizable] "Linearizable read concern guarantees only apply if read operations specify a query filter that uniquely identifies a single document." | `official-vendor-doc` | `linearizable` 을 사용하는 쿼리의 filter 설계 | 다중 document 대상 쿼리 자체가 문법적으로 금지된다는 뜻은 아님 — "guarantee 가 적용되지 않는다"는 의미로, 실행 자체의 허용 여부와는 구분 |
|
||||
| MONGO-READCONCERN-C9 | `snapshot` read concern 은 여러 shard 에 걸쳐 특정 단일 시점(recent past)의 majority-committed 데이터를 반환한다 | [§read-concern-snapshot] "A query with read concern "snapshot" returns majority-committed data as it appears across shards from a specific single point in time in the recent past." | `official-vendor-doc` | `snapshot` read concern, sharded cluster 포함 다중 shard 조회 | 이 보장이 자동으로 오는 것은 아니며 트랜잭션이 write concern `"majority"` 로 커밋되어야 함(별도 C10) |
|
||||
| MONGO-READCONCERN-C10 | `snapshot` read concern 의 보장은 트랜잭션이 write concern `"majority"` 로 커밋될 때만 제공된다 | [§read-concern-snapshot — Transactions] "Read concern "snapshot" provides its guarantees only if the transaction commits with write concern "majority"." | `official-vendor-doc` | multi-document transaction 내 `snapshot` read concern | 트랜잭션 밖에서 사용하는 `snapshot`(find/aggregate/distinct)에도 "트랜잭션 커밋" 조건이 그대로 적용된다는 뜻은 아님 — 트랜잭션 밖 사용은 별도(C11) |
|
||||
| MONGO-READCONCERN-C11 | multi-document transaction 밖에서 `snapshot` read concern 을 사용할 수 있는 operation 은 `find`, `aggregate`, 그리고 `distinct`(단, unsharded collection 에서만)로 제한된다 | [§read-concern-snapshot — Supported Operations] "The following methods outside of multi-document transactions:" ... "distinct on unsharded collections only" | `official-vendor-doc` | 트랜잭션 없이 단일 operation 에 `snapshot` read concern 을 지정하는 경우 | sharded collection 에서 트랜잭션 밖 `distinct` + `snapshot` 조합이 가능한지는 이 문장이 오히려 **불가능**하다고 증명함(허용이 아니라 제약 근거) — 트랜잭션 내부의 `distinct` 는 이 제약과 무관(트랜잭션-레벨이므로) |
|
||||
| MONGO-READCONCERN-C12 | MongoDB 의 `local` 데이터베이스(리플리케이션 메타데이터 등 내부용 특수 DB)에 대한 operation 은 read concern 을 지원하지 않으며, 어떤 read concern 이 설정되어도 조용히 무시된다 | [§Read Concern and the local Database] "The local database does not support read concerns. MongoDB silently ignores any configured read concern for operations on collections in the local database." | `official-vendor-doc` | 이름이 `local` 인 특수 시스템 데이터베이스 상의 operation | 사용자 애플리케이션 데이터가 담긴 일반 database/collection 에서도 read concern 이 무시된다는 뜻이 아님 — 오직 시스템 `local` DB 에만 해당(read concern **level** `"local"` 과는 다른 개념이므로 혼동 주의) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `MONGO-READCONCERN-C1`~`C2`: `local` 이 무엇을 (비)보증하는지, 그리고 read 의 implicit default 라는 사실
|
||||
- `MONGO-READCONCERN-C3`: `available` 이 sharded collection 에서 orphaned document 를 반환할 수 있다는 위험
|
||||
- `MONGO-READCONCERN-C4`~`C6`: `majority` 의 정의, WiredTiger 요건, multi-document transaction 내에서의 조건부 보장(write concern `"majority"` 커밋 필요)
|
||||
- `MONGO-READCONCERN-C7`~`C8`: `linearizable` 이 primary 전용이고 단일 document uniquely-identifying filter 를 요구한다는 제약
|
||||
- `MONGO-READCONCERN-C9`~`C11`: `snapshot` 의 point-in-time majority-committed 정의, 트랜잭션 write concern `"majority"` 조건, 트랜잭션 밖 사용 시 operation 제한(`find`/`aggregate`/`distinct`-unsharded-only)
|
||||
- `MONGO-READCONCERN-C12`: 시스템 `local` 데이터베이스는 read concern 설정 자체를 무시함
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- 각 level 의 실제 지연시간(latency) 수치 비교 — 상대적 순서(예: `linearizable` 이 가장 느림)는 원문에 언급되나 이번 raw 에는 verbatim 인용/self-grep 미포함(별도 확인 필요)
|
||||
- 드라이버(Java Reactive Streams / Spring Data MongoDB 등)가 read concern 을 어떤 설정 키·API 로 노출하는지 — 이 자료는 서버 개념만 정의
|
||||
- causally consistent session 에서의 read concern 조합 세부(예: `afterClusterTime` 자동 설정 메커니즘) — 이번 raw 수집 범위 밖(추가 확인 필요 항목으로 아래 메모에 기록)
|
||||
- `readConcern` provenance(`clientSupplied`/`customDefault`/`implicitDefault`) 추적 메커니즘의 세부 동작 — 이번 raw 에는 verbatim 미수집
|
||||
- **ca-skeleton 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-skeleton 이 채택할 MongoDB 배포(standalone / replica set / sharded cluster)에 따라 어떤 level 조합이 실제로 유효한지 재확인 — 특히 `linearizable`(primary 전용)·`snapshot`(트랜잭션 or 제한된 operation) 은 sharded cluster 여부에 민감
|
||||
- `majority` 채택 시 실제 배포가 WiredTiger 를 쓰는지(현대 MongoDB 배포판은 기본값이나, 별도 storage engine 설정을 쓰지 않는지) 확인
|
||||
- Spring Data MongoDB(또는 채택할 드라이버)가 `readConcern` 을 어떤 설정 키·API 로 노출하는지 — 별도 official-doc 필요 (`raw/official-docs/write-concern-mongodb-official.md` 와 동일한 드라이버 바인딩 gap)
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 본 fetch 는 WebFetch 소형 모델이 markdown 하이퍼링크 문법으로 원문을 재구성한 형태였다. 링크 문법이 문장 중간에 끼어들어 self-grep 리터럴 매칭이 여러 차례 깨졌다(예: "available" read concern can return orphaned documents 사이에 "orphaned documents" 를 감싸는 하이퍼링크가 삽입된 형태). 총 7회의 WebFetch 재요청으로 (1) 하이퍼링크 마크업 제거 + (2) level 이름을 감싸는 큰따옴표(`"majority"` 등, 문서 값 표기의 일부)는 보존하도록 명시적으로 지시해 최종 인용을 확보했다. 원문 페이지 자체의 HTML 은 curl 로 직접 대조하지 않음 — 다음 재검증 시 curl 직접 fetch 로 교차 확인 권장(`write-concern-mongodb-official.md` 메모와 동일한 한계).
|
||||
- `local`/`available` 두 level 의 기본 정의 문장이 원문에서 **완전히 동일**하다(WebFetch 3회 반복 확인, 동일 문자열 재현). 두 level 의 실질적 차이는 이 공유 문장이 아니라 (1) `available` 만 causally consistent session/transaction 에서 사용 불가하고 (2) sharded collection 에서 orphaned document 를 반환할 수 있다는 추가 제약(C3)에 있다.
|
||||
- 미수집(추가 fetch 필요): causally consistent session 에서 사용 가능한 level 목록 문장("local"/"majority"/"snapshot"), `readConcern` provenance 3종 값의 세부 동작, level별 상대적 성능 비교 문장(`linearizable` 이 가장 느림 — best practice 로 `maxTimeMS` 권장).
|
||||
- 다음 fetch 후보: MongoDB 공식 `causal-consistency`(causally consistent session) 페이지, `transactions` 페이지(read concern 과 write concern 조합 표).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc:
|
||||
- [[raw/official-docs/write-concern-mongodb-official]] — write concern (`w`/`j`/`wtimeout`) 공식 근거, 같은 branch 의 write 측 대응 문서
|
||||
- [[raw/official-docs/change-streams-mongodb-official]] — change stream resume token/oplog window
|
||||
- [[raw/official-docs/read-preference-mongodb-official]] — 조회를 어느 replica set 멤버로 라우팅할지(read concern 과는 별개 축)
|
||||
- [[raw/official-docs/retryable-writes-mongodb-official]] — write 재시도 공식 근거
|
||||
- [[raw/official-docs/mongodb-connection-string-options]] — connection string 옵션
|
||||
- 인용하는 branch: [[raw/branch-notes/feature-mongo-runtime-baseline-contract]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: official-doc / MongoDB — Read Preference (Replica Set Read Routing)
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/core/read-preference/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, read-preference]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Read Preference (Replica Set Read Routing)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — MongoDB 공식 매뉴얼(레퍼런스) 문서.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | 조회의 replica 라우팅을 read preference mode로 명시하고, secondary 읽기는 stale read를 허용하는 경로에만 쓴다는 결정 — mode 5종 정의 + secondary 읽기 stale 경고 + maxStalenessSeconds 메커니즘 + 트랜잭션 내 read preference 제약의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/core/read-preference/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: MongoDB, Inc. (공식 매뉴얼)
|
||||
- 발행일: (문서 자체에 명시 없음 — 버전별 롤링 업데이트되는 레퍼런스 페이지)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch가 "조회 replica 라우팅을 read preference로 명시하고, secondary 읽기는 stale read를 허용하는 경로에만 쓴다"를 결정하는 데 필요한 5종 mode 정의, stale 경고, maxStalenessSeconds 메커니즘, 트랜잭션 제약의 공식 근거로 보관.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Behavior] "All read preference modes except `primary` may return stale data because secondaries replicate operations from the primary in an asynchronous process. Ensure that your application can tolerate stale data if you choose to use a non-`primary` mode."
|
||||
|
||||
> [§Read Preference and Transactions] "Transactions that contain read operations must use read preference `primary`. All operations in a given transaction must route to the same member."
|
||||
|
||||
> [§Read Preference Modes (Detailed) — primary] "All read operations use only the current replica set primary. This is the default read mode. If the primary is unavailable, read operations produce an error or throw an exception."
|
||||
|
||||
> [§Read Preference Modes (Detailed) — secondary] "Operations read _only_ from the secondary members of the set. If no secondaries are available, then this read operation produces an error or exception."
|
||||
|
||||
> [§Read Preference Modes (Detailed) — secondary] "When the `secondary` read preference includes a maxStalenessSeconds value, the client estimates how stale each secondary is by comparing the secondary's last write to that of the primary. The client then directs the read operation to a secondary whose estimated lag is less than or equal to `maxStalenessSeconds`. If there is no primary, the client uses the secondary with the most recent write for the comparison."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-READPREF-C1 | `primary` 모드가 기본값이며 primary에서만 읽는다. primary 사용 불가 시 에러/예외가 발생한다 | [§primary] "All read operations use only the current replica set primary. This is the default read mode. If the primary is unavailable, read operations produce an error or throw an exception." | `official-vendor-doc` | MongoDB 드라이버 전반의 기본 read routing 동작 | ca-skeleton이 실제로 이 기본값을 재정의하지 않았는지는 코드 확인 필요 |
|
||||
| MONGO-READPREF-C2 | `secondary` 모드는 secondary에서만 읽으며, 가용한 secondary가 없으면 에러/예외가 발생한다 | [§secondary] "Operations read _only_ from the secondary members of the set. If no secondaries are available, then this read operation produces an error or exception." | `official-vendor-doc` | secondary 전용 조회 경로를 선택했을 때의 실패 모드 근거 | 특정 배포에서 이 실패가 얼마나 자주 발생하는지는 증명 안 함 |
|
||||
| MONGO-READPREF-C3 | `primary`를 제외한 모든 read preference 모드는 secondary의 비동기 복제 지연 때문에 stale data를 반환할 수 있다. 애플리케이션이 stale data를 감내할 수 있어야 한다 | [§Behavior] "All read preference modes except `primary` may return stale data because secondaries replicate operations from the primary in an asynchronous process. Ensure that your application can tolerate stale data if you choose to use a non-`primary` mode." | `official-vendor-doc` | secondary/secondaryPreferred/nearest/primaryPreferred 조회 경로 전체에 stale read 가능성이 있다는 근거 | 구체적인 replication lag 수치(ms/sec)는 배포 환경에 따라 다르며 이 문서는 수치를 제시하지 않음 |
|
||||
| MONGO-READPREF-C4 | `secondary` 모드에서 `maxStalenessSeconds`가 지정되면, 클라이언트는 secondary의 마지막 write와 primary의 마지막 write를 비교해 추정 지연이 임계값 이하인 secondary로만 라우팅한다 | [§secondary] "When the `secondary` read preference includes a maxStalenessSeconds value, the client estimates how stale each secondary is by comparing the secondary's last write to that of the primary. The client then directs the read operation to a secondary whose estimated lag is less than or equal to `maxStalenessSeconds`. If there is no primary, the client uses the secondary with the most recent write for the comparison." | `official-vendor-doc` | maxStalenessSeconds를 지정했을 때 secondary 선택 알고리즘의 근거 | `maxStalenessSeconds`의 허용 최소값(예: 90초 같은 하한)은 이 페이지에 없음 — `needs-confirmation`, 별도 페이지(`docs/manual/core/read-preference-staleness/`) 확인 필요 |
|
||||
| MONGO-READPREF-C5 | read operation을 포함한 트랜잭션은 반드시 read preference `primary`를 사용해야 하고, 한 트랜잭션 내 모든 연산은 같은 멤버로 라우팅되어야 한다 | [§Read Preference and Transactions] "Transactions that contain read operations must use read preference `primary`. All operations in a given transaction must route to the same member." | `official-vendor-doc` | multi-document transaction 안에서는 read preference를 primary 이외로 설정할 수 없다는 제약의 근거 | 트랜잭션 밖의 단일 연산에는 이 제약이 적용되지 않음(5개 모드 모두 사용 가능) — 이 문서가 그 대비를 명시하지는 않음, 유추 |
|
||||
| MONGO-READPREF-C6 | `primaryPreferred` 모드는 대부분 primary에서 읽되, failover 등으로 primary가 없으면 `maxStalenessSeconds`와 tag set을 만족하는 secondary에서 읽는다 | [§primaryPreferred] "In most situations, operations read from the primary member of the set. However, if the primary is unavailable, as is the case during failover situations, operations read from secondary members that satisfy the read preference's `maxStalenessSeconds` and tag set lists." | `official-vendor-doc` | failover 시 fallback 라우팅 동작 근거 | 여러 조건을 만족하는 secondary가 다수일 때 정확히 어느 secondary가 선택되는지 세부 알고리즘은 별도 문서(member selection) 확인 필요 |
|
||||
| MONGO-READPREF-C7 | `secondaryPreferred` 모드는 보통 secondary에서 읽되, 유일한 멤버가 primary뿐이면 primary에서 읽는다 | [§secondaryPreferred] "Operations typically read data from secondary members of the replica set. If the replica set has only one single primary member and no other members, operations read data from the primary member." | `official-vendor-doc` | secondary 우선 + primary fallback 라우팅 동작 근거 | 조건을 만족하는 secondary가 없을 때(단일 primary 이외의 경우)의 세부 fallback 조건은 별도 상세 문단 확인 필요 |
|
||||
| MONGO-READPREF-C8 | `nearest` 모드는 네트워크 지연이 낮은 멤버에서 읽으며, primary/secondary 역할을 구분하지 않는다 | [§nearest] "The driver reads from a member whose network latency falls within the acceptable latency window. Reads in the `nearest` mode do not consider whether a member is a primary or secondary when routing read operations: primaries and secondaries are treated equivalently." | `official-vendor-doc` | 지연 최소화 목적의 조회 라우팅 근거 | latency window의 정확한 계산식(`localThresholdMS` 등)은 이 인용 범위 밖 — 별도 문단 확인 필요 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, MongoDB 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGO-READPREF-C1`~`C3`, `C6`~`C8`: read preference 5종 모드의 공식 정의와 각 모드의 기본 라우팅/실패 동작
|
||||
- `MONGO-READPREF-C3`: `primary`를 제외한 모든 모드가 stale data를 반환할 수 있다는 것 (branch의 "secondary 읽기는 stale read 허용 경로에만" 결정의 핵심 근거)
|
||||
- `MONGO-READPREF-C4`: `maxStalenessSeconds`가 어떻게 secondary 선택에 반영되는지의 메커니즘
|
||||
- `MONGO-READPREF-C5`: multi-document transaction 안에서 read preference가 `primary`로 고정되어야 한다는 제약
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `maxStalenessSeconds`의 허용 **최소값**(예: 90초 같은 수치 하한) — 이 URL에는 해당 수치가 없음. 링크된 하위 페이지(`docs/manual/core/read-preference-staleness/`)에 있을 가능성이 있으나 별도 dispatch로 확인 필요 (1 dispatch = 1 URL 원칙에 따라 본 자료에 포함하지 않음)
|
||||
- 특정 드라이버(Java, Node.js 등)의 실제 구현 세부사항 — 이 페이지는 드라이버 무관 개념 문서
|
||||
- sharded cluster 전용 동작(`SystemOverloadedError`, `terminateSecondaryReadsOnOrphanCleanup`)이 ca-skeleton의 배포 토폴로지(단일 replica set 가정 여부)에 적용되는지 여부
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton의 실제 MongoDB 배포가 replica set인지, sharded cluster인지 (전자에만 이 문서의 기본 가정이 그대로 적용됨)
|
||||
- `maxStalenessSeconds`의 최소값 제약 — 별도 raw 자료(`read-preference-staleness`) dispatch 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- `MONGO-READPREF-C4`의 "의미"는 확보했으나, 사용자가 명시적으로 요구한 "최소값 제약"(90초 등 구체적 하한)은 이 URL의 fetch 결과에 존재하지 않았다. 자체 grep으로도 확인되지 않음 — fabrication 방지를 위해 claim에 포함하지 않고 gap으로 남김.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `https://www.mongodb.com/docs/manual/core/read-preference-staleness/` (maxStalenessSeconds 최소값 제약 확인용), `https://www.mongodb.com/docs/manual/core/read-preference-tags/` (tag set 상세)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (write concern 관련 자료가 taxonomy changelog에 `raw/official-docs/write-concern-mongodb-official.md`로 예고되어 있으나 2026-07-28 기준 아직 생성되지 않음 — 생성되면 상호 링크)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: "official-doc / Redis ACL — Access Control Lists (command·key 단위 최소 권한)"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, security, redis, least-privilege]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Redis ACL — Access Control Lists (command·key 단위 최소 권한)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> Redis 공식 문서 — Operate › Management › Security › ACL
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | Redis role(cache-runtime / coordination-runtime / session-runtime)마다 별도 named ACL user 를 두고, `reset` 으로 초기화된(=`-@all`) 상태에서 필요한 command category·key pattern 만 선택적으로 부여하는 least-privilege 결정의 공식 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/
|
||||
- 아카이브 URL: (미확보)
|
||||
- 저자 / 조직: Redis (공식 문서)
|
||||
- 발행일: (버전 미표기 페이지 — Redis 7.0+ 기능 포함 서술)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-redis-runtime-role-isolation-contract` branch 가 role 별 별도 named ACL user + `reset -@all` 기반 least-privilege 부여를 결정하려면, ACL 이 실제로 command·key 단위로 권한을 제한하는 메커니즘인지, 새 user 가 기본적으로 무권한 상태로 생성되는지, `+@<category>`/`~<pattern>` 문법이 무엇을 의미하는지를 Redis 공식 문서로 확인해야 했음. 이 자료는 그 메커니즘 전체(정의·문법·기본값·reset 동작·카테고리 예시)를 공식 근거로 제공한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 7개)
|
||||
|
||||
> [§(intro), self-grep L214] "The Redis ACL, short for Access Control List, is the feature that allows certain connections to be limited in terms of the commands that can be executed and the keys that can be accessed."
|
||||
|
||||
> [§ACL rules — key patterns, self-grep L104] "`~<pattern>`: Add a pattern of keys that can be mentioned as part of commands. For instance `~*` allows all the keys. The pattern is a glob-style pattern like the one of KEYS. It is possible to specify multiple patterns."
|
||||
|
||||
> [§Create and edit user ACLs — new user status, self-grep L170] "* There are no key patterns that the user can access."
|
||||
|
||||
> [§ACL rules — command categories, self-grep L96] "`+@<category>`: Add all the commands in such category to be called by the user, with valid categories being like @admin, @set, @sortedset, ... and so forth, see the full list by calling the ACL CAT command. The special category @all means all the commands, both the ones currently present in the server, and the ones that will be loaded in the future via modules."
|
||||
|
||||
> [§ACL rules — reset directive, self-grep L135] "`reset` Performs the following actions: resetpass, resetkeys, resetchannels, allchannels (if acl-pubsub-default is set), off, clearselectors, -@all. The user returns to the same state it had immediately after its creation."
|
||||
|
||||
> [§When ACLs are useful — goal 2, self-grep L41] "2. You want to improve operational safety, so that processes or humans accessing Redis are not allowed to damage the data or the configuration due to software errors or manual mistakes. For instance, there is no reason for a worker that fetches delayed jobs from Redis to be able to call the FLUSHALL command."
|
||||
|
||||
> [§Command categories — admin, self-grep L216] "* **admin** - Administrative commands. Normal applications will never need to use these. Includes REPLICAOF, CONFIG, DEBUG, SAVE, MONITOR, ACL, SHUTDOWN, etc."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-ACL-C1 | Redis ACL 은 연결(connection) 단위로 실행 가능한 command 와 접근 가능한 key 를 제한하는 기능이다 | [§intro] "...is the feature that allows certain connections to be limited in terms of the commands that can be executed and the keys that can be accessed." | `official-vendor-doc` | Redis 6 이상의 모든 ACL 사용 배포 | 네트워크 계층 격리(포트 분리, VPC/보안그룹)까지 ACL 이 대신한다는 뜻은 아님 — ACL 은 인증된 연결 내부의 command/key 권한만 제한 |
|
||||
| REDIS-ACL-C2 | `~<pattern>` 규칙은 glob-style 패턴으로 접근 가능한 key 를 지정하며, 여러 패턴을 동시에 지정할 수 있다 | [§key patterns] "`~<pattern>`: Add a pattern of keys that can be mentioned as part of commands... It is possible to specify multiple patterns." | `official-vendor-doc` | `ACL SETUSER` 또는 `aclfile` 로 정의되는 모든 named user 의 key 접근 범위 | key pattern 만으로 read/write 를 구분하지 않음 — read-only 또는 write-only 제한이 필요하면 별도 `%R~<pattern>`/`%W~<pattern>` (Redis 7.0+) 을 써야 함 |
|
||||
| REDIS-ACL-C3 | `ACL SETUSER <name>` 으로 새로 생성된 user 는 기본적으로 접근 가능한 key pattern 이 하나도 없다 | [§new user status] "* There are no key patterns that the user can access." | `official-vendor-doc` | `ACL SETUSER` 로 새로 생성되는 named user (예: `alice`) | 이미 존재하는 `default` user 에는 적용되지 않음 — `default` user 는 별도 설정 없이 `on nopass ~* &* +@all` (모든 key·모든 command 허용) 상태로 존재함 |
|
||||
| REDIS-ACL-C4 | `+@<category>` 는 해당 카테고리에 속한 모든 command 를 user 에게 허용하고, `@all` 은 현재/향후 모듈 command 를 포함한 모든 command 를 의미하는 특수 카테고리다 | [§command categories rule] "`+@<category>`: Add all the commands in such category... The special category @all means all the commands..." | `official-vendor-doc` | Redis 6 이상 ACL rule DSL 전체 (`+@<category>`/`-@<category>` 조합) | 어떤 command 가 어느 카테고리에 속하는지 이 문장만으로는 규정하지 않음 — 카테고리 소속 목록은 `ACL CAT <category>` 런타임 조회 또는 개별 카테고리 설명(C7 등)으로 별도 확인 필요 |
|
||||
| REDIS-ACL-C5 | `reset` 지시어는 resetpass, resetkeys, resetchannels, off, clearselectors, `-@all` 을 모두 수행해 user 를 생성 직후 상태로 되돌린다 | [§reset directive] "`reset` Performs the following actions: resetpass, resetkeys, resetchannels, allchannels (if acl-pubsub-default is set), off, clearselectors, -@all." | `official-vendor-doc` | `ACL SETUSER <user> reset ...` 호출 | `reset` 뒤에 별도로 `-@all` 을 다시 명시하는 것이 필수 문법이라는 뜻은 아님 — `reset` 자체가 이미 `-@all` 효과를 포함하므로, `reset -@all +get ...` 처럼 이어 쓰는 것은 명시적 표현이지 문법상 필수 절차가 아님 |
|
||||
| REDIS-ACL-C6 | 공식 문서는 ACL 을 쓰는 이유로 (1) untrusted/trusted client 의 접근을 최소 권한으로 제한하는 "보안" 목적과 (2) 소프트웨어 오류·사람 실수로 데이터/설정이 손상되지 않도록 하는 "운영 안전성" 목적, 두 가지를 나란히 제시한다 | [§goal 2] "You want to improve operational safety, so that processes or humans accessing Redis are not allowed to damage the data or the configuration due to software errors or manual mistakes. For instance, there is no reason for a worker that fetches delayed jobs from Redis to be able to call the FLUSHALL command." | `official-vendor-doc` | worker/서비스 role 별 command 제한 근거 (예: cache-runtime 이 `FLUSHALL`/`CONFIG` 를 호출할 이유가 없다는 논리) | **ACL이 "보안 경계가 아니라 실수 방지 목적"이라고 이 문서가 말하지는 않는다** — 원문은 보안(goal 1)과 운영 안전성(goal 2)을 별개의 두 목적으로 병렬 제시할 뿐, 운영 안전성이 보안을 대체하거나 ACL이 보안 경계가 아니라고 단정하지 않음 |
|
||||
| REDIS-ACL-C7 | `@admin` 카테고리는 `REPLICAOF`, `CONFIG`, `DEBUG`, `SAVE`, `MONITOR`, `ACL`, `SHUTDOWN` 등을 포함하며, 공식 문서는 "일반 애플리케이션은 이 command 들이 전혀 필요 없다"고 명시한다 | [§admin category] "**admin** - Administrative commands. Normal applications will never need to use these. Includes REPLICAOF, CONFIG, DEBUG, SAVE, MONITOR, ACL, SHUTDOWN, etc." | `official-vendor-doc` | 일반 애플리케이션 역할(캐시/좌표/세션 client)의 ACL 에서 `@admin` 을 제외하는 결정의 근거 | 목록이 "etc." 로 끝나 완전한 목록이 아님 — `@admin` 카테고리의 정확한 전체 command 목록은 런타임 `ACL CAT admin` 으로 별도 확인 필요 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-ACL-C1`~`C2`: ACL 이 command·key pattern 단위로 권한을 제한하는 메커니즘 자체
|
||||
- `REDIS-ACL-C3`: named user 를 새로 만들면 기본적으로 key 접근 권한이 0인 default-deny 상태로 시작 (role 별 named user 분리 설계의 안전한 출발점 근거)
|
||||
- `REDIS-ACL-C4`~`C5`: `+@<category>`/`-@<category>`/`reset`(=`-@all` 포함) 문법 의미
|
||||
- `REDIS-ACL-C6`: least-privilege 부여가 "운영 실수 방지"라는 공식적으로 인정된 목적 중 하나임 (단 유일한 목적은 아님 — 아래 참조)
|
||||
- `REDIS-ACL-C7`: `@admin` 카테고리에 속하는 대표 command 예시와 "일반 애플리케이션엔 불필요"라는 공식 권고
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **ACL이 "보안 경계가 아니라 실수 방지용"이라는 이분법** — 원문은 보안과 운영 안전성을 병렬적인 두 목적으로 제시하며, 어느 한쪽이 다른 쪽을 배제하거나 대체한다고 말하지 않음. branch 결정 근거에 이 이분법을 쓴다면 `REDIS-ACL-C6` 로 과대 해석한 것이 되므로 사용 금지
|
||||
- `@dangerous` 카테고리의 정확한 전체 command 목록 (이 자료엔 `@admin` 만 verbatim 인용 확보 — `@dangerous` 는 `FLUSHALL`, `MIGRATE`, `RESTORE`, `SORT`, `KEYS` 등을 포함한다고 서술되어 있으나 이번 인용 세트엔 미포함, 별도 확인 필요)
|
||||
- TLS·네트워크 계층 인증/암호화 (ACL 은 인증된 연결 내부의 command/key 권한만 다룸 — TLS·mTLS·network policy 는 별도 관심사)
|
||||
- `%R~<pattern>`/`%W~<pattern>` (read/write 세분화 key permission, Redis 7.0+) 과 selector(`(...)`, Redis 7.0+) 의 실제 branch 적용 여부 — 이 문서는 문법을 설명할 뿐 branch 가 이를 채택해야 한다고 규정하지 않음
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- cache-runtime / coordination-runtime / session-runtime 각 role 이 실제로 필요로 하는 command 목록(예: cache-runtime 은 `@string`+`@hash`+`@keyspace` 일부만, coordination-runtime 은 Lua/Function 실행 관련 `@scripting` 포함 여부) — role 별 command 카탈로그는 이 문서 범위 밖, `feature-redis-atomic-program-catalog-contract` 등 sibling branch 산출물과 대조 필요
|
||||
- `default` user 를 실제로 `off` 처리하거나 강한 password 로 잠그는 배포 설정 — 이 문서는 문법만 제공하며 배포 시 `default` user 처리 방침은 별도 결정 사항
|
||||
- Redis 버전별 `%R`/`%W`/selector 기능 가용 여부 (Redis 7.0 이상 조건부 기능) — 실제 배포 Redis 버전 확인 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 문서 자체는 Redis 버전을 페이지 상단에 명시하지 않고, 본문 곳곳에 "Redis 6.2 and later", "Redis 7.0 and later" 조건부 문구로 기능별 최소 버전을 표기하는 구조. 인용 시 버전 조건이 붙은 기능(`%R~`/`%W~`, selector, subcommand allow/block)은 branch 적용 전 배포 Redis 버전 확인 필요.
|
||||
- `reset` 이 이미 `-@all` 을 포함한다는 사실(C5)은 branch 결정 문구의 "`reset -@all`" 표현이 강조적 병기(중복 명시)이지 필수 2단계 절차가 아님을 의미 — `/branch-spec` 단계에서 이 표현을 "reset (내부적으로 -@all 포함) 후 필요한 command·key 만 추가 부여"로 다듬을 것을 권고.
|
||||
- C6 관련 — least-privilege 를 "보안이 아니라 실수 방지"로 단정하는 프레이밍은 이 공식 문서로 뒷받침되지 않으므로, branch-note 본문에 그렇게 쓰지 말 것 (Usage Boundaries 참조).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Redis TLS/mTLS 공식 문서, `%R`/`%W` key permission 세부 공식 문서는 추후 별도 수집 후보)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: official-doc / Redis Cluster Specification — Hash Slots, Hash Tags, MOVED/ASK
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, caching, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Redis Cluster Specification — Hash Slots, Hash Tags, MOVED/ASK
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 문서는 `raw/official-docs/` 카테고리.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`official-doc` — Redis 공식 레퍼런스 사양 문서 (`redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/`).
|
||||
|
||||
## Parent / 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | Redis key model 에서 hash tag `{...}` 로 원자 연산에 필요한 최소 key group 만 co-locate 하고, Cluster 는 database 0 만 사용하므로 DB number 를 격리 수단으로 쓸 수 없다는 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Redis (공식 문서)
|
||||
- 발행일: (문서에 명시된 발행일 없음 — "continuously synchronized with the actual implementation of Redis" 로 계속 갱신되는 living spec)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Redis Cluster 는 key space 를 16384 hash slot 으로 나누고 CRC16(key) mod 16384 로 slot 을 결정하며, multi-key 연산은 모든 key 가 같은 slot 에 있어야 한다. hash tag `{...}` 는 이 제약을 우회해 co-location 을 강제하는 유일한 공식 메커니즘이다. 또한 Cluster 는 database 0 만 지원하고 `SELECT` 를 허용하지 않는다 — 이는 `feature-redis-runtime-role-isolation-contract` 가 "DB number 를 role 격리 수단으로 쓸 수 없고, key prefix + hash tag 로만 격리해야 한다"고 결정한 직접 근거다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Key distribution model] "Each master node in a cluster handles a subset of the 16384 hash slots." (line 146)
|
||||
|
||||
> [§Key distribution model] "HASH_SLOT = CRC16(key) mod 16384" (line 156)
|
||||
|
||||
> [§Hash tags] "Then instead of hashing the key, only what is between the first occurrence of `{` and the following first occurrence of `}` is hashed." (line 195)
|
||||
|
||||
> [§Implemented subset] "all of the keys involved in the operation hash to the same slot." (line 36 — 전체 문장: "Commands performing complex multi-key operations like set unions and intersections are implemented for cases where all of the keys involved in the operation hash to the same slot.")
|
||||
|
||||
> [§Implemented subset] ``"We only support database `0`; the [`SELECT`](/commands/select) command is not allowed."`` (line 44)
|
||||
|
||||
> [§MOVED Redirection] "-MOVED 3999 127.0.0.1:6381" / "The error includes the hash slot of the key (3999) and the endpoint:port of the instance that can serve the query." (line 381, 383)
|
||||
|
||||
> [§ASK redirection] "If ASK redirection is received, send only the query that was redirected to the specified node but continue sending subsequent queries to the old node." (line 549)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-CLUSTER-C1 | Redis Cluster 의 key space 는 16384 hash slot 으로 나뉘고, 각 master 는 그 부분집합을 담당하며, key → slot 매핑은 고정 공식 `HASH_SLOT = CRC16(key) mod 16384` 를 따른다(hash tag 예외 제외) | [§Key distribution model] "Each master node in a cluster handles a subset of the 16384 hash slots." / "HASH_SLOT = CRC16(key) mod 16384" | `official-vendor-doc` | Redis Cluster (OSS/Stack) 모드의 key routing 전반 | slot 수·매핑 공식이 애플리케이션에서 변경 가능하다는 것을 증명하지 않음 — 고정 알고리즘임 |
|
||||
| REDIS-CLUSTER-C2 | key 안에 `{` 와 그 뒤 첫 `}` 사이에 1글자 이상 있으면, hash slot 계산 시 그 substring 만 해싱한다 (hash tag) | [§Hash tags] "Then instead of hashing the key, only what is between the first occurrence of `{` and the following first occurrence of `}` is hashed." | `official-vendor-doc` | 여러 key 를 같은 slot 에 강제 co-locate 시켜야 하는 모든 상황(트랜잭션, MSET, Lua, atomic 연산) | hash tag 내부에 어떤 문자열을 넣어야 하는지(네이밍 컨벤션)는 애플리케이션 책임 — 문서가 규정하지 않음 |
|
||||
| REDIS-CLUSTER-C3 | set 합집합/교집합 같은 복잡한 multi-key 연산은 관련된 모든 key 가 같은 slot 으로 해싱되는 경우에만 구현되어 있다(지원된다) | [§Implemented subset] "all of the keys involved in the operation hash to the same slot." | `official-vendor-doc` | Redis Cluster 에서 multi-key 명령·Lua script·트랜잭션 설계 시 key 그룹핑 제약 | 어떤 명령이 "multi-key" 로 분류되는지 전체 목록까지 증명하지는 않음(본 인용 범위 밖) |
|
||||
| REDIS-CLUSTER-C4 | Redis Cluster 는 standalone 과 달리 다중 database 를 지원하지 않으며 database `0` 만 사용하고 `SELECT` 명령은 허용되지 않는다 | [§Implemented subset] ``"We only support database `0`; the [`SELECT`](/commands/select) command is not allowed."`` | `official-vendor-doc` | Redis **Cluster 모드**에서 DB number 기반 격리 불가 판단 | standalone/non-cluster Redis 의 다중 DB 지원 여부는 이 claim 범위 밖(별개 진술) |
|
||||
| REDIS-CLUSTER-C5 | MOVED 는 slot 이 영구적으로 다른 노드로 재배치됐다는 의미이며 `-MOVED <slot> <endpoint:port>` 형태로 응답하고, 클라이언트는 해당 endpoint 로 재요청해야 한다 | [§MOVED Redirection] "-MOVED 3999 127.0.0.1:6381" / "The error includes the hash slot of the key (3999) and the endpoint:port of the instance that can serve the query." | `official-vendor-doc` | Redis Cluster 클라이언트의 redirection 처리 로직 설계 | 구체적 클라이언트 라이브러리(Lettuce/Jedis 등)의 실제 구현 세부는 증명하지 않음 |
|
||||
| REDIS-CLUSTER-C6 | ASK 는 MOVED 와 달리 "다음 한 번의 query 만" 지정된 노드로 보내고 이후 query 는 계속 이전 노드로 보내는 **일회성** 리다이렉션이다(슬롯 마이그레이션 중 사용) | [§ASK redirection] "If ASK redirection is received, send only the query that was redirected to the specified node but continue sending subsequent queries to the old node." | `official-vendor-doc` | resharding(슬롯 마이그레이션) 중 발생하는 일시적 리다이렉션 처리 설계 | 애플리케이션 레벨 재시도 정책 전체를 규정하지 않음 — ASK/ASKING 처리 의미만 규정 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-CLUSTER-C1`: hash slot 총량(16384)과 고정 매핑 공식(CRC16 mod 16384)
|
||||
- `REDIS-CLUSTER-C2`: hash tag `{...}` 문법의 정확한 파싱 규칙
|
||||
- `REDIS-CLUSTER-C3`: multi-key 연산이 "같은 slot" 제약 하에서만 지원된다는 사실
|
||||
- `REDIS-CLUSTER-C4`: Cluster 모드가 database 0 만 지원하고 `SELECT` 를 금지한다는 사실
|
||||
- `REDIS-CLUSTER-C5`, `REDIS-CLUSTER-C6`: MOVED(영구) vs ASK(일회성) 리다이렉션의 의미 차이
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- key prefix + hash tag 조합을 role(cache/coordination/session) 격리 전략으로 채택하는 것이 "모범 사례"라는 것 — 이는 프로젝트 내부 설계 결정(branch-local decision)이며, 이 공식 문서는 "DB number 로는 격리 불가"라는 제약만 증명함
|
||||
- 특정 클라이언트 라이브러리(Lettuce, Jedis, redis-py 등)가 MOVED/ASK 를 어떻게 캐싱·재시도하는지의 세부 구현
|
||||
- Redis Stack의 모든 모듈(RedisJSON, RediSearch 등)이 Cluster 모드에서 동일하게 동작하는지 여부
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 실제로 사용할 Redis Cluster 설정(단일 노드 vs 실제 cluster)에서 hash tag 적용 대상 key 목록 확정 필요
|
||||
- Spring Data Redis / Lettuce 클라이언트가 MOVED/ASK 를 투명하게 처리하는지 별도 공식 문서로 검증 필요 (`raw/official-docs/` 에 별도 등록 대상)
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): key prefix(namespace) 는 co-location 을 보장하지 않으므로, role 별 격리는 prefix + hash tag 조합이 필요할 것으로 보임 — 이는 branch-local 결정이며 이 문서만으로 증명되지 않음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Redis 공식 `CLUSTER SETSLOT`/`CLUSTER SLOTS`/`MIGRATE` 명령 레퍼런스 페이지(본 spec 문서가 링크만 걸고 상세 옵션은 별도 페이지에 위임함), maxmemory-policy/eviction 공식 문서(본 branch 범위의 "배포 설정 소유" 항목과 관련).
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog: (아직 없음 — Spring Data Redis / Lettuce 클러스터 클라이언트 문서 등록 후 링크 예정)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: "official-doc / Redis — Scripting with Lua (atomicity, script cache/EVALSHA/NOSCRIPT, KEYS/ARGV, SCRIPT KILL)"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/develop/programmability/eval-intro/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Redis — Scripting with Lua (atomicity, script cache/EVALSHA/NOSCRIPT, KEYS/ARGV, SCRIPT KILL)
|
||||
|
||||
> Layer: `raw/official-docs/` — Redis 공식 reference ("Scripting with Lua" / eval-intro) 원문 발췌.
|
||||
> ca-skeleton `feature-redis-atomic-program-catalog-contract` 의 "다단계 read-decide-write 는 EVALSHA 기반 버전 있는 atomic program 으로만 제공" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | 다단계 read-decide-write 는 EVALSHA 로 실행하는 버전 있는 atomic program 으로만 제공하고, NOSCRIPT 시 안전 재적재하며, bounded execution(유한 실행) 규칙을 둔다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/develop/programmability/eval-intro/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Redis (Redis Ltd. 공식 문서)
|
||||
- 발행일: Redis 공식 reference, 최신 버전 anchor (7.0 관련 변경 다수 명시 — verbatim 인용 참조)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Redis Lua 스크립트가 **원자적으로 실행**되고(다른 클라이언트 명령과 인터리빙되지 않음), 스크립트 캐시(SHA1)가 **휘발성**이라 `NOSCRIPT` 발생 시 재적재가 필요하며, 접근하는 key 는 반드시 `KEYS` 인자로 선언해야 한다는 공식 근거를 보존한다. `feature-redis-atomic-program-catalog-contract` 의 EVALSHA + 안전 재적재 + KEYS 선언 규칙 결정을 직접 뒷받침한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§(intro, "Getting started" 이전)] "Redis guarantees the script's atomic execution. While executing the script, all server activities are blocked during its entire runtime. These semantics mean that all of the script's effects either have yet to happen or had already happened."
|
||||
> (한글 요약: Redis 는 스크립트의 원자적 실행을 보장한다. 스크립트 실행 중에는 모든 서버 활동이 전체 실행 시간 동안 블로킹된다. 즉 스크립트의 효과는 "아직 발생 안 함" 아니면 "이미 발생함" 둘 중 하나다.)
|
||||
|
||||
> [§Getting started] "to ensure the correct execution of scripts, both in standalone and clustered deployments, all names of keys that a script accesses must be explicitly provided as input key arguments. The script should only access keys whose names are given as input arguments. Scripts should never access keys with programmatically-generated names or based on the contents of data structures stored in the database."
|
||||
> (한글 요약: standalone/clustered 배포 모두에서 스크립트의 정확한 실행을 보장하려면, 스크립트가 접근하는 모든 key 이름은 반드시 입력 key 인자로 명시적으로 제공해야 한다. 스크립트는 입력 인자로 주어진 이름의 key 만 접근해야 하며, 프로그램적으로 생성된 이름이나 DB 저장 데이터 구조의 내용에 기반한 key 는 절대 접근해서는 안 된다.)
|
||||
|
||||
> [§Script cache → Cache volatility] "The Redis script cache is always volatile. It isn't considered as a part of the database and is not persisted. The cache may be cleared when the server restarts, during fail-over when a replica assumes the master role, or explicitly by SCRIPT FLUSH."
|
||||
> (한글 요약: Redis 스크립트 캐시는 항상 휘발성이다. DB 의 일부로 취급되지 않으며 영속화되지 않는다. 서버 재시작, failover 시 replica 의 master 승격, 또는 명시적 SCRIPT FLUSH 호출 시 캐시가 지워질 수 있다.)
|
||||
|
||||
> [§Script cache → Cache volatility] "Applications that use scripts should always call EVALSHA to execute them. The server returns an error if the script's SHA1 digest is not in the cache." ... "(error) NOSCRIPT No matching script" ... "In this case, the application should first load it with SCRIPT LOAD and then call EVALSHA once more to run the cached script by its SHA1 sum."
|
||||
> (한글 요약: 스크립트를 사용하는 애플리케이션은 항상 EVALSHA 로 실행해야 한다. SHA1 다이제스트가 캐시에 없으면 서버는 에러를 반환한다(`NOSCRIPT No matching script`). 이 경우 애플리케이션은 먼저 SCRIPT LOAD 로 적재한 뒤 SHA1 로 EVALSHA 를 다시 호출해야 한다.)
|
||||
|
||||
> [§Script cache → EVALSHA in the context of pipelining] "The commands in a pipelined request run in the order they are sent, but other clients' commands may be interleaved for execution between these. Because of that, the NOSCRIPT error can return from a pipelined request but can't be handled. Therefore, a client library's implementation should revert to using plain EVAL of parameterized in the context of a pipeline."
|
||||
> (한글 요약: 파이프라인 요청 내 명령은 보낸 순서대로 실행되지만 다른 클라이언트의 명령이 그 사이에 인터리빙될 수 있다. 이 때문에 파이프라인에서 발생한 NOSCRIPT 에러는 처리(handle)될 수 없다. 따라서 클라이언트 라이브러리 구현은 파이프라인 맥락에서는 파라미터화된 일반 EVAL 로 되돌아가야 한다.)
|
||||
|
||||
> [§The SCRIPT command] "SCRIPT KILL: this command is the only way to interrupt a long-running script (a.k.a slow script), short of shutting down the server. A script is deemed as slow once its execution's duration exceeds the configured maximum execution time threshold. The SCRIPT KILL command can be used only with scripts that did not modify the dataset during their execution (since stopping a read-only script does not violate the scripting engine's guaranteed atomicity)."
|
||||
> (한글 요약: SCRIPT KILL 은 서버를 종료하지 않고 장시간 실행 중인(slow) 스크립트를 중단시키는 유일한 방법이다. 스크립트는 설정된 최대 실행 시간 임계값을 초과하면 slow 로 간주된다. SCRIPT KILL 은 실행 중 dataset 을 수정하지 않은 스크립트에만 사용 가능하다 — read-only 스크립트를 중단하는 것은 스크립팅 엔진이 보장하는 원자성을 위반하지 않기 때문이다.)
|
||||
|
||||
> [§Script replication] "Effects replication: only the script's data-modifying commands are replicated. Replicas then run the commands without executing any scripts. While potentially lengthier in terms of network traffic, this replication mode is deterministic by definition and therefore doesn't require special consideration." ... "In Redis 5.0, effects replication became the default mode. As of Redis 7.0, verbatim replication is no longer supported."
|
||||
> (한글 요약: Effects replication 은 스크립트의 데이터 변경 명령만 복제한다. Replica 는 스크립트를 실행하지 않고 그 명령만 실행한다. 네트워크 트래픽 면에서 더 길어질 수 있으나, 이 복제 모드는 정의상 결정론적이라 별도 조치가 필요 없다. Redis 5.0 부터 effects replication 이 기본값이 되었고, Redis 7.0 부터는 verbatim replication 이 더 이상 지원되지 않는다.)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-EVAL-C1 | Redis 는 스크립트의 원자적 실행을 보장하며, 실행 중 모든 서버 활동이 블로킹되어 스크립트 효과는 "아직 발생 안 함" 또는 "이미 발생함" 둘 중 하나만 관측된다 | "Redis guarantees the script's atomic execution. While executing the script, all server activities are blocked during its entire runtime." | `official-vendor-doc` | 모든 EVAL/EVALSHA 스크립트 실행 (standalone) | 이 페이지는 스크립트 원자성을 MULTI/EXEC 트랜잭션과 명시적으로 대조·비교하는 서술을 포함하지 않는다 — 두 메커니즘의 이동성/차이는 본 인용 범위 밖 |
|
||||
| REDIS-EVAL-C2 | 스크립트 캐시는 항상 휘발성이며 DB 의 일부로 영속화되지 않는다 — 서버 재시작, failover, 명시적 SCRIPT FLUSH 시 지워질 수 있다 | "The Redis script cache is always volatile. ... The cache may be cleared when the server restarts, during fail-over when a replica assumes the master role, or explicitly by SCRIPT FLUSH." | `official-vendor-doc` | 모든 Redis 배포(standalone/replica/cluster)의 스크립트 캐시 | 클러스터 각 노드별 캐시 상태가 서로 독립적으로 유실될 수 있는지의 세부 동작까지는 명시하지 않음 |
|
||||
| REDIS-EVAL-C3 | 애플리케이션은 항상 EVALSHA 로 실행해야 하며, SHA1 다이제스트가 캐시에 없으면 서버는 `NOSCRIPT` 에러를 반환한다. 이 경우 클라이언트는 SCRIPT LOAD 로 재적재 후 EVALSHA 를 재시도해야 한다 | "Applications that use scripts should always call EVALSHA to execute them. The server returns an error if the script's SHA1 digest is not in the cache." / "(error) NOSCRIPT No matching script" / "the application should first load it with SCRIPT LOAD and then call EVALSHA once more" | `official-vendor-doc` | EVALSHA 실행 경로 + NOSCRIPT 복구 절차 일반 | 구체적인 client 라이브러리(예: Lettuce/Jedis)가 이 재적재를 자동화하는지는 "대부분의 클라이언트가 유틸리티 API 를 제공한다"고만 언급 — 특정 라이브러리 API 는 본 인용 범위 밖 |
|
||||
| REDIS-EVAL-C4 | 파이프라인 컨텍스트에서는 다른 클라이언트 명령이 인터리빙될 수 있어 `NOSCRIPT` 에러가 발생해도 처리(handle)할 수 없으므로, 클라이언트 라이브러리는 파이프라인 안에서는 파라미터화된 일반 EVAL 로 되돌아가야(fallback) 한다 | "the NOSCRIPT error can return from a pipelined request but can't be handled. Therefore, a client library's implementation should revert to using plain EVAL of parameterized in the context of a pipeline." | `official-vendor-doc` | pipelined EVALSHA 실행 시나리오 | 이것이 client 라이브러리의 필수 구현 요건인지 권고인지의 구속력(should = 권고) 강도까지만 확인됨 — RFC 수준 MUST 아님 |
|
||||
| REDIS-EVAL-C5 | standalone/clustered 배포 모두에서 정확한 스크립트 실행을 보장하려면, 스크립트가 접근하는 모든 key 이름은 `KEYS` 입력 인자로 명시적으로 제공되어야 하며, 프로그램적으로 생성되거나 DB 데이터 구조 내용에 기반한 key 는 절대 접근해서는 안 된다 | "all names of keys that a script accesses must be explicitly provided as input key arguments. The script should only access keys whose names are given as input arguments. Scripts should never access keys with programmatically-generated names or based on the contents of data structures stored in the database." | `official-vendor-doc` | 모든 EVAL/EVALSHA 스크립트의 key 접근 설계 (cluster 포함) | 미선언 key 를 실제로 접근했을 때의 구체적 에러 코드/거부 동작, 그리고 cluster hash-slot 라우팅 알고리즘 자체는 이 페이지에 명시되지 않음 |
|
||||
| REDIS-EVAL-C6 | `SCRIPT KILL` 은 서버 종료 없이 장시간 실행 스크립트를 중단하는 유일한 방법이며, 설정된 최대 실행 시간 임계값 초과 시 slow 로 간주되고, dataset 을 수정하지 않은 스크립트에만 사용 가능하다(원자성 보장 위반 방지) | "SCRIPT KILL: this command is the only way to interrupt a long-running script ... A script is deemed as slow once its execution's duration exceeds the configured maximum execution time threshold. The SCRIPT KILL command can be used only with scripts that did not modify the dataset during their execution ..." | `official-vendor-doc` | 장시간 실행(slow) 스크립트의 중단 절차 일반 | `busy-reply-threshold`(구 `lua-time-limit`) 설정 이름, `BUSY` 에러 문자열, 쓰기를 수행한 스크립트에 대한 `SHUTDOWN NOSAVE` 강제 종료 조건은 **본 페이지에서 확인되지 않음** — "maximum execution time" 은 별도 개요 페이지(`develop/programmability/#maximum-execution-time`)로만 링크됨, 별도 fetch 필요 |
|
||||
| REDIS-EVAL-C7 | Effects replication(Redis 5.0 부터 기본값, Redis 7.0 부터 유일 모드)은 스크립트의 데이터 변경 명령만 복제하고 replica 는 스크립트를 재실행하지 않으며, 이 방식은 정의상 결정론적이라 별도 조치가 불필요하다 | "Effects replication: only the script's data-modifying commands are replicated. Replicas then run the commands without executing any scripts. ... this replication mode is deterministic by definition ..." / "In Redis 5.0, effects replication became the default mode. As of Redis 7.0, verbatim replication is no longer supported." | `official-vendor-doc` | Redis 5.0+ (특히 7.0+) 의 스크립트 복제 모드 | Redis 7.0 미만 버전에서 verbatim replication 을 여전히 사용하는 배포의 결정론 제약(비결정적 명령 제한)은 별도 섹션("Scripts with deterministic writes")에 있으며, ca-skeleton 이 대상으로 하는 Redis 버전이 7.0+ 인지 여부는 본 자료가 증명하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `REDIS-EVAL-C1`: 스크립트 실행 중 서버 전체가 블로킹되어 원자적으로 실행됨
|
||||
- `REDIS-EVAL-C2`, `C3`, `C4`: 스크립트 캐시의 휘발성 + EVALSHA/NOSCRIPT 복구 절차 (단일 요청 및 파이프라인 맥락 모두)
|
||||
- `REDIS-EVAL-C5`: 스크립트가 접근하는 key 는 반드시 `KEYS` 인자로 선언해야 하며 미선언/동적 생성 key 접근 금지
|
||||
- `REDIS-EVAL-C6`: `SCRIPT KILL` 은 dataset 미수정 + 실행시간 초과 스크립트에만 적용되는 중단 메커니즘
|
||||
- `REDIS-EVAL-C7`: Effects replication 이 Redis 5.0+ 기본, 7.0+ 유일 모드이며 결정론적임
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `busy-reply-threshold`(구 `lua-time-limit`) 설정 항목, `BUSY` 에러 문자열, 쓰기 스크립트에 대한 `SHUTDOWN NOSAVE` 필요조건 — 이 페이지에는 등장하지 않음(다른 programmability 개요 페이지로만 링크)
|
||||
- 스크립트 원자성과 `MULTI`/`EXEC` 트랜잭션의 명시적 비교·차이 서술
|
||||
- Redis Cluster 의 실제 hash-slot 라우팅/정적 분석 알고리즘 detail
|
||||
- Redis Functions(7.0+ 대안 프로그래밍 모델)의 세부 규칙 — 존재만 언급되고 상세는 별도 문서로 링크됨
|
||||
- **내 프로젝트(ca-skeleton)에 적용하려면 추가 확인이 필요한 것**:
|
||||
- "bounded execution(유한 실행)" 규칙의 구체적 서버 설정값(`busy-reply-threshold` 기본값/권장값, `BUSY` 에러 처리 흐름) — `https://redis.io/docs/latest/develop/programmability/#maximum-execution-time` 별도 fetch 필요
|
||||
- unsafe multi-command recipe ↔ atomic 대체 쌍의 실제 Lua 스크립트 구현 detail은 이 자료 범위 밖 — `feature-redis-atomic-program-catalog-contract` 자체 설계 결정
|
||||
- Redisson/Jedis/Lettuce 등 실제 사용 클라이언트 라이브러리가 EVALSHA→NOSCRIPT→SCRIPT LOAD 재시도를 자동화하는지 여부는 각 라이브러리 문서에서 별도 확인 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- `busy-reply-threshold` / `BUSY` / `SHUTDOWN NOSAVE` 관련 인용은 이 페이지(`eval-intro`)에 없음을 self-grep 으로 확인(부재 확인, fabrication 아님) — bounded execution 완전한 근거를 위해 `develop/programmability/` 개요 페이지 추가 수집 필요.
|
||||
- 인용 6 (`REDIS-EVAL-C7`)의 "결정론/복제" 서술은 branch 결정의 직접 근거는 아니지만, atomic program 이 실행 후 replica 에 어떻게 전파되는지에 대한 배경 지식으로 유용.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — `develop/programmability/` 개요, `develop/programmability/lua-api` 후속 수집 후보)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
title: "official-doc / Redis Functions — Library Deployment (FUNCTION LOAD, Shebang, no-writes Flag, EVAL 비교)"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/develop/programmability/functions-intro/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, caching, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Redis Functions — Library Deployment (FUNCTION LOAD, Shebang, no-writes Flag, EVAL 비교)
|
||||
|
||||
> Layer: `raw/official-docs/` — Redis 공식 문서 ("Redis functions" / functions-intro) 원문 발췌.
|
||||
> ca-skeleton `feature-redis-atomic-program-catalog-contract` 의 "atomic program 기본 배포 모드는 EVAL/EVALSHA script, Redis Functions 는 명시적 opt-in 조건을 만족할 때만" 결정의 근거.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | atomic program 의 기본 배포 모드는 EVAL/EVALSHA script 이고, Redis Functions 는 명시적 opt-in 조건(Redis 7.0+ 버전 게이트, cluster 전체 노드 수동 배포 부담, `no-writes` 류 flag 명시적 선언)을 만족할 때만 채택한다는 결정의 공식 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/develop/programmability/functions-intro/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Redis (Redis Ltd. 공식 문서)
|
||||
- 발행일: 문서에 명시 없음
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
atomic program 카탈로그 branch 에서 EVAL/EVALSHA(기본) 배포 모드와 Redis Functions(opt-in) 배포 모드를 가르는 조건을 정할 때, 공식 문서가 말하는 두 모드의 실질적 차이(스크립트 캐시 vs persistence/replication, 버전 요구사항, library 배포·조회 명령, `no-writes` flag 의미, cluster 배포 오버헤드)를 그대로 근거로 삼기 위해 저장.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Redis functions (도입부)] "This feature, which became available in Redis 7, supersedes the use of [EVAL](https://redis.io/docs/latest/develop/programmability/eval-intro) in prior versions of Redis."
|
||||
|
||||
> [§Prologue] "To address these needs while avoiding breaking changes to already-established and well-liked ephemeral scripts, Redis v7.0 introduces Redis Functions."
|
||||
|
||||
> [§Prologue] "By design, Redis only caches the loaded scripts."
|
||||
|
||||
> [§Prologue] "That means that the script cache can become lost at any time, such as after calling [`SCRIPT FLUSH`](https://redis.io/docs/latest/commands/script-flush), after restarting the server, or when failing over to a replica."
|
||||
|
||||
> [§Prologue] "The application is responsible for reloading scripts during runtime if any are missing."
|
||||
|
||||
> [§What are Redis Functions?] "Functions are also persisted to the AOF file and replicated from master to replicas, so they are as durable as the data itself."
|
||||
|
||||
> [§What are Redis Functions?] "Like all other operations in Redis, the execution of a function is atomic."
|
||||
|
||||
> [§What are Redis Functions?] "Because running a function blocks the Redis server, functions are meant to finish executing quickly, so you should avoid using long-running functions."
|
||||
|
||||
> [§Loading libraries and functions] "the library payload must start with Shebang statement that provides a metadata about the library (like the engine to use and the library name)."
|
||||
|
||||
> [§Loading libraries and functions] "#!<engine name> name=<library name>"
|
||||
|
||||
> [§Loading libraries and functions] "Notice that the [`FUNCTION LOAD`](https://redis.io/docs/latest/commands/function-load) command returns the name of the loaded library, this name can later be used [`FUNCTION LIST`](https://redis.io/docs/latest/commands/function-list) and [`FUNCTION DELETE`](https://redis.io/docs/latest/commands/function-delete)."
|
||||
|
||||
> [§Functions in cluster] "As noted above, Redis automatically handles propagation of loaded functions to replicas."
|
||||
|
||||
> [§Functions in cluster] "In a Redis Cluster, it is also necessary to load functions to all cluster nodes. This is not handled automatically by Redis Cluster, and needs to be handled by the cluster administrator (like module loading, configuration setting, etc.)."
|
||||
|
||||
> [§Function flags] "By default, Redis assumes that all functions may perform arbitrary read or write operations. Function Flags make it possible to declare more specific function behavior at the time of registration."
|
||||
|
||||
> [§Function flags] "In these cases, you can add the `no-writes` flag to the function's registration, disable the safeguard and allow them to run."
|
||||
|
||||
> [§Function flags] "For the complete documentation flags, please refer to [Script flags](https://redis.io/docs/latest/develop/programmability/lua-api#script_flags)."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-FN-C1 | Redis Functions 는 Redis 7(v7.0)에서 도입되었고, 이전 버전의 EVAL 사용을 대체(supersede)한다 | [§Redis functions] "became available in Redis 7, supersedes the use of EVAL ... in prior versions" / [§Prologue] "Redis v7.0 introduces Redis Functions" | `official-vendor-doc` | Functions 채택의 최소 버전 게이트(7.0+) 판단 | 6.x 이하에서 FUNCTION LOAD 호출 시 정확한 실패 모드(에러 메시지)는 미서술. ca-skeleton 실제 배포 Redis 버전이 7.0 이상인지는 이 자료로 확인 불가 — 별도 확인 필요 |
|
||||
| REDIS-FN-C2 | EVAL/EVALSHA 스크립트는 서버가 "캐시"로만 취급해 SCRIPT FLUSH·재시작·failover 시 소실될 수 있고 재적재는 애플리케이션 책임인 반면, Functions 는 데이터베이스의 first-class 아티팩트로 AOF persistence + replication 을 통해 데이터와 동일하게 지속(durable)된다 | "By design, Redis only caches the loaded scripts." / "script cache can become lost at any time... application is responsible for reloading" / "Functions are also persisted to the AOF file and replicated..." | `official-vendor-doc` | EVAL 배포 시 NOSCRIPT 재적재가 왜 애플리케이션 책임인지의 공식 근거; Functions 채택 시 이 부담이 서버 측으로 이전됨을 뒷받침 | AOF 가 비활성화된 배포(`appendonly no`)에서도 이 durability 가 보장되는지는 조건부 서술 없음 — 별도 확인 필요 |
|
||||
| REDIS-FN-C3 | Function library 는 `#!<engine name> name=<library name>` 형식 Shebang 선언으로 시작해야 하며 최소 1개 이상의 등록된 함수가 필요하다. `FUNCTION LOAD` 는 라이브러리를 적재하고 이름을 반환하며, 이후 `FUNCTION LIST`/`FUNCTION DELETE` 로 조회·삭제한다 | "library payload must start with Shebang statement..." / "#!<engine name> name=<library name>" / "FUNCTION LOAD command returns the name... used FUNCTION LIST and FUNCTION DELETE" | `official-vendor-doc` | library 이름·엔진 선언 규칙, 배포·조회 명령 카탈로그 구현 가이드 근거 | `FUNCTION DUMP` / `FUNCTION RESTORE` 명령은 이 문서(functions-intro)에 전혀 등장하지 않는다 — 별도 command reference 확인 없이 이 자료만으로 DUMP/RESTORE 워크플로우를 정당화할 수 없음 |
|
||||
| REDIS-FN-C4 | 함수 실행은 원자적이며 실행되는 동안 서버의 모든 활동을 블로킹한다(트랜잭션과 유사한 시맨틱). 따라서 함수는 빠르게 끝나야 하고 long-running 함수는 피해야 한다 | "the execution of a function is atomic." / "Because running a function blocks the Redis server, functions are meant to finish executing quickly, so you should avoid using long-running functions." | `official-vendor-doc` | bounded-execution(유한 실행, 무한 루프·대량 스캔 금지) 요구의 공식 근거 — EVAL 과 동일한 블로킹 제약을 Functions 도 그대로 가진다는 근거 | "빠르게"의 구체적 시간 임계값(ms 단위)은 문서에 없음 — 별도 설정(예: 실행 시간 제한 설정) 문서 확인 필요 |
|
||||
| REDIS-FN-C5 | 기본적으로 모든 함수는 read/write 모두 가능하다고 간주되어 read-only replica 에서 실행이 차단된다. 등록 시 `no-writes` flag 를 명시해야 이 안전장치가 해제되고 `FCALL_RO` 로 read-only replica 에서 실행 가능해진다. 이 문서는 `no-writes` 외 flag 의 완전한 목록은 별도 "Script flags" 문서를 참조하라고 안내한다 | "Redis assumes that all functions may perform arbitrary read or write operations..." / "you can add the `no-writes` flag..." / "For the complete documentation flags, please refer to Script flags." | `official-vendor-doc` | `no-writes` flag 의 의미와 등록 API 모양(named-args 변형) | `allow-oom` / `no-cluster` / `allow-stale` 등 다른 flag 의 의미는 이 문서에 등장하지 않는다 — 별도 "Lua API — Script flags" 페이지를 raw 로 등록 후에만 인용 가능. 이 claim 만으로 다른 flag 를 정의할 수 없음 |
|
||||
| REDIS-FN-C6 | Redis Cluster 환경에서 함수는 replica 로는 자동 전파되지만, cluster 내 모든(마스터) 노드에 함수를 적재하는 것은 자동화되지 않으며 cluster 관리자가 별도로(module loading·설정 변경과 마찬가지로) 처리해야 한다 | "Redis automatically handles propagation of loaded functions to replicas." / "it is also necessary to load functions to all cluster nodes. This is not handled automatically by Redis Cluster, and needs to be handled by the cluster administrator" | `official-vendor-doc` | cluster 배포 시 Functions 채택에 따르는 운영 오버헤드 — opt-in 조건 판단 근거 중 하나 | EVAL/EVALSHA 스크립트가 cluster 각 노드에 배포될 때 상대적으로 더 쉬운지/어려운지는 이 문서에 비교 서술이 없음 — "Functions 가 EVAL 보다 cluster 배포가 더 번거롭다"는 상대적 결론은 이 자료만으로 내릴 수 없음 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 적용 가능한 범위:
|
||||
- Redis Functions 와 EVAL/EVALSHA 의 지속성(persistence)·복제(replication) 차이를 branch 결정의 공식 근거로 사용
|
||||
- Redis 7.0+ 버전 요구사항을 Functions opt-in 조건의 최소 버전 게이트로 사용
|
||||
- library Shebang 선언 규칙(`#!<engine> name=<library>`), `FUNCTION LOAD`/`FUNCTION LIST`/`FUNCTION DELETE` 명령 의미를 구현 가이드에 직접 인용
|
||||
- `no-writes` flag 의 정확한 의미와 등록 API 모양(named-args 변형)을 그대로 인용
|
||||
- cluster 배포 시 수동 전파 필요성을 opt-in 조건(cluster 운영 오버헤드) 근거로 사용
|
||||
- 적용 금지 (본 자료만으로 정당화 불가):
|
||||
- `FUNCTION DUMP` / `FUNCTION RESTORE` 명령의 동작 — 이 문서에 없음. 별도 command reference 확인 후 별도 인용 필요
|
||||
- `allow-oom` / `no-cluster` / `allow-stale` 등 `no-writes` 외 flag 의 의미 — 별도 "Lua API — Script flags" 문서 확인 필요
|
||||
- "Functions 가 EVAL 보다 항상 낫다"는 식의 일반화된 평가 — 문서는 EVAL 의 한계와 Functions 의 지속성 이점을 서술하지만, Functions 도 "실행 중 서버 전체를 블로킹"하며 "빠르게 끝나야 한다"는 EVAL 과 동일한 제약을 그대로 가진다는 점도 함께 서술한다("항상 우월"이라는 표현은 원문에 없음)
|
||||
- ca-skeleton 의 실제 Redis 배포 버전이 7.0 이상인지 여부 — 이 자료는 일반 버전 요구사항만 서술하며, 프로젝트 실제 환경 검증은 별도 확인 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- 이 문서(functions-intro)는 flag 전체 목록을 다루지 않는다 — `allow-oom`/`no-cluster`/`allow-stale` 확인이 필요해지면 Lua API "Script flags" 페이지를 별도 `raw/official-docs/`로 등록해야 한다.
|
||||
- `FUNCTION DUMP`/`FUNCTION RESTORE` 확인이 필요해지면 별도 command reference 페이지(`redis.io/docs/latest/commands/function-dump`, `function-restore`)를 별도 raw 문서로 등록해야 한다.
|
||||
- 같은 branch 근거로 이미 등록된 `[[raw/official-docs/redis-eval-scripting-atomicity]]` (eval-intro) 와 짝을 이뤄, "EVAL 기본 vs Functions opt-in" 비교의 양쪽 절반을 구성한다.
|
||||
|
||||
## Related
|
||||
|
||||
- [[raw/official-docs/redis-eval-scripting-atomicity]] — 같은 branch 근거, EVAL/Lua 스크립팅 원자성·NOSCRIPT 재적재 쪽 공식 문서 (opt-in 비교의 EVAL 쪽 절반)
|
||||
- 같은 주제 다른 official-doc 후보 (미등록): Redis "Lua API — Script flags" 페이지 (`no-writes` 외 전체 flag 목록), Redis "FUNCTION DUMP"/"FUNCTION RESTORE" command reference
|
||||
- 이 자료를 인용한 wiki 요약: (아직 생성 안 됨)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: "official-doc / Redis INCR — Atomicity & Rate Limiter Pattern Race Condition"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/commands/incr/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, caching, redis, rate-limit]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
status: raw
|
||||
confidence: high
|
||||
vendor: Redis
|
||||
author: Redis (official docs)
|
||||
published:
|
||||
---
|
||||
|
||||
# Redis INCR — Atomicity & Rate Limiter Pattern Race Condition
|
||||
|
||||
> Layer: `raw/official-docs/` — Redis 공식 커맨드 레퍼런스(`INCR`) 원문 발췌·출처 기록.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | 각 atomic program helper 는 흔한 unsafe multi-command recipe 와 그 안전한 atomic 대체를 쌍으로 문서화한다 — 대표 사례 `GET → 판단 → INCR → EXPIRE` 의 race condition 을 공식 문서가 명시적으로 서술한다는 근거 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/commands/incr/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Redis (공식 커맨드 레퍼런스)
|
||||
- 발행일: (페이지에 명시 없음 — `since: 1.0.0` 커맨드 메타데이터만 존재)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-redis-atomic-program-catalog-contract` branch 는 "다단계 read-decide-write recipe 는 항상 unsafe 버전과 atomic 대체를 쌍으로 문서화한다"는 프로젝트 결정(`DEC-CA-SKELETON-OPERATIONAL-CONTRACT-REDIS-PROGRAM-001`)을 구현해야 한다. Redis 공식 `INCR` 문서의 "Pattern: rate limiter" 절이 바로 이 다단계 recipe (`GET` → 판단 → `INCR` → 조건부 `EXPIRE`)의 race condition 을 공식적으로 서술하고, `MULTI`/`EXEC` 및 Lua(`EVAL`) 대체를 명시적으로 권고하므로 1차 근거로 확보한다.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Details > Pattern: counter] "The counter pattern is the most obvious thing you can do with Redis atomic increment operations."
|
||||
> (lines 508–509 in fetched text)
|
||||
|
||||
> [§Details > Pattern: rate limiter > Pattern: rate limiter 1] "Note the used of `MULTI` and `EXEC` in order to make sure that we'll both increment and set the expire at every API call."
|
||||
> (lines 565–566 in fetched text)
|
||||
|
||||
> [§Details > Pattern: rate limiter > Pattern: rate limiter 2] "**In the above code there is a race condition**."
|
||||
> (line 593 in fetched text)
|
||||
|
||||
> [§Details > Pattern: rate limiter > Pattern: rate limiter 2] "If for some reason the client performs the `INCR` command but does not perform the `EXPIRE` the key will be leaked until we'll see the same IP address again."
|
||||
> (lines 594–595 in fetched text)
|
||||
|
||||
> [§Details > Pattern: rate limiter > Pattern: rate limiter 2] "This can be easily fixed by turning the `INCR` with optional `EXPIRE` into a Lua script that is then sent using the `EVAL` command (only available since Redis version 2.6)."
|
||||
> (lines 597–599 in fetched text)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-INCR-C1 | `INCR` 은 "atomic increment operation" 으로 서술된다 — 단일 `INCR` 호출 자체는 원자적이다 | [§Pattern: counter] "The counter pattern is the most obvious thing you can do with Redis atomic increment operations." | `official-vendor-doc` | 단일 `INCR` 커맨드 호출 자체의 원자성 (per-command atomicity) | 여러 명령(`GET`→판단→`INCR`→`EXPIRE`)을 조합한 시퀀스 *전체* 가 원자적이라는 것 — 오히려 본 문서가 뒤에서 이를 반박(REDIS-INCR-C2) |
|
||||
| REDIS-INCR-C2 | "GET 으로 현재값 확인 → 조건부 `INCR` → 첫 증가 시에만 `EXPIRE`" 형태의 rate limiter 구현("Pattern: rate limiter 2")에 대해 공식 문서가 명시적으로 race condition 존재를 선언한다 | [§Pattern: rate limiter 2] "**In the above code there is a race condition**." | `official-vendor-doc` | `GET`/`INCR`/조건부 `EXPIRE` 다단계 recipe 에 race 가 실재한다는 사실 자체 | 다른 언어·클라이언트 구현에서도 항상 동일 race 가 발생한다는 일반화 — 문서는 이 특정 pseudocode 예시에 대해서만 명시함 |
|
||||
| REDIS-INCR-C3 | 위 race 의 구체적 실패 모드는 "클라이언트가 `INCR` 은 수행했지만 `EXPIRE` 를 수행하지 못한 경우" 이며, 이 경우 같은 IP 를 다시 볼 때까지 key 가 TTL 없이 leak 된다 | [§Pattern: rate limiter 2] "If for some reason the client performs the `INCR` command but does not perform the `EXPIRE` the key will be leaked until we'll see the same IP address again." | `official-vendor-doc` | `INCR` 성공 후 `EXPIRE` 미실행(크래시·네트워크 단절 등) 시 key 누수 시나리오 | 이 leak 이 rate-limit *판정 정확성* 자체를 깨뜨린다는 것 — 문서는 메모리/키 누수로 서술하지, 카운트 오판으로 서술하지 않음 |
|
||||
| REDIS-INCR-C4 | 공식 문서는 두 가지 다른 recipe 에 두 가지 다른 대체를 권고한다 — (a) 고정 타임스탬프 키 카운터("rate limiter 1")에는 `INCR`+`EXPIRE` 를 `MULTI`/`EXEC` 로 묶을 것을, (b) 조건부 분기가 있는 단일 카운터("rate limiter 2")에는 `INCR`+조건부 `EXPIRE` 를 Lua 스크립트로 옮겨 `EVAL` 로 실행할 것을 권고한다 | [§rate limiter 1] "Note the used of `MULTI` and `EXEC` in order to make sure that we'll both increment and set the expire at every API call." + [§rate limiter 2] "This can be easily fixed by turning the `INCR` with optional `EXPIRE` into a Lua script that is then sent using the `EVAL` command (only available since Redis version 2.6)." | `official-vendor-doc` | 두 recipe 각각에 맞는 공식 대체 메커니즘 선택 근거 | `MULTI`/`EXEC` 가 "rate limiter 2" 처럼 읽은 값을 기준으로 분기(`IF value == 1 THEN EXPIRE`)하는 recipe 의 race 도 없앤다는 것 — 문서는 그 recipe 의 fix 로 `MULTI`/`EXEC` 가 아니라 Lua 를 명시적으로 별도 제시함 (조건부 분기 recipe 와 무분기 recipe 는 다른 처방) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-INCR-C1`: `INCR` 단일 커맨드 자체가 원자적이라는 공식 서술
|
||||
- `REDIS-INCR-C2`: `GET`→판단→`INCR`→조건부 `EXPIRE` recipe 에 공식 문서가 인정하는 race condition 이 존재한다는 사실
|
||||
- `REDIS-INCR-C3`: 그 race 의 구체적 실패 모드(EXPIRE 유실 시 key leak)
|
||||
- `REDIS-INCR-C4`: recipe 종류에 따라 공식이 권고하는 두 가지 다른 대체 메커니즘(MULTI/EXEC vs Lua/EVAL)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- `MULTI`/`EXEC` 가 조건부 분기(읽은 값에 따라 다음 커맨드를 결정)가 있는 recipe 의 race 도 없앤다는 것 — 문서는 이 경우 Lua 를 명시적으로 제시
|
||||
- `EVAL`/Lua 스크립트가 모든 Redis 배포 모드(clustered, Redis Functions 등)에서 동일하게 동작한다는 것 — 버전 제약("only available since Redis version 2.6")만 명시
|
||||
- key leak 이 실제 운영 환경에서 관측 가능한 심각도(메모리 규모·발생 빈도)를 가진다는 것 — 문서는 정성적 서술만 제공, 정량적 근거 없음
|
||||
- ca-tmpl/ca-skeleton 의 `ScriptDescriptor`/EVALSHA/NOSCRIPT 재적재 같은 구체적 구현 메커니즘 — 이는 본 branch 의 로컬 결정 사항이지 이 공식 문서의 주장이 아님
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `feature-redis-atomic-program-catalog-contract` 의 unsafe recipe 카탈로그에 이 `GET`→`INCR`→조건부 `EXPIRE` 패턴을 등재할 때, 대응 atomic 대체를 Lua 스크립트(EVALSHA)로 명시하고 MULTI/EXEC 로 오인하지 않도록 §구현 가이드에서 구분해야 함
|
||||
- 경합 재현 test 시나리오는 REDIS-INCR-C2/C3 의 실패 모드(첫 INCR 성공 후 EXPIRE 실패)를 인위적으로 유발할 수 있는 fault-injection 지점 확인 필요
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 본 문서의 "Pattern: rate limiter" 절은 rate limiter 1(고정 타임스탬프 키, 분기 없음 → MULTI/EXEC 로 충분)과 rate limiter 2(단일 카운터, `IF value == 1` 분기 있음 → MULTI/EXEC 로 불충분, Lua 필요)를 **구분**해서 서술한다. branch 의 unsafe-recipe 카탈로그를 작성할 때 이 두 변형을 하나의 "INCR+EXPIRE" 항목으로 뭉뚱그리면 REDIS-INCR-C4 의 구분을 잃는다 — 별개 카탈로그 항목으로 분리 권장.
|
||||
- 문서는 "Pattern: rate limiter 2" 의 세 번째 대안으로 Redis list(`RPUSH`/`RPUSHX`/`LLEN`) 기반 구현도 제시하며, 여기에도 `EXISTS` 확인과 `MULTI`/`EXEC` 사이의 race 가 있지만 "rare conditions 에서 API call 하나를 놓치는 정도"로 무해하다고 서술한다 — 이는 이번 branch 의 핵심 인용 범위(GET→INCR→EXPIRE) 밖이라 인용에는 포함하지 않았으나, "모든 race 가 치명적인 것은 아니다"라는 대비 사례로 §메모에만 남긴다(검증 안 된 해석이므로 claim 화하지 않음).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 프로젝트 내 Redis 원자성/락 관련 다른 official-doc: [[raw/official-docs/cache-redisson-rlock-vs-setnx]]
|
||||
- 이 자료를 인용한 wiki 요약: (미생성 — `/ingest` 후 `wiki/concepts/` 예정)
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: official-doc / Redis — Key Eviction & maxmemory-policy
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/develop/reference/eviction/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, caching, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Redis — Key Eviction & maxmemory-policy
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — Redis 공식 레퍼런스 문서 (redis.io/docs, vendor-neutral 공식 사양은 아니지만 Redis 프로젝트가 직접 운영하는 1차 문서).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | Redis role 을 cache(evictable) / coordination(noeviction) / session(noeviction) 으로 분리하고, `maxmemory-policy` 는 deployment(instance) 단위 설정이므로 애플리케이션이 아니라 배포 설정이 소유한다는 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/develop/reference/eviction/
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Redis (공식 문서, redis.io/docs)
|
||||
- 발행일: (문서에 명시 없음 — 페이지 내 "Redis 8.6" 기준 최신 갱신 확인)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`maxmemory-policy` 가 Redis 서버(인스턴스) 단위의 설정 directive라는 것과, eviction policy 종류별 의미(특히 `noeviction`이 write 명령을 에러로 거부한다는 것)를 원문으로 확인해, 3-role(cache/coordination/session) 분리와 policy 소유권을 배포 설정으로 두는 결정의 근거로 삼는다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Eviction policies] "Use the `maxmemory-policy` configuration directive to select the eviction policy you want to use when the limit set by `maxmemory` is reached." (source lines 80–81)
|
||||
|
||||
> [§Eviction policies] "`noeviction`: Keys are not evicted but the server will return an error when you try to execute commands that cache new data. If your database uses replication then this condition only applies to the primary database. Note that commands that only read existing data still work as normal." (source lines 85–88)
|
||||
|
||||
> [§Eviction policies] "`volatile-lru`: Evict the least recently used keys that have an associated expiration (TTL)." (source line 93)
|
||||
|
||||
> [§LFU eviction] "LFU is approximated like LRU: it uses a probabilistic counter, called a Morris counter to estimate the object access frequency using just a few bits per object, combined with a decay period so that the counter is reduced over time." (source line 243)
|
||||
|
||||
> [§Eviction policies] "Note also that if you make good use of key expiration, then you are less likely to run into the cache memory limit because keys will often expire before they need to be evicted." (source lines 120–122)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-EVICT-C1 | `maxmemory-policy` 는 `maxmemory` 한도 도달 시 적용할 eviction policy 를 선택하는 **configuration directive**다 — `redis.conf` 또는 `CONFIG SET` 으로 설정하는 서버(인스턴스) 단위 값이며, 애플리케이션 코드 API 가 아니다 | [§Eviction policies] "Use the `maxmemory-policy` configuration directive to select the eviction policy you want to use when the limit set by `maxmemory` is reached." | `official-vendor-doc` | 임의 Redis 인스턴스(단일 노드/replica 포함)에서의 eviction policy 설정 경로 | 한 인스턴스 안에서 key 별/role 별로 다른 eviction policy 를 동시 적용할 수 있는지 여부 — 문서가 명시하지 않음 (다만 "하나의 directive" 라는 서술 자체가 인스턴스당 정책이 1개임을 시사) |
|
||||
| REDIS-EVICT-C2 | `noeviction` policy 에서는 key 가 evict 되지 않고, 대신 `maxmemory` 초과 시 **새 데이터를 캐시하는 명령이 에러를 반환**한다. read-only 명령은 정상 동작한다. replication 환경에서는 이 조건이 primary 에만 적용된다 | [§Eviction policies] "`noeviction`: Keys are not evicted but the server will return an error when you try to execute commands that cache new data. ... Note that commands that only read existing data still work as normal." | `official-vendor-doc` | `maxmemory-policy=noeviction` 이고 `maxmemory` 한도에 도달한 모든 Redis 배포 | 어떤 명령이 정확히 "새 데이터를 캐시하는 명령"에 해당하는지 명령어 단위 목록은 제공하지 않음. `maxmemory=0`(무제한)일 때는 이 조건 자체가 발동하지 않음(이 문서의 다른 절에서 별도 서술) |
|
||||
| REDIS-EVICT-C3 | `volatile-lru` 는 TTL(만료 시각)이 설정된 key 들 중에서만 least-recently-used 기준으로 evict 한다 — `volatile-*` 계열 policy 는 TTL 이 있는 key 만 eviction 대상으로 삼는다 | [§Eviction policies] "`volatile-lru`: Evict the least recently used keys that have an associated expiration (TTL)." | `official-vendor-doc` | TTL 을 명시적으로 설정한 key 들이 존재하는 상황에서 `volatile-*` 계열 policy 를 쓸 때의 동작 | 이 branch 의 session/coordination role 이 실제로 TTL 을 세팅하는지는 이 문서로 증명되지 않음(프로젝트 구현 사실) |
|
||||
| REDIS-EVICT-C4 | Redis 의 LFU eviction(및 이와 비교되는 LRU)은 **근사(approximated)** 알고리즘이다 — probabilistic counter(Morris counter)로 접근 빈도를 추정하고 decay 를 적용하는 방식이며, 정확한 전역 순서 계산이 아니다 | [§LFU eviction] "LFU is approximated like LRU: it uses a probabilistic counter, called a Morris counter to estimate the object access frequency using just a few bits per object, combined with a decay period so that the counter is reduced over time." | `official-vendor-doc` | `allkeys-lfu`/`volatile-lfu`(및 대응 LRU policy) 를 사용하는 모든 Redis 배포의 eviction 정확도 특성 | 특정 워크로드에서 hit/miss ratio 에 미치는 정량적 영향은 제공하지 않음. ca-skeleton cache role 의 실측 hit ratio 는 이 문서로 증명되지 않음 |
|
||||
| REDIS-EVICT-C5 | key expiration(TTL 기반 자동 삭제)을 잘 활용하면 key 가 evict 되기 전에 먼저 만료되므로 memory 한도 초과 상황 자체가 줄어든다 — 즉 "만료(expire)"와 "축출(evict)"은 서로 다른, 별개의 삭제 경로다 | [§Eviction policies] "Note also that if you make good use of key expiration, then you are less likely to run into the cache memory limit because keys will often expire before they need to be evicted." | `official-vendor-doc` | TTL 기반 삭제(expiration)와 memory-pressure 기반 삭제(eviction)를 구분해야 하는 모든 Redis 사용 맥락 | `noeviction` 처럼 애초에 evict 를 하지 않는 policy 에서 TTL 이 필수인지는 이 문장만으로 증명되지 않음(별도로 C2 가 `noeviction` 자체의 동작을 규정) |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, Redis 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-EVICT-C1`: `maxmemory-policy` 는 Redis **인스턴스(서버) 단위** 설정이다 — `redis.conf` 또는 `CONFIG SET` 으로 지정되는 configuration directive 이며 애플리케이션 코드 API 가 아니다. 하나의 directive 라는 서술은 한 인스턴스에 정책이 1개만 존재함을 시사한다.
|
||||
- `REDIS-EVICT-C2`: `noeviction` 은 write 를 에러로 거부하고 read 는 계속 허용하는 "fail loud" 동작이다 — 데이터 유실보다 명시적 실패를 선호하는 role(coordination/session)에 적합한 근거가 된다.
|
||||
- `REDIS-EVICT-C3` / `REDIS-EVICT-C5`: eviction(memory-pressure 기반)과 expiration(TTL 기반)은 서로 다른 삭제 메커니즘이며, `volatile-*` policy 는 TTL 있는 key 만 대상으로 삼는다.
|
||||
- `REDIS-EVICT-C4`: LRU/LFU eviction 은 근사 알고리즘이라 완벽한 전역 순서를 보장하지 않는다 — cache role 설계 시 "정확한 LRU"를 전제하면 안 된다는 근거.
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- Redis 의 **기본(default) `maxmemory-policy` 값**이 무엇인지 이 페이지는 명시하지 않는다(다른 절이 `maxmemory` 기본값 0 만 언급).
|
||||
- "cache = evictable, coordination/session = noeviction" 이라는 **3-role 매핑 자체**는 이 문서의 용어가 아니다 — Redis 문서는 "cache" 사용 사례를 일반적으로 언급할 뿐, ca-skeleton 의 role 분류 체계(cache/coordination/session)를 알지 못한다. 이 매핑은 branch 의 결정이며, 본 자료는 그 결정을 뒷받침하는 일반 eviction 시맨틱만 제공한다.
|
||||
- 한 Redis 인스턴스 안에서 key/role 별로 다른 `maxmemory-policy` 를 동시 적용할 수 없다는 것을 **명시적으로 금지**한다고 말하지는 않는다 — `maxmemory-policy` 가 단일 directive 라는 서술에서 **유추**할 뿐이며, 이는 오히려 "다른 정책이 필요한 role 은 별도 Redis 배포로 분리해야 한다"는 branch 결정과 정합적이다.
|
||||
- `maxmemory`/persistence(RDB·AOF) 소유 경계에 대한 심층 규정은 없다 — replication/persistence buffer 가 `maxmemory` 계산에서 제외된다는 부수적 언급만 있고, 이 문서는 eviction 에 집중한다.
|
||||
- ca-skeleton 의 실제 Redis 배포(단일 인스턴스 vs 복수 인스턴스, docker-compose/Helm 설정 파일 소유권)가 어떻게 구성되어야 하는지는 이 문서로 확인되지 않는다 — `/branch-spec` 단계에서 구현 결정으로 별도 확정 필요.
|
||||
|
||||
## 메모
|
||||
|
||||
- 원문에 명시된 evict/expire 구분과 "maxmemory-policy = 인스턴스 단위 directive" 서술은, coordination/session role 이 cache role 과 같은 Redis 인스턴스에 co-locate 될 수 없다는 branch 결정(incompatible co-location 거부)의 방향성과 일치한다 — 단, 이 연결 자체는 branch 의 해석이며 본 문서가 직접 말하는 것은 아니다.
|
||||
- `allkeys-lru` / `allkeys-lfu` / `allkeys-random` / `volatile-ttl` 등 나머지 policy 종류의 정확한 정의도 같은 페이지 §Eviction policies 목록에 있음 — 필요 시 추가 인용 가능(현재는 5개로 제한).
|
||||
- Redis 8.6 부터 추가된 LRM(Least Recently Modified) policy 도 같은 페이지에 있으나, 이 branch 결정과 직접 관련 없어 인용에서 제외.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Spring Data Redis / Lettuce 커넥션 설정 관련 공식 문서는 `/branch-spec` 단계에서 추가 수집 예정)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: official-doc / Redis Lua API Reference — call vs pcall, typed errors, effects replication, sandbox
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/develop/programmability/lua-api/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, runtime, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Redis Lua API Reference — call vs pcall, typed errors, effects replication, sandbox
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | atomic program 은 bounded·결정론적이어야 하며, 오류는 typed 로 반환하고, 복제 의미(effects replication)를 이해한 상태에서 작성해야 한다 — 그 제약을 카탈로그 규칙으로 고정한다. |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/develop/programmability/lua-api/
|
||||
- 참고: dispatch 에서 지정한 `https://redis.io/docs/latest/develop/interact/programmability/lua-api/` 는 WebFetch 시 `404 Not Found` — 위 대체 URL 로 fetch 성공(본 문서의 `url:` 은 실제 fetch 성공한 URL).
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Redis, Inc. (공식 레퍼런스 문서 — "Redis Lua API reference")
|
||||
- 발행일: 문서 자체에 발행일 명시 없음 (버전 미고정 "latest" reference 페이지, 각 API 항목에 `Since version:` / `Until version:` 개별 명시)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-redis-atomic-program-catalog-contract` 는 EVALSHA 기반 atomic program 이 (1) bounded/deterministic 하고 (2) 오류를 typed 로 반환하며 (3) replication 의미(verbatim vs effects)를 이해한 상태로 작성되어야 한다는 카탈로그 규칙을 정의하려 한다. 이 문서는 그 세 축(오류 처리, typed reply, replication 모드)의 1차 공식 근거다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§redis.call] "If and when `redis.call()` triggers a runtime exception, the raw exception is raised back to the user as an error, automatically." (line 127)
|
||||
|
||||
> [§redis.pcall] "The `redis.pcall()` function behaves exactly like [`redis.call()`](#redis.call), except that it: * Always returns a reply. * Never throws a runtime exception, and returns in its stead a [`redis.error_reply`](#redis.error_reply) in case that a runtime exception is thrown by the server." (line 149)
|
||||
|
||||
> [§redis.error_reply] "The helper accepts a single string argument and returns a Lua table with the _err_ field set to that string." (line 177)
|
||||
|
||||
> [§redis.status_reply] "The Lua API represents status replies as tables with a single field, _ok_, set with a simple status string." (line 211)
|
||||
|
||||
> [§redis.set_repl] "As of Redis version 7.0, script replication is no longer supported, and the only replication mode available is script effects replication." (line 278)
|
||||
|
||||
> [§redis.replicate_commands] "* Until version: 7.0.0" ... "The default, and only script replication mode supported, is script effects' replication." (lines 318, 327)
|
||||
|
||||
> [§The KEYS global variable] "The script **should only** access keys whose names are given as input arguments. Scripts **should never** access keys with programmatically-generated names or based on the contents of data structures stored in the database." (lines 87–88)
|
||||
|
||||
> [§Sandbox context > Global variables and functions] "The sandboxed Lua execution context blocks the declaration of global variables and functions." (line 17)
|
||||
|
||||
> [§Sandbox context > Imported Lua modules] "The sandboxed execution context prevents the loading modules by disabling Lua's [`require` function](https://www.lua.org/pil/8.1.html)." (line 65)
|
||||
|
||||
> [§redis.setresp] "The default protocol version is _2_, but it can be switched to version _3_." (line 257)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-LUAAPI-C1 | `redis.call()` propagates a runtime exception as a raw error automatically (script aborts); `redis.pcall()` never throws — it always returns a reply, converting the exception into a `redis.error_reply` table instead | [§redis.call] "the raw exception is raised back to the user as an error, automatically." / [§redis.pcall] "Never throws a runtime exception, and returns in its stead a `redis.error_reply`..." | official-reference | EVAL/EVALSHA 기반 ephemeral script 및 Redis Functions 모두 (문서에 "Available in functions: yes" 명시) | 상위 클라이언트 라이브러리(예: Spring Data Redis `RedisScript`)가 이 예외를 어떻게 재래핑하는지는 증명 안 함 |
|
||||
| REDIS-LUAAPI-C2 | `redis.error_reply(x)` / `redis.status_reply(x)` 는 각각 `{err=...}` / `{ok=...}` Lua table 을 반환하는 공식 helper — 스크립트가 typed error/status 를 반환하는 공식 수단 | [§redis.error_reply] "returns a Lua table with the _err_ field set to that string." / [§redis.status_reply] "tables with a single field, _ok_, set with a simple status string." | official-reference | 모든 EVAL/EVALSHA 스크립트 및 함수의 반환값 설계 | 어떤 error code(첫 단어 convention)를 카탈로그가 강제해야 하는지는 규정 안 함 — 문서는 "convention"일 뿐 "not mandatory"라고 명시 |
|
||||
| REDIS-LUAAPI-C3 | Redis 7.0 부터 verbatim script replication 이 완전히 제거되고, 오직 effects replication(쓰기 명령의 결과만 복제) 만 지원됨 | [§redis.set_repl] "As of Redis version 7.0, script replication is no longer supported, and the only replication mode available is script effects replication." | official-reference | Redis 7.0+ 배포 환경의 replication 동작 이해 | 6.x 이하에서의 verbatim replication 동작 세부(스크립트 소스 자체가 replica로 전송되는 방식)까지는 이 인용만으로 재구성 불가 — 별도 §redis.set_repl 상단 문단 확인 필요 |
|
||||
| REDIS-LUAAPI-C4 | `redis.replicate_commands()` 는 `3.2.0`~`7.0.0` 구간에만 존재한 API(verbatim→effects 전환 함수)였고, 7.0 이후에는 effects replication 이 기본값이자 유일한 모드가 되어 이 함수 호출이 불필요해짐 | [§redis.replicate_commands] "* Until version: 7.0.0" / "The default, and only script replication mode supported, is script effects' replication." | official-reference | Redis 버전별(7.0 이전 vs 이후) 카탈로그 program 작성 시 `redis.replicate_commands()` 호출 필요 여부 판단 | 비결정적 명령(RANDOMKEY, TIME, SPOP 등)을 program 에서 사용해도 되는지에 대한 명시적 규칙은 **이 페이지에 없음** — 별도 확인 필요 (아래 Usage Boundaries 참고) |
|
||||
| REDIS-LUAAPI-C5 | atomic program 이 접근하는 key 이름은 반드시 입력 인자(KEYS)로 명시되어야 하며, 프로그램적으로 생성되거나 데이터 내용에 기반한 key 이름 접근은 금지된다 | [§The KEYS global variable] "The script **should only** access keys whose names are given as input arguments. Scripts **should never** access keys with programmatically-generated names..." | official-reference | ScriptDescriptor 의 필수 필드 설계(KEYS 선언 강제) 근거 | 이 규칙은 key 접근 방식에 대한 것이며, program 의 시간/공간 복잡도(O(1)/bounded)를 직접 규정하지 않음 — bounded execution 규칙 자체는 별도 근거 필요 |
|
||||
| REDIS-LUAAPI-C6 | Lua 샌드박스는 전역 변수/함수 선언과 미정의 전역 변수 접근을 차단한다 — 모든 선언은 `local` 이어야 한다 | [§Sandbox context > Global variables and functions] "The sandboxed Lua execution context blocks the declaration of global variables and functions." | official-reference | 카탈로그 program 작성 코딩 규칙(전역 변수 금지) | 문서 자체가 "sandbox 우회는 실수로는 어렵지만 불가능하지 않다"고 명시 — 우회 시도에 대한 강제력의 절대성까지는 증명 안 함 |
|
||||
| REDIS-LUAAPI-C7 | Lua 샌드박스는 `require` 를 비활성화해 임의 외부 모듈 로드를 금지하며, 문서가 나열한 runtime 라이브러리(string/table/math/os 일부/struct/cjson/cmsgpack/bit)만 사용 가능하다 | [§Sandbox context > Imported Lua modules] "prevents the loading modules by disabling Lua's `require` function." | official-reference | 카탈로그 program 이 의존할 수 있는 라이브러리 화이트리스트 근거 | Redis Functions 배포 모드에서 라이브러리 제약이 ephemeral script 와 동일한지는 이 인용만으로 확정 안 됨(문서는 이 제약이 sandbox 공통 서술이라고만 함) |
|
||||
| REDIS-LUAAPI-C8 | `redis.setresp(x)` 로 `redis.call()`/`redis.pcall()` 응답의 RESP 버전을 전환할 수 있으며 기본값은 RESP2, 옵트인으로 RESP3 전환 가능 | [§redis.setresp] "The default protocol version is _2_, but it can be switched to version _3_." | official-reference | typed facade 가 `redis.call`/`redis.pcall` 반환값을 파싱할 때 어떤 RESP 버전을 가정해야 하는지 근거 | RESP3 개별 타입(map/set/double/big_number/verbatim_string) 각각의 Lua 변환 세부 규칙까지는 이 인용에 포함하지 않음 — 필요 시 원문 "RESP3 to Lua type conversion" 섹션 추가 확인 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-LUAAPI-C1`, `C2`: call/pcall 오류 전파 차이 + typed error/status reply 공식 API
|
||||
- `REDIS-LUAAPI-C3`, `C4`: Redis 7.0 기준 effects replication 이 유일한 모드이고 `redis.replicate_commands()` 는 과거(3.2.0~7.0.0) 호환 함수였다는 사실
|
||||
- `REDIS-LUAAPI-C5`: KEYS 는 반드시 입력 인자로 선언되어야 한다는 규칙
|
||||
- `REDIS-LUAAPI-C6`, `C7`: 전역 변수 금지 + `require` 비활성화(라이브러리 화이트리스트)
|
||||
- `REDIS-LUAAPI-C8`: `redis.setresp()` 기본값/전환 가능 여부
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **비결정적 명령(RANDOMKEY, TIME, SPOP 등) 사용 시의 규칙** — 이 페이지(`lua-api`)에는 명시적 서술이 없다. `eval-intro` 페이지(`redis.io/docs/latest/develop/programmability/eval-intro`)에서 다룰 가능성이 높으나 본 fetch 범위 밖이다. 카탈로그 문서에서 "비결정적 명령을 program 에서 금지/제한한다"는 규칙을 세우려면 **`UNSUPPORTED_DECISION`** 으로 표시하거나 `eval-intro` 를 별도 raw-source 로 추가 수집해야 한다.
|
||||
- Redis Functions(`FUNCTION LOAD`) 전용 API(`redis.register_function`, script flags `no-writes`/`allow-oom`/`no-cluster` 등)의 세부 동작 — 본 문서에 존재하나 이번 인용 셋에 포함하지 않았다. Functions 배포 모드를 카탈로그 규칙에 넣으려면 별도 claim 추출 필요.
|
||||
- RESP3 개별 타입 변환 세부 규칙(map/set/double/big_number/verbatim_string) — 원문에 존재하나 인용 미포함.
|
||||
- Redis 6.x 이하에서 `redis.replicate_commands()` 를 호출하지 않았을 때의 구체적 오류/폴백 동작.
|
||||
- 내 프로젝트(ca-skeleton)에 적용하려면 추가 확인이 필요한 것:
|
||||
- 사용 중인 Redis 서버/클라이언트 버전이 7.0 이상인지 확인 — 이상이면 `redis.replicate_commands()` 호출은 불필요(no-op)하고 effects replication 이 기본.
|
||||
- typed facade 구현 시 `redis.setresp()` 미호출 상태(RESP2 기본)를 가정해도 되는지 실제 배포 Redis 버전과 클라이언트 설정으로 검증.
|
||||
|
||||
## 메모
|
||||
|
||||
> 검증되지 않은 추론은 여기까지만. 인용 섹션에는 넣지 않았다.
|
||||
|
||||
- 비결정적 명령 규칙 공백(RANDOMKEY/TIME/SPOP)은 `eval-intro` 페이지를 별도 `wiki-source-summarizer` dispatch 로 수집해야 branch-note의 "bounded/deterministic" 결정이 완전히 근거를 갖춘다 — 현재는 부분 근거만 확보된 상태.
|
||||
- 이 문서 하나로 branch 의 "복제 의미를 이해한 상태에서 작성" 요구는 effects-replication 전환(C3/C4) 근거는 있으나, "왜 그 요구가 필요한가"(replica/AOF 불일치 위험)는 `redis.set_repl` 섹션 상단의 warning 문단(이번 인용에 미포함)에 더 있다 — 필요 시 추가 인용 가능.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 branch 의 sibling 근거 (아직 미등록): eval-intro (bounded execution, blocking, 비결정적 명령) — `/branch-spec feature-redis-atomic-program-catalog-contract` 진행 시 추가 수집 권고.
|
||||
- 이 자료를 인용한 wiki 요약: 아직 없음 (raw 단계).
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: "official-doc / Redis Programmability — Maximum Execution Time (busy-reply-threshold, BUSY, SHUTDOWN NOSAVE)"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/develop/programmability/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, runtime, redis]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Redis Programmability — Maximum Execution Time (busy-reply-threshold, BUSY, SHUTDOWN NOSAVE)
|
||||
|
||||
> Layer: `raw/official-docs/` — Redis 공식 개요 페이지(`develop/programmability/`, §Maximum execution time) 원문 발췌.
|
||||
> 자매 문서 `[[raw/official-docs/redis-eval-scripting-atomicity]]` 의 `REDIS-EVAL-C6` `Does not prove` 가 명시한 공백(`busy-reply-threshold`/구 `lua-time-limit` 설정 이름, `BUSY` 에러, 쓰기 스크립트의 `SHUTDOWN NOSAVE` 강제 종료 조건)을 닫기 위한 dispatch.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | atomic program 에 bounded execution(유한 실행) 규칙을 두고, 실행 시간 초과의 사후 대응(서버 설정 + 런북)을 예방 계층과 분리해 상시 병행한다 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/develop/programmability/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Redis (Redis Ltd. 공식 문서)
|
||||
- 발행일: Redis 공식 reference, 최신 버전 anchor (본문에 Redis 7.0/2.6.0/5.0 버전 변경 이력 명시)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-redis-atomic-program-catalog-contract` 의 bounded execution 규칙은 "예방"(KEYS 선언·O(1)/유한 루프 설계)과 "사후 대응"(서버가 실제로 시간 초과된 스크립트를 어떻게 처리하는가)이 분리된 계약이다. 자매 문서 `redis-eval-scripting-atomicity.md` 는 `SCRIPT KILL` 이 "장시간 실행 스크립트를 중단하는 유일한 방법"이라고만 말하고 임계값 설정 이름·`BUSY` 상태·쓰기 스크립트의 예외를 확인하지 않았다(`REDIS-EVAL-C6` Does not prove). 이 문서는 정확히 그 공백 — 기본 임계값·설정 파라미터명·타임아웃 도달 후 서버 상태 전이(BUSY)·명령별 허용 여부 — 을 원문으로 닫는다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Maximum execution time] "Scripts are subject to a maximum execution time (set by default to five seconds)."
|
||||
|
||||
> [§Maximum execution time] "The configuration parameter affecting max execution time is called busy-reply-threshold."
|
||||
|
||||
> [§Maximum execution time] "When a script reaches the timeout threshold, it isn't terminated by Redis automatically."
|
||||
>
|
||||
> [§Maximum execution time] "Doing so would violate the contract between Redis and the scripting engine that ensures that scripts are atomic."
|
||||
|
||||
> [§Maximum execution time] "It starts accepting commands again from other clients but will reply with a BUSY error to all the clients sending normal commands. The only commands allowed in this state are SCRIPT KILL, FUNCTION KILL, and SHUTDOWN NOSAVE."
|
||||
|
||||
> [§Maximum execution time] "If the script had already performed even a single write operation, the only command allowed is SHUTDOWN NOSAVE that stops the server without saving the current data set on disk (basically, the server is aborted)."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-PROG-C1 | 스크립트(및 함수)는 기본 5초의 최대 실행 시간 제한을 가지며, 이 값을 제어하는 설정 파라미터 이름은 `busy-reply-threshold`이다(밀리초 단위로 `redis.conf` 또는 `CONFIG SET`으로 변경 가능) | "Scripts are subject to a maximum execution time (set by default to five seconds)." / "The configuration parameter affecting max execution time is called busy-reply-threshold." | `official-vendor-doc` | 모든 EVAL/EVALSHA/FCALL 스크립트·함수 실행의 기본 timeout 설정 | 구 파라미터 이름 `lua-time-limit` 은 본 페이지에 등장하지 않음 — 이름 변경 이력(구명→신명)은 이 자료만으로 증명되지 않음, 별도 changelog/config 문서 확인 필요 |
|
||||
| REDIS-PROG-C2 | 스크립트가 임계값을 초과해도 Redis 는 자동으로 스크립트를 강제 종료하지 않는다 — 그렇게 하면 스크립트가 원자적이라는 Redis-스크립팅 엔진 간 계약을 위반하게 되기 때문이다 | "When a script reaches the timeout threshold, it isn't terminated by Redis automatically." / "Doing so would violate the contract between Redis and the scripting engine that ensures that scripts are atomic." | `official-vendor-doc` | 시간 초과된 모든 스크립트/함수 실행 일반 | half-written 상태를 실제로 어떤 내부 메커니즘(예: 인터프리터 훅 지점)으로 방지하는지 구현 detail 은 제공하지 않음 |
|
||||
| REDIS-PROG-C3 | 임계값 초과 후 서버는 다른 클라이언트의 일반 명령에 `BUSY` 에러로 응답하며, 이 상태에서 허용되는 명령은 `SCRIPT KILL`, `FUNCTION KILL`, `SHUTDOWN NOSAVE` 뿐이다. 그중 `SCRIPT KILL`/`FUNCTION KILL` 은 아직 쓰기를 하지 않은(read-only) 스크립트에만 사용 가능하다 | "It starts accepting commands again from other clients but will reply with a BUSY error to all the clients sending normal commands. The only commands allowed in this state are SCRIPT KILL, FUNCTION KILL, and SHUTDOWN NOSAVE." | `official-vendor-doc` | timeout 이후 BUSY 상태의 서버에 대한 클라이언트 명령 허용 목록 | `maxmemory` 초과와 이 BUSY 상태의 상호작용은 본 페이지에서 확인되지 않음(§Usage Boundaries 참조) |
|
||||
| REDIS-PROG-C4 | 스크립트가 이미 쓰기 연산을 한 번이라도 수행했다면, 그 시점부터 허용되는 명령은 `SHUTDOWN NOSAVE` 뿐이며 이는 현재 데이터셋을 디스크에 저장하지 않고 서버를 정지시킨다(사실상 서버 강제 종료) | "If the script had already performed even a single write operation, the only command allowed is SHUTDOWN NOSAVE that stops the server without saving the current data set on disk (basically, the server is aborted)." | `official-vendor-doc` | 쓰기를 이미 수행한 시간 초과 스크립트/함수에 대한 유일한 복구 경로 | `SHUTDOWN NOSAVE` 이후 재시작 절차, replica/cluster 환경에서의 failover 영향은 본 페이지에 없음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `REDIS-PROG-C1`: 기본 5초 실행 시간 제한 + 설정 파라미터명 `busy-reply-threshold`
|
||||
- `REDIS-PROG-C2`: 시간 초과 시 Redis 가 스크립트를 자동 종료하지 않음(원자성 계약 보호 목적)
|
||||
- `REDIS-PROG-C3`: 시간 초과 후 `BUSY` 에러 상태 + 허용 명령(`SCRIPT KILL`/`FUNCTION KILL`/`SHUTDOWN NOSAVE`) + read-only 스크립트만 `SCRIPT KILL`/`FUNCTION KILL` 로 중단 가능
|
||||
- `REDIS-PROG-C4`: 쓰기를 수행한 스크립트는 `SHUTDOWN NOSAVE` 만 허용(서버 강제 종료)
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- **구 파라미터 이름 `lua-time-limit`** — 본 페이지에 전혀 등장하지 않는다(self-grep 부재 확인, fabrication 아님). `busy-reply-threshold` 가 그 후신이라는 이름 변경 이력은 별도 Redis changelog/config 문서 확인 필요.
|
||||
- **`maxmemory` 와의 상호작용** — "maxmemory 초과 상태에서 추가 메모리를 쓰는 첫 write 명령이 스크립트를 abort 시킨다"는 서술은 본 페이지에 없다(self-grep 부재 확인). `maxmemory-policy` 자체의 일반 의미(예: `noeviction` 이 write 를 에러로 거부)는 `[[raw/official-docs/redis-key-eviction-maxmemory-policy]]` 가 별도로 다루지만, 그 문서 역시 "스크립트 abort" 를 스크립트 맥락에서 명시하지는 않는다 — 두 자료를 합쳐도 "maxmemory 초과 → 스크립트 abort" 인과관계 자체는 증명되지 않는다.
|
||||
- `SHUTDOWN NOSAVE` 이후의 클러스터/replica 복구 절차, Sentinel/failover 와의 상호작용
|
||||
- Redis Functions(FUNCTION) 고유의 timeout 규칙 차이 — 이 페이지는 스크립트와 함수를 동일 취급하는 서술("SCRIPT KILL, FUNCTION KILL")만 제공, 세부 차이는 없음
|
||||
- **내 프로젝트(ca-skeleton)에 적용하려면 추가 확인이 필요한 것**:
|
||||
- 운영 환경에서 `busy-reply-threshold` 를 기본 5초에서 조정할지 여부와 그 근거(레이턴시 SLO 대비)
|
||||
- `BUSY` 상태 발생 시 알림/런북 설계 — 이 페이지는 서버 동작만 규정하고 운영 대응 절차는 제공하지 않음
|
||||
- `maxmemory` 초과와 스크립트 실행의 실제 상호작용은 별도 실험/공식 자료로 검증 필요(현재 미확인 상태로 branch 결정에 반영하지 않을 것)
|
||||
|
||||
## 메모
|
||||
|
||||
- 이번 dispatch 로 자매 문서 `redis-eval-scripting-atomicity.md` 의 `REDIS-EVAL-C6` Does not prove 공백 중 `busy-reply-threshold`/`BUSY`/`SHUTDOWN NOSAVE` 3개는 닫혔다. `lua-time-limit`(구명)과 `maxmemory` 상호작용 2개는 이 페이지에도 없어 여전히 미확인 — 필요하면 별도 config 레퍼런스 또는 릴리스 노트 fetch 후보.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Redis `redis.conf` 공식 주석(파라미터 changelog), maxmemory 공식 문서의 스크립트 상호작용 여부 재확인.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- [[raw/official-docs/redis-eval-scripting-atomicity]] — 이 문서가 닫는 공백을 남긴 자매 문서(`REDIS-EVAL-C6`)
|
||||
- [[raw/official-docs/redis-key-eviction-maxmemory-policy]] — `maxmemory-policy` 일반 의미(스크립트 맥락은 아님)
|
||||
- [[raw/official-docs/redis-lua-api-determinism-replication]] — 같은 programmability 문서군, sandbox/replication 세부
|
||||
- [[raw/official-docs/redis-functions-library-deployment]] — Redis Functions(FUNCTION LOAD 등) 배포 조건
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "official-doc / Redis Replication — Asynchronous Propagation, Stale Reads, WAIT Semantics"
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/operate/oss_and_stack/management/replication/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, redis, replication]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Redis Replication — Asynchronous Propagation, Stale Reads, WAIT Semantics
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 본 문서는 Redis 공식 replication 문서 원문 발췌. 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
- `official-doc` — Redis 공식 레퍼런스 (redis.io/docs)
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | coordination/session/idempotency/lease/rate role 은 primary read 만 쓰고 stale 을 허용하는 cache region 만 replica read 를 opt-in 한다는 read routing 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/operate/oss_and_stack/management/replication/
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Redis (공식 문서, redis.io/docs)
|
||||
- 발행일: (문서 내 명시 없음 — "latest" 채널 문서, 버전 미고정)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Redis 는 기본적으로 비동기(asynchronous) replication 을 쓰고, replica 는 stale 데이터를 반환할 수 있으며, `WAIT` 명령조차 strong consistency(CP)를 보장하지 않는다는 공식 근거. coordination/session/idempotency/lease/rate 처럼 정확성이 중요한 role 은 replica read 를 쓰면 안 되고 primary read 만 써야 한다는 role-isolation 결정을 뒷받침한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Important facts about Redis replication] "Redis uses asynchronous replication, with asynchronous replica-to-master acknowledges of the amount of data processed."
|
||||
|
||||
> [§Important facts about Redis replication] "While the replica is performing the initial synchronization, it can handle queries using the old version of the dataset, assuming you configured Redis to do so in redis.conf."
|
||||
|
||||
> [§Allow writes only with N attached replicas] "However, because Redis uses asynchronous replication it is not possible to ensure the replica actually received a given write, so there is always a window for data loss."
|
||||
|
||||
> [§Read-only replica] "Since Redis 2.6, replicas support a read-only mode that is enabled by default."
|
||||
|
||||
> [intro, before §Important facts — WAIT 명령 관련, 원문은 `WAIT` 를 `/commands/wait` 로 하이퍼링크 처리함] "However `WAIT` is only able to ensure there are the specified number of acknowledged copies in the other Redis instances, it does not turn a set of Redis instances into a CP system with strong consistency: acknowledged writes can still be lost during a failover, depending on the exact configuration of the Redis persistence."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-REPL-C1 | Redis 는 기본적으로 비동기 replication 을 쓰고, replica 는 master 에게 처리한 데이터 양을 비동기적으로 ack 한다 (master 는 매 command 마다 replica 응답을 기다리지 않음) | [§Important facts] "Redis uses asynchronous replication, with asynchronous replica-to-master acknowledges of the amount of data processed." | `official-vendor-doc` | Redis OSS/Stack 기본 replication 설정 전반 (master-replica) | Redis Cluster·Sentinel 이 이 기본 동작 위에 추가하는 failover 세부 동작까지 증명하지 않음(문서가 "이 문서는 기본 replication 특성만 다룬다"고 명시) |
|
||||
| REDIS-REPL-C2 | replica 는 초기 동기화(initial synchronization) 중에도 설정에 따라 이전 버전의 데이터셋으로 쿼리를 처리할 수 있다 — 즉 replica read 는 stale 데이터를 반환할 수 있는 경로가 존재한다 | [§Important facts] "While the replica is performing the initial synchronization, it can handle queries using the old version of the dataset, assuming you configured Redis to do so in redis.conf." | `official-vendor-doc` | initial full sync 구간의 replica read 동작 | 정상 steady-state(sync 이후) replication lag 에 의한 stale read 크기(초/밀리초 단위)는 이 문장만으로는 정량화되지 않음 — 별도 latency 측정 필요 |
|
||||
| REDIS-REPL-C3 | 비동기 replication 특성상 특정 write 가 replica 에 실제로 도달했는지 보장할 수 없고, 따라서 항상 data loss 가능 구간(window)이 존재한다 | [§Allow writes only with N attached replicas] "However, because Redis uses asynchronous replication it is not possible to ensure the replica actually received a given write, so there is always a window for data loss." | `official-vendor-doc` | master 장애/failover 시 최근 write 유실 가능성 일반 (min-replicas-to-write 설정 여부와 무관하게 근본 원인) | `min-replicas-to-write` 를 설정하면 이 window 가 "완전히 사라진다"는 뜻은 아님 — 문서는 이 설정도 "best effort" 라고 명시(별도 인용은 미등록, 같은 섹션 서술) |
|
||||
| REDIS-REPL-C4 | Redis 2.6 이후 replica 는 기본적으로 read-only 모드다 | [§Read-only replica] "Since Redis 2.6, replicas support a read-only mode that is enabled by default." | `official-vendor-doc` | `replica-read-only` 설정을 명시적으로 바꾸지 않은 모든 Redis 2.6+ replica | writable replica 로 전환 가능하다는 사실 자체가 그것이 권장된다는 뜻은 아님(문서는 반대로 "writable replica 는 권장하지 않는다"고 별도 서술) |
|
||||
| REDIS-REPL-C5 | `WAIT` 명령은 지정한 수의 replica 에 acknowledge 된 복사본이 있음을 보장할 뿐, Redis 인스턴스 집합을 strong consistency 를 갖는 CP 시스템으로 만들지 않는다 — ack 된 write 도 failover 중 유실될 수 있다 | [intro] "it does not turn a set of Redis instances into a CP system with strong consistency: acknowledged writes can still be lost during a failover, depending on the exact configuration of the Redis persistence." | `official-vendor-doc` | `WAIT` 를 사용하는 준-동기(quasi-synchronous) write 패턴 전반 | `WAIT` 가 무의미하다는 뜻은 아님 — 문서는 이어서 "WAIT dramatically reduces the probability of losing a write" 라고 명시. 즉 "손실 확률을 낮추지만 0으로 만들지 않는다"가 정확한 범위 |
|
||||
|
||||
### Strength 허용값 참고
|
||||
|
||||
전부 `official-vendor-doc` — Redis 공식 문서(redis.io/docs)이며 벤더가 직접 발행. `company-case-study` 아님(회사 사례가 아니라 제품 자체의 공식 동작 명세).
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `REDIS-REPL-C1`~`C5`: Redis 기본 replication 이 비동기이고, replica read 는 stale 을 반환할 수 있으며, `WAIT` 조차 acknowledged write 의 failover 유실을 완전히 막지 못한다는 것. 이는 "정확성이 중요한 role(coordination/session/idempotency/lease/rate) 은 replica read 를 쓰면 안 된다"는 branch 결정의 **일반 원리 근거**로 충분하다.
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- ca-skeleton 이 실제로 어떤 Redis 배포 토폴로지(managed service, Sentinel, Cluster 등)를 쓰는지 — 이 문서는 "basic replication" 만 다루고 Cluster/Sentinel 세부는 별도 문서로 위임한다고 명시.
|
||||
- stale read 의 정량적 크기(수 ms vs 수 초) — 배포·네트워크 환경에 의존, 이 문서만으로 SLA 수치를 도출할 수 없음.
|
||||
- cache region 에 replica read 를 opt-in 했을 때 실제 애플리케이션 정확성에 문제가 없다는 것 — 그건 ca-skeleton 이 cache region 을 "stale 허용" 으로 설계했다는 branch-local 결정이지, 이 문서가 그 설계를 승인하는 것이 아님.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 사용할 실제 Redis 배포(managed / self-hosted, Sentinel 유무)에서 replication lag 실측치
|
||||
- `replica-read-only`, `min-replicas-to-write`, `min-replicas-max-lag` 등 세부 설정값이 branch 구현 가이드에 실제로 반영되는지
|
||||
|
||||
## 메모
|
||||
|
||||
- REDIS-REPL-C3 는 `min-replicas-to-write` best-effort 서술과 함께 읽으면 "설정을 켜도 window 를 줄일 뿐 제거하지 못한다"는 뉘앙스가 더 명확해짐 — 필요하면 해당 서술도 별도 인용으로 추가 가능(미검증 상태로 남겨둠).
|
||||
- 추가로 봐야 할 동일 출처 페이지: Redis Sentinel 문서(failover 세부), Redis Cluster 문서(샤딩된 환경에서의 replication) — 이 문서는 두 문서로 위임하고 있음.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/redis-lua-api-determinism-replication]]` (Lua effects replication — 다른 주제, replication 메커니즘의 다른 단면)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: official-doc / Redis — TLS (Encryption in Transit)
|
||||
source_type: official-doc
|
||||
url: https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/
|
||||
archive_url:
|
||||
related_branches: [feature-redis-runtime-role-isolation-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, security, redis, mtls]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Redis — TLS (Encryption in Transit)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
- `official-doc` — Redis 공식 레퍼런스 (redis.io/docs, oss_and_stack).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-runtime-role-isolation-contract]] | production 에서 role 별 Redis 연결에 TLS(mutual TLS 포함) + 명시적 trust bundle(CA cert)을 요구하고, `port 0` + `tls-port` 로 plaintext downgrade 를 startup 에서 원천 차단하며, replication/cluster bus 채널도 별도 TLS 플래그(`tls-replication`/`tls-cluster`)로 각각 켜야 한다는 결정의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/
|
||||
- 아카이브 URL: (미등록)
|
||||
- 저자 / 조직: Redis (공식 문서, redis.io)
|
||||
- 발행일: (페이지에 명시 없음 — redis.io docs 는 버전별 업데이트, 게시일 미표기)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
Redis 서버 자체가 TLS 를 컴파일 타임 옵션으로 지원하며 기본적으로 켜져 있지 않다는 것, `port 0`/`tls-port` 조합으로 plaintext 를 완전히 차단하는 방법, mutual TLS 가 (TLS 활성화 시) 기본 동작이라는 것, 그리고 replication/cluster 채널은 client-facing `tls-port` 와 별개로 각각 켜야 한다는 것을 검증하기 위해 보관. `feature-redis-runtime-role-isolation-contract` 의 role 별 TLS 강제 결정의 1차 근거.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Getting Started] "SSL/TLS is supported by Redis starting with version 6 as an optional feature" [...] "that needs to be enabled at compile time."
|
||||
|
||||
> [§TLS listening port] "You may specify `port 0` to disable the non-TLS port completely."
|
||||
|
||||
> [§Client certificate authentication] "By default, Redis uses mutual TLS and requires clients to authenticate with a" [...] "valid certificate (authenticated against trusted root CAs specified by" [...] "`ca-cert-file` or `ca-cert-dir`)." [...] "You may use `tls-auth-clients no` to disable client authentication."
|
||||
|
||||
> [§Replication] "On the replica server side, it is necessary to specify `tls-replication yes` to" [...] "use TLS for outgoing connections to the master."
|
||||
|
||||
> [§Cluster] "When Redis Cluster is used, use `tls-cluster yes` in order to enable TLS for the" [...] "cluster bus and cross-node connections."
|
||||
|
||||
> [§Performance considerations] "TLS adds a layer to the communication stack with overheads due to writing/reading to/from an SSL connection, encryption/decryption and integrity checks." [...] "using TLS results in a decrease of the achievable throughput per Redis instance"
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| REDIS-TLS-C1 | Redis 의 SSL/TLS 지원은 버전 6부터 시작된 컴파일 타임(`BUILD_TLS=yes`) 옵션 기능이며 기본 활성 상태가 아니다 | [§Getting Started] "SSL/TLS is supported by Redis starting with version 6 as an optional feature that needs to be enabled at compile time." | `official-vendor-doc` | Redis OSS/Stack 서버 바이너리가 TLS 를 지원하려면 별도 빌드 플래그가 필요하다는 사실 | 특정 배포판(managed Redis, Docker 공식 이미지 등)이 기본적으로 TLS 지원 빌드인지 여부는 증명하지 않음 — 배포 환경별 확인 필요 |
|
||||
| REDIS-TLS-C2 | `port 0` 로 non-TLS 포트를 완전히 끄고 `tls-port` 만 열어 plaintext 연결을 원천 차단할 수 있다 | [§TLS listening port] "You may specify `port 0` to disable the non-TLS port completely." | `official-vendor-doc` | production 에서 plaintext downgrade 를 startup 설정으로 차단하는 방법의 근거 | `tls-port` 만으로 인증서 검증 강도(hostname verification 등)까지 보장한다는 뜻은 아님 — 이 문서는 hostname verification 을 언급하지 않음 |
|
||||
| REDIS-TLS-C3 | TLS 활성화 시 Redis 는 기본적으로 mutual TLS 를 사용하며 신뢰 루트 CA 로 검증된 유효 클라이언트 인증서를 요구하고, 이 요구는 `tls-auth-clients no` 로 명시적으로 꺼야만 해제된다 | [§Client certificate authentication] "By default, Redis uses mutual TLS and requires clients to authenticate with a valid certificate (authenticated against trusted root CAs specified by `ca-cert-file` or `ca-cert-dir`)." + "You may use `tls-auth-clients no` to disable client authentication." | `official-vendor-doc` | 서버가 클라이언트 인증서를 요구하는 기본값(opt-in 아니라 opt-out)이라는 것, trust-all 이 기본이 아니라는 것 | **hostname verification(서버 인증서의 CN/SAN이 접속 hostname과 일치하는지 검증)은 이 문서에 전혀 언급되지 않음** — mutual TLS(클라이언트 인증) 과 hostname verification(서버 인증 강화) 은 별개 개념이며, 후자는 클라이언트 라이브러리(Lettuce/Jedis 등) 설정에서 별도로 검증 필요. `UNSUPPORTED_DECISION` — 이 문서만으로 hostname verification 요구를 정당화할 수 없음 |
|
||||
| REDIS-TLS-C4 | replication 링크는 client-facing `tls-port`/`tls-auth-clients` 설정을 그대로 상속하지만, replica 가 master 로의 outbound 연결에 TLS 를 쓰려면 replica 측에서 별도로 `tls-replication yes` 를 명시해야 한다 | [§Replication] "On the replica server side, it is necessary to specify `tls-replication yes` to use TLS for outgoing connections to the master." | `official-vendor-doc` | replication 채널이 client 채널과 별개의 명시적 opt-in 플래그를 요구한다는 근거 | 이 프로젝트의 role(cache/coordination/session)별 replication 토폴로지가 실제로 존재하는지, Spring Data Redis 클라이언트 설정에서 이 서버 플래그가 어떻게 매핑되는지는 증명하지 않음 |
|
||||
| REDIS-TLS-C5 | Redis Cluster 사용 시 `tls-cluster yes` 를 명시해야 cluster bus 와 cross-node 연결에 TLS 가 적용된다 | [§Cluster] "When Redis Cluster is used, use `tls-cluster yes` in order to enable TLS for the cluster bus and cross-node connections." | `official-vendor-doc` | cluster bus 채널이 client-facing TLS 설정과 별개의 명시적 opt-in 플래그를 요구한다는 근거 | 이 프로젝트가 Redis Cluster 토폴로지를 실제로 쓰는지(vs standalone/Sentinel) 는 증명하지 않음 — 이 branch 의 role 분리 결정과 무관하게 배포 토폴로지 별개 확인 필요 |
|
||||
| REDIS-TLS-C6 | TLS 는 SSL 연결에 대한 write/read, 암복호화, 무결성 검사 오버헤드로 인해 Redis 인스턴스의 처리 가능 처리량(throughput)을 감소시킨다 | [§Performance considerations] "TLS adds a layer to the communication stack with overheads due to writing/reading to/from an SSL connection, encryption/decryption and integrity checks. Consequently, using TLS results in a decrease of the achievable throughput per Redis instance" | `official-vendor-doc` | production 에서 TLS 적용이 성능 trade-off 를 수반한다는 사실 인정의 근거 | 구체적 처리량 감소 수치(%, latency 등)는 이 페이지에 없음 — 문서는 GitHub 논의(#7595) 로 위임, 수치는 별도 벤치마크 필요 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- **적용 가능**: production Redis 연결에 대해 (1) `port 0` + `tls-port` 로 plaintext 리스너를 완전히 제거하고, (2) TLS 활성화 시 mutual TLS(클라이언트 인증서)가 기본이며 `tls-auth-clients no` 로만 해제 가능하다는 사실, (3) replication(`tls-replication yes`)과 cluster bus(`tls-cluster yes`)는 client-facing TLS 와 독립적으로 각각 켜야 한다는 사실, (4) TLS 가 처리량 저하 비용을 수반한다는 사실 — 이 4가지는 이 문서만으로 정당화 가능.
|
||||
- **적용 불가 / UNSUPPORTED_DECISION**: **"hostname verification"을 요구한다는 branch 결정은 이 문서만으로 정당화되지 않는다.** 이 페이지는 mutual TLS(클라이언트→서버 인증서 인증)와 CA trust bundle(`tls-ca-cert-file`)만 다루며, "서버 인증서의 hostname/SNI/CN 매칭"에 대한 서술이 전혀 없다(§본문 self-grep 결과 `hostname`, `verify` 키워드 0건). hostname verification 은 통상 클라이언트 라이브러리(Lettuce `SslOptions.verifyPeer` 등) 레벨 설정이므로, branch 가 이 요구를 유지하려면 별도의 클라이언트 라이브러리 공식 문서를 추가 근거로 확보해야 한다.
|
||||
- **trust-all 거절**: `tls-auth-clients` 는 서버가 클라이언트를 신뢰할지에 대한 스위치일 뿐, 클라이언트가 서버를 무조건 신뢰(trust-all)하지 않도록 막는 것은 클라이언트 측 설정 영역이라 이 문서 범위 밖.
|
||||
- **이 프로젝트에 적용하려면 추가 확인 필요**: Spring Data Redis / Lettuce 클라이언트가 이 서버 플래그들(`tls-port`, `tls-auth-clients`, `tls-replication`, `tls-cluster`)에 대응하는 자체 설정(`RedisStandaloneConfiguration`, `SslOptions`, hostname verification mode 등)이 실제로 무엇인지는 별도 official-doc(Spring Data Redis / Lettuce) 확보 필요.
|
||||
|
||||
## 메모
|
||||
|
||||
- 이 문서는 Redis **서버(redis-server)** 측 TLS 설정만 다룬다. 클라이언트(애플리케이션) 측 hostname verification·trust store 구성은 다른 공식 문서(Lettuce/Jedis)로 별도 보강 필요 — branch 결정의 "hostname verification" 부분은 현재 `UNSUPPORTED_DECISION` 상태.
|
||||
- `tls-replication`/`tls-cluster` 는 client 채널(`tls-port`)과 독립적인 opt-in 이므로, role 별(cache/coordination/session) 배포에서 replication 이나 cluster 를 쓰는 role 이 있다면 각각 별도로 켜야 한다는 점을 구현 가이드에 명시할 근거로 쓸 수 있음.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Redis ACL 공식 문서(role 별 ACL 격리 근거), Lettuce/Jedis SSL 옵션 공식 문서(hostname verification 근거).
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Lettuce/Jedis SSL 옵션 공식 문서 추가 수집 후보)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "official-doc / MongoDB — Retryable Writes (Once-Only Retry Semantics)"
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/core/retryable-writes/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, write-concern]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Retryable Writes (Once-Only Retry Semantics)
|
||||
|
||||
> Layer: `raw/official-docs/` — MongoDB 공식 Manual reference (`retryable-writes`) 원문 발췌.
|
||||
> `feature-mongo-runtime-baseline-contract` 의 "Mongo write 재시도 의미를 명시한다 — 드라이버의 retryable writes 가 무엇을 보장하고 무엇을 보장하지 않는지를 계약에 적고, 애플리케이션 재시도와 겹치지 않게 한다" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | Mongo write 재시도 의미를 명시한다 — 드라이버의 retryable writes 가 무엇을 보장하고 무엇을 보장하지 않는지를 계약에 적고, 애플리케이션 재시도와 겹치지 않게 한다 — 기본 활성 여부, 재시도 1회 한정 서술, 재시도 가능/불가능 연산의 정확한 목록, 배포 형태 요건의 verbatim 근거 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-skeleton 이 MongoDB 를 optional adapter 로 채택할 경우, 드라이버가 기본 제공하는 retryable writes 가 애플리케이션 재시도 로직과 겹치면 이중 재시도·의미 혼동이 생길 수 있다. 이 판단에는 (1) retryable writes 가 기본으로 켜져 있는지, (2) 재시도가 정확히 몇 번 수행되는지(무한 재시도가 아님을 확인), (3) 어떤 write 연산이 재시도 대상이고 어떤 연산(특히 multi-document update/delete, unacknowledged write concern)이 대상 밖인지, (4) 어떤 배포 형태(standalone 은 제외)에서만 지원되는지에 대한 공식 정의가 필요하다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/core/retryable-writes/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: MongoDB, Inc.
|
||||
- 발행일: MongoDB Manual (현재 버전 — 본문에 "Starting in MongoDB 6.1" 문구 포함되어 있어 6.1 이후 반영분까지 포함된 최신 manual 로 판단)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Compatibility — Deployment Topologies] "A **replica set** or **sharded cluster**. Not supported on **standalone instances**."
|
||||
|
||||
> [§Enabling Retryable Writes] "**Drivers compatible with MongoDB 4.2 and higher enable Retryable Writes by default**."
|
||||
|
||||
> [§Retryable Write Behaviors — Retry Behavior] "**By default, MongoDB retries writes once**. One retry attempts to address transient network errors and replica set elections, but not persistent network errors."
|
||||
|
||||
> [§Retryable Write Operations] "MongoDB retries the following operations if they have acknowledged write concern:"
|
||||
|
||||
> [§Retryable Write Operations — Non-retryable operations] "Writes with Write Concern `0` (unacknowledged)"
|
||||
|
||||
> [§Retryable Write Operations — Non-retryable operations] "Multi-document write operations like `updateMany()` and `deleteMany()`"
|
||||
|
||||
> [§Multi-Document Transactions] "**Transaction commit and abort operations are retryable**. Drivers retry these operations once on error, even if `retryWrites` is `false`."
|
||||
|
||||
> [§Multi-Document Transactions] "**Writes inside a transaction are not individually retryable**, regardless of the value of `retryWrites`."
|
||||
|
||||
> [§Error Handling] "Starting in MongoDB 6.1, if both the first and second attempt of a retryable write fail without a single write being performed, MongoDB returns an error with the `NoWritesPerformed` label."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-RETRYWRITE-C1 | Retryable writes 는 standalone instance 에서는 지원되지 않고, replica set 또는 sharded cluster 배포에서만 동작한다 | [§Compatibility] "A **replica set** or **sharded cluster**. Not supported on **standalone instances**." | `official-vendor-doc` | ca-skeleton 이 MongoDB 를 replica set/sharded cluster 로 배포할 때만 retryable writes 를 전제할 수 있음 | standalone 단일 인스턴스로 운영 중인 환경에서 retryable writes 관련 동작을 기대할 수 있다는 것 — 이 문장이 명시적으로 부정함 |
|
||||
| MONGO-RETRYWRITE-C2 | MongoDB 4.2 이상과 호환되는 드라이버는 retryable writes 를 기본으로 활성화한다 | [§Enabling Retryable Writes] "**Drivers compatible with MongoDB 4.2 and higher enable Retryable Writes by default**." | `official-vendor-doc` | 4.2+ 호환 드라이버를 사용하는 애플리케이션 — 별도 `retryWrites=true` 설정 없이도 활성 상태 | 4.2 미만 호환 드라이버의 기본값(이 경우 `retryWrites=true` 를 명시해야 함) — 이 문장은 4.2+ 케이스만 증명 |
|
||||
| MONGO-RETRYWRITE-C3 | MongoDB 는 기본적으로 write 를 정확히 한 번만 재시도하며, 이 1회 재시도는 일시적(transient) 네트워크 오류와 replica set election 상황을 커버할 뿐 지속적(persistent) 네트워크 오류는 커버하지 않는다 | [§Retry Behavior] "**By default, MongoDB retries writes once**. One retry attempts to address transient network errors and replica set elections, but not persistent network errors." | `official-vendor-doc` | `timeoutMS` 를 별도 설정하지 않은 기본 동작. 애플리케이션 레벨 재시도 설계 시 "드라이버가 이미 1회 재시도한다"는 전제로 중복 재시도 설계를 피해야 함 | `timeoutMS` 를 설정했을 때는 문서가 별도로 "성공/재시도불가오류/타임아웃 중 하나까지 여러 번 재시도할 수 있다"고 규정하므로, 이 문장은 그 case 를 다루지 않음 |
|
||||
| MONGO-RETRYWRITE-C4 | MongoDB 는 acknowledged write concern 을 가진 특정 연산(insert, single-document update/delete, findAndModify 계열, single-document 로만 구성된 bulkWrite)만 재시도 대상으로 삼는다 | [§Retryable Write Operations] "MongoDB retries the following operations if they have acknowledged write concern:" | `official-vendor-doc` | 위 나열된 단일 문서 단위 연산들. write concern 이 acknowledged(즉 `w != 0`)일 때만 성립 | 나열되지 않은 연산(예: multi-document 연산)이 재시도된다는 것 — 아래 C5 가 명시적으로 부정 |
|
||||
| MONGO-RETRYWRITE-C5 | Write Concern `0`(unacknowledged) 인 write 와 `updateMany()`/`deleteMany()` 같은 multi-document write 연산은 재시도되지 않는다 | [§Non-retryable operations] "Writes with Write Concern `0` (unacknowledged)" / "Multi-document write operations like `updateMany()` and `deleteMany()`" | `official-vendor-doc` | 애플리케이션이 `w:0` 로 fire-and-forget write 를 하거나 multi-document update/delete 를 쓸 때, 드라이버 재시도에 의존할 수 없다는 계약 | 애플리케이션이 이런 연산에 대해 직접 idempotent 재시도 로직을 구현해야 하는지 여부의 구체적 방법 — 이 문장은 "드라이버가 안 해준다"는 사실만 증명 |
|
||||
| MONGO-RETRYWRITE-C6 | 트랜잭션의 commit/abort 연산 자체는 `retryWrites` 값과 무관하게 오류 시 1회 재시도되지만, 트랜잭션 내부의 개별 write 는 `retryWrites` 값과 무관하게 개별적으로 재시도되지 않는다 | [§Multi-Document Transactions] "**Transaction commit and abort operations are retryable**. Drivers retry these operations once on error, even if `retryWrites` is `false`." / "**Writes inside a transaction are not individually retryable**, regardless of the value of `retryWrites`." | `official-vendor-doc` | 멀티 도큐먼트 트랜잭션을 쓰는 write 흐름 — 트랜잭션 경계(commit/abort) 재시도와 트랜잭션 내부 개별 write 재시도를 구분해서 설계해야 함 | 트랜잭션 자체가 실패했을 때 애플리케이션이 트랜잭션 전체를 재시도해야 하는지 여부의 구체적 정책 — 이 문장은 "드라이버가 commit/abort 만 1회 재시도한다"는 사실만 증명 |
|
||||
| MONGO-RETRYWRITE-C7 (보조) | MongoDB 6.1 부터, retryable write 의 1차·2차 시도가 모두 실패하고 write 가 하나도 수행되지 않았다면 `NoWritesPerformed` 라벨이 붙은 에러를 반환한다 | [§Error Handling] "Starting in MongoDB 6.1, if both the first and second attempt of a retryable write fail without a single write being performed, MongoDB returns an error with the `NoWritesPerformed` label." | `official-vendor-doc` | MongoDB 6.1+ 배포에서 `insertMany()` 등 batch 연산의 부분 실패 여부를 구분해야 하는 에러 핸들링 로직 | 6.1 미만 버전에서의 동일 상황 에러 형태 — 이 문장은 6.1+ 에만 적용됨을 명시 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `MONGO-RETRYWRITE-C1`: 배포 형태 요건 — replica set/sharded cluster 필수, standalone 불가
|
||||
- `MONGO-RETRYWRITE-C2`: MongoDB 4.2+ 호환 드라이버는 retryable writes 기본 활성
|
||||
- `MONGO-RETRYWRITE-C3`: 기본 재시도는 **정확히 1회**, transient 오류/election 만 커버
|
||||
- `MONGO-RETRYWRITE-C4`: 재시도 대상 연산 조건 — acknowledged write concern + 단일 문서 단위 연산
|
||||
- `MONGO-RETRYWRITE-C5`: 재시도 불가 연산 — unacknowledged(`w:0`) write, multi-document update/delete
|
||||
- `MONGO-RETRYWRITE-C6`: 트랜잭션 commit/abort 는 재시도되지만 트랜잭션 내부 개별 write 는 재시도되지 않음
|
||||
- `MONGO-RETRYWRITE-C7`: 6.1+ 에서 완전 실패 시 `NoWritesPerformed` 에러 라벨
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `timeoutMS` 설정 시 정확히 몇 회까지 재시도되는지의 구체적 상한 — 문서는 "성공/재시도불가오류/타임아웃 중 하나까지" 라고만 규정, 횟수 자체는 명시하지 않음
|
||||
- Spring Data MongoDB(또는 채택할 드라이버)가 `retryWrites` 옵션을 어떤 설정 키/property 로 노출하는지 — 이 자료는 서버·프로토콜 레벨 계약만 정의
|
||||
- 애플리케이션 레벨 재시도(예: idempotency key 기반 재시도)와 드라이버 레벨 retryable writes 를 어떻게 조합해야 하는지에 대한 설계 지침 — 이 자료는 드라이버가 "무엇을 하는지"만 말하고 "애플리케이션이 무엇을 더 해야 하는지"는 말하지 않음
|
||||
- `local` 데이터베이스로의 write 에 대한 retryable writes 비활성화 권고의 정확한 근거 문장(이번 raw 에는 verbatim 인용/self-grep 미포함 — 별도 확인 필요)
|
||||
- **ca-skeleton 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-skeleton 이 채택할 MongoDB 드라이버(Java/Reactive Streams 또는 Spring Data MongoDB)가 실제로 4.2+ 호환인지, 그래서 별도 `retryWrites=true` 설정 없이 기본 활성인지 확인 필요
|
||||
- branch 의 §구현 가이드에서 애플리케이션 레벨 재시도(예: idempotent write 재시도)를 드라이버의 1회 재시도와 어떻게 겹치지 않게 설계할지는 이 자료가 아니라 branch 자체 결정 사항
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- WebFetch 1차 시도는 원문 링크를 markdown `[text](url)` 문법으로 감싼 형태로 반환해 self-grep 인용에 마크다운 아티팩트가 섞일 위험이 있었다. 재요청 시 "링크를 순수 텍스트로" 명시해 재fetch, 브래킷 없는 깨끗한 인용을 확보했다.
|
||||
- G2 재검증 1차 시도에서 4개 인용("A replica set or sharded cluster...", "Drivers compatible with MongoDB 4.2...", "By default, MongoDB retries writes once...", "Transaction commit and abort operations..."/"Writes inside a transaction...")이 `grep -nF` 불일치로 확인됨 — WebFetch 결과의 `**볼드**` 강조 마커를 인용에서 임의로 제거했기 때문. 원문 그대로 `**` 를 보존하도록 정정하고 재검증 통과.
|
||||
- `local` 데이터베이스 write 에 대한 retryable writes 비활성화 권고, `timeoutMS` 재시도 상한의 정확한 조건 문장은 이번 라운드에서 verbatim 미수집 — 필요 시 재수집.
|
||||
- MongoDB 드라이버(Java/Reactive)의 `retryWrites` 구체 설정 API 는 별도 official-doc 후보.
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/write-concern-mongodb-official]] (write concern 정의 — 본 문서의 "acknowledged write concern" 조건과 직결)
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/spring-data-mongodb-index-management-official]] (같은 branch cluster 의 다른 MongoDB 공식 근거)
|
||||
- 인용하는 branch: [[raw/branch-notes/feature-mongo-runtime-baseline-contract]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
@@ -7,9 +7,9 @@ status: raw
|
||||
confidence: high
|
||||
tags: [ca-skeleton, runtime, health, lifecycle, spring-boot, actuator]
|
||||
related_projects: [ca-skeleton-operational-contract]
|
||||
related_branches: [feature-runtime-health-lifecycle-contract, feature-management-actuator-security-contract]
|
||||
related_branches: [feature-runtime-health-lifecycle-contract, feature-management-actuator-security-contract, feature-capability-provider-selection-contract]
|
||||
created: 2026-05-22
|
||||
last_reviewed: 2026-05-27
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring Boot Actuator — Health Endpoint Groups (Liveness / Readiness / Startup)
|
||||
@@ -23,6 +23,7 @@ last_reviewed: 2026-05-27
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-runtime-health-lifecycle-contract]] | actuator `/actuator/health/liveness` + `/actuator/health/readiness` group 채택 + readiness 에 외부 dependency 포함 정책 결정 |
|
||||
| [[raw/branch-notes/feature-management-actuator-security-contract]] | health endpoint 의 prod 노출 + group 별 detail 노출 정책 결정 |
|
||||
| [[raw/branch-notes/feature-capability-provider-selection-contract]] | 활성화된 provider 만 readiness 에 반영한다 — optional cache 실패는 pod 를 unready 로 만들지 않고, session/idempotency/필수 lock 실패는 unready 로 만든다. 판정 주체는 capability descriptor 이지 bean 존재가 아니다 (StatusAggregator 기반 aggregation + health group include/exclude 메커니즘 근거) |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
@@ -57,6 +58,18 @@ ca-tmpl `feature-runtime-health-lifecycle-contract` 는 actuator 의 **liveness
|
||||
|
||||
> needs-confirmation: 이전 raw 본문에서 인용된 "Custom `HealthIndicator` beans can be assigned to groups using `management.endpoint.health.group.<name>.include`. By default, the readiness group includes the `readinessState` indicator only — application liveness and readiness must NOT depend on external systems by Spring Boot's default model." 문장은 2026-05-27 WebFetch 결과에서 **단일 문장 verbatim 으로 확인 불가**. `management.endpoint.health.group.<name>.include` property 자체는 reference 의 다른 위치에 존재하나, "must NOT depend on external systems" 라는 정책 문장의 verbatim 출처는 별도 fetch 필요. 따라서 본 raw 의 직접 증명 범위에서 제외.
|
||||
|
||||
**2026-07-28 추가 수집 (`feature-capability-provider-selection-contract` 근거, curl 직접 fetch → self-grep 검증 완료, 위 SB-HEALTH-C8 gap 부분 해소):**
|
||||
|
||||
> [§actuator.endpoints.health.groups] "To create a health indicator group, you can use the `management.endpoint.health.group.<name>` property and specify a list of health indicator IDs to include or exclude."
|
||||
|
||||
> [§actuator.endpoints.health] "By default, the final system health is derived by a `StatusAggregator`, which sorts the statuses from each `HealthIndicator` based on an ordered list of statuses."
|
||||
|
||||
> [§actuator.endpoints.health.groups] "By default, groups inherit the same `StatusAggregator` and `HttpCodeStatusMapper` settings as the system health."
|
||||
|
||||
> [§actuator.endpoints.kubernetes-probes.external-state] "By default, Spring Boot does not add other health indicators to these groups."
|
||||
|
||||
> [§actuator.endpoints.kubernetes-probes.external-state] "Other external systems might not be essential to the application (the application could have circuit breakers and fallbacks), in which case they definitely should not be included."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
@@ -68,7 +81,11 @@ ca-tmpl `feature-runtime-health-lifecycle-contract` 는 actuator 의 **liveness
|
||||
| SB-HEALTH-C5 | `AvailabilityChangeEvent.publish(eventPublisher, ex, LivenessState.BROKEN)` 패턴으로 application code 가 명시적으로 state 전이 publish 가능 | [§features.spring-application.application-availability] "We can also update the state of the application, when the application breaks and cannot recover: `AvailabilityChangeEvent.publish(this.eventPublisher, ex, LivenessState.BROKEN);`" | `official-vendor-doc` | LivenessState / ReadinessState 전이 시점 명시 제어 | exception handler 외 다른 위치 (예: scheduled task) 에서의 published pattern 은 본 인용 범위 밖 |
|
||||
| SB-HEALTH-C6 | 애플리케이션 instance 의 readiness 가 unready 이면 Kubernetes 는 해당 instance 로 traffic routing 안 함 | [§actuator.endpoints.kubernetes-probes.external-state] "If the readiness state of an application instance is unready, Kubernetes does not route traffic to that instance." | `official-vendor-doc` | Kubernetes 환경의 Spring Boot Actuator readiness group | "ready → unready 전이" 의 정확한 propagation 지연 (kubelet probe period × failureThreshold) 은 K8s probe 측 변수 — 별도 |
|
||||
| SB-HEALTH-C7 | health group 은 `CompositeHealthContributor` 를 include / exclude 가능 | [§actuator.endpoints.health.groups] "A health group can also include/exclude a `CompositeHealthContributor`." | `official-vendor-doc` | health group 구성 시 | 외부 dependency 를 readiness 에 포함시키는 권장 / 비권장 정책은 본 인용 범위 밖 (needs-confirmation 참조) |
|
||||
| SB-HEALTH-C8 | "default readiness group 은 외부 의존성을 포함하지 않는다 / 포함해서는 안 된다" 라는 **단일 문장 verbatim** 은 본 capture 에서 확보 못 함 | (부재 자체가 claim) | `needs-confirmation` | readiness group default 멤버 정책 | 외부 의존성을 readiness 에 추가해도 안전하다는 뜻도 아님 — Spring Boot reference 의 별도 섹션 fetch 필요 |
|
||||
| SB-HEALTH-C8 | "default readiness group 은 외부 의존성을 포함하지 않는다 / 포함해서는 안 된다" 라는 **단일 문장 verbatim** 은 본 capture 에서 확보 못 함 | (부재 자체가 claim) | `needs-confirmation` | readiness group default 멤버 정책 | 외부 의존성을 readiness 에 추가해도 안전하다는 뜻도 아님 — Spring Boot reference 의 별도 섹션 fetch 필요 — **2026-07-28: C11/C12 로 부분 해소 (아래)** |
|
||||
| SB-HEALTH-C9 | health indicator group 은 `management.endpoint.health.group.<name>` property 로 health indicator ID 목록을 include/exclude 지정해 생성한다 | [§actuator.endpoints.health.groups] "To create a health indicator group, you can use the `management.endpoint.health.group.<name>` property and specify a list of health indicator IDs to include or exclude." | `official-vendor-doc` | 모든 custom health group (liveness/readiness 포함) 생성 메커니즘 | liveness/readiness 그룹이 startup 시 자동으로 이 매커니즘을 거쳐 구성된다는 순서·시점까지는 본 인용 범위 밖 |
|
||||
| SB-HEALTH-C10 | 전체 system health(및 각 group)는 기본적으로 `StatusAggregator` 가 각 `HealthIndicator` 의 status 를 정렬된 순서 목록 기준으로 정렬하여 도출한다 — 첫 항목이 overall status | [§actuator.endpoints.health] "By default, the final system health is derived by a `StatusAggregator`, which sorts the statuses from each `HealthIndicator` based on an ordered list of statuses." | `official-vendor-doc` | global `/actuator/health` 및 모든 health group 의 aggregation 규칙 | 개별 capability 의 실패가 어떤 Status(DOWN/OUT_OF_SERVICE 등) 로 매핑되어야 하는지는 본 인용 범위 밖 — application 코드 책임 |
|
||||
| SB-HEALTH-C11 | health group 은 기본적으로 system 전체와 동일한 `StatusAggregator`/`HttpCodeStatusMapper` 설정을 상속한다 — group 마다 별도 override 가능 | [§actuator.endpoints.health.groups] "By default, groups inherit the same `StatusAggregator` and `HttpCodeStatusMapper` settings as the system health." | `official-vendor-doc` | readiness/liveness group 포함 모든 custom group 의 aggregation 상속 규칙 | readiness group 이 실제로 override 설정을 갖는지 여부는 이 인용만으로 알 수 없음 — 프로젝트별 설정 확인 필요 |
|
||||
| SB-HEALTH-C12 | readiness/liveness health group 은 기본적으로 다른 health indicator 를 추가하지 않으며, 외부 시스템을 readiness 에 포함할지는 "필수(essential)한가"를 기준으로 애플리케이션 개발자가 판단해야 한다 — non-essential(예: fallback 가능한 optional cache)은 포함하지 말아야 함 | [§actuator.endpoints.kubernetes-probes.external-state] "By default, Spring Boot does not add other health indicators to these groups." + "Other external systems might not be essential to the application (the application could have circuit breakers and fallbacks), in which case they definitely should not be included." | `official-vendor-doc` | readiness probe 에 캐시·비필수 외부 시스템을 포함할지 판단하는 일반 기준 | "필수/비필수"를 자동으로 구분해주는 Spring Boot 메커니즘은 없음 — capability descriptor 로 판정하는 것은 ca-skeleton 의 설계 결정(본 branch), 공식 문서가 강제하는 구현 방식은 아님 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
@@ -77,15 +94,21 @@ ca-tmpl `feature-runtime-health-lifecycle-contract` 는 actuator 의 **liveness
|
||||
- `SB-HEALTH-C2` ~ `C5`: LivenessState / ReadinessState 의 정의와 ApplicationAvailability + AvailabilityChangeEvent.publish 모델
|
||||
- `SB-HEALTH-C6`: Kubernetes 가 unready 인스턴스로 traffic routing 안 함
|
||||
- `SB-HEALTH-C7`: health group 의 include / exclude 메커니즘 존재
|
||||
- `SB-HEALTH-C9`: health group 생성은 `management.endpoint.health.group.<name>` property 로 이뤄짐
|
||||
- `SB-HEALTH-C10`: system health(및 group)의 aggregation 은 기본적으로 `StatusAggregator` 가 정렬된 상태 목록 기준으로 도출
|
||||
- `SB-HEALTH-C11`: health group 은 기본적으로 system 과 동일한 `StatusAggregator`/`HttpCodeStatusMapper` 를 상속
|
||||
- `SB-HEALTH-C12`: readiness/liveness group 은 기본적으로 다른 indicator 를 추가하지 않으며, non-essential 외부 시스템은 포함하지 말아야 한다는 판단 기준이 명시됨
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `SB-HEALTH-C8`: default readiness group 멤버 (readinessState 만 포함 vs 외부 의존성 포함 정책) verbatim
|
||||
- `SB-HEALTH-C8`: default readiness group 멤버 (readinessState 만 포함 vs 외부 의존성 포함 정책) verbatim — **2026-07-28: `SB-HEALTH-C12` 로 정책 판단 기준("essential 한가")은 확보. 단 "capability descriptor 로 판정한다"는 구현 방식 자체는 여전히 ca-skeleton 의 설계 결정이며 본 자료가 증명하지 않음.**
|
||||
- startup probe 를 Spring Boot 가 dedicated group 으로 제공하는지 (현재 인용 범위: liveness + readiness 만 명시)
|
||||
- HealthIndicator 의 per-indicator timeout 제어 메커니즘 (endpoint-level vs indicator-level)
|
||||
- graceful shutdown 시 readiness 가 자동 DOWN 으로 전환되는 mechanism 의 verbatim 출처 (Application Availability 페이지 본문에는 명시 부재 — 2026-05-27 fetch 결과)
|
||||
- "판정 주체는 capability descriptor 이지 bean 존재가 아니다" — 이는 Spring Boot 문서가 아닌 ca-skeleton 자체 설계 결정. 본 자료는 group aggregation·include/exclude 메커니즘과 "essential 여부로 판단하라"는 일반 기준만 증명
|
||||
- **내 프로젝트 (ca-tmpl) 에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-tmpl 이 readiness group 에 DB / outbox broker 를 포함시키려면 `management.endpoint.health.group.readiness.include=readinessState,db,...` 명시 설정 필요 — 이 property 의 verbatim 출처 별도 fetch
|
||||
- ca-tmpl 의 startup endpoint (`/actuator/health/startup`) 가 manually 구성된 health group 인지, 아니면 별도 endpoint 인지 (Spring Boot 가 dedicated group 제공 여부 미확정)
|
||||
- graceful shutdown ↔ readiness DOWN 자동 전환의 공식 메커니즘 (Application Availability 또는 별도 graceful-shutdown reference 페이지)
|
||||
- `feature-capability-provider-selection-contract`: capability descriptor 의 readiness 기여 여부를 실제로 `management.endpoint.health.group.readiness.include/exclude` 로 배선하는 구체 구현(어떤 capability 를 이 property 목록에 넣을지)은 이 자료가 아니라 branch 의 §구현 가이드에서 결정 — 본 자료는 "그렇게 include/exclude 할 수 있는 메커니즘이 존재한다"까지만 증명
|
||||
|
||||
## ca-tmpl 함의 (내 프로젝트 해석)
|
||||
|
||||
@@ -101,8 +124,8 @@ ca-tmpl `feature-runtime-health-lifecycle-contract` 는 actuator 의 **liveness
|
||||
## 메모 / Notes
|
||||
|
||||
- 2026-05-27 재검증: Application Availability 페이지 verbatim 확보. Endpoints 페이지의 Kubernetes Probes 섹션 verbatim 확보. 단 "default readiness group 멤버 + 외부 의존성 정책" 단일 문장 verbatim 미확보 → `SB-HEALTH-C8` 로 분리.
|
||||
- 2026-07-28 재수집 (`feature-capability-provider-selection-contract` 근거): WebFetch 의 소형 모델 요약이 paraphrase 되어 verbatim 요건을 충족하지 못해, `curl` 로 원문 HTML 을 직접 fetch(HTTP 200, 235KB) 후 Python `html.parser` 로 텍스트만 추출해 self-grep 대조. `management.endpoint.health.group.<name>` property, `StatusAggregator` aggregation, group 의 aggregator 상속, "essential 하지 않은 외부 시스템은 포함하지 말 것" 판단 기준의 verbatim 4개 신규 확보 (`SB-HEALTH-C9`~`C12`). `SB-HEALTH-C8` 의 "포함 여부는 개발자 판단" 부분은 해소되었으나, "capability descriptor 로 그 판단을 자동화한다"는 부분은 여전히 ca-skeleton 자체 설계(본 자료가 증명 안 함).
|
||||
- 다음 fetch 후보:
|
||||
- `https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.health.groups` (group include property verbatim)
|
||||
- `https://docs.spring.io/spring-boot/reference/features/graceful-shutdown.html` (readiness 자동 DOWN 전이)
|
||||
|
||||
## Related / 관련
|
||||
@@ -114,6 +137,7 @@ ca-tmpl `feature-runtime-health-lifecycle-contract` 는 actuator 의 **liveness
|
||||
- 인용하는 branch:
|
||||
- [[raw/branch-notes/feature-runtime-health-lifecycle-contract]]
|
||||
- [[raw/branch-notes/feature-management-actuator-security-contract]]
|
||||
- [[raw/branch-notes/feature-capability-provider-selection-contract]]
|
||||
- 인용하는 project:
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: official-doc / Spring Boot — Application Availability (Liveness/Readiness) & SpringApplication Lifecycle
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-boot/reference/features/spring-application.html
|
||||
archive_url:
|
||||
related_branches: [feature-capability-provider-selection-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, runtime, spring-boot]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring Boot — Application Availability (Liveness/Readiness) & SpringApplication Lifecycle
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-capability-provider-selection-contract]] | capability 토폴로지 검증은 startup 에서 fail-fast 로 수행하고, 검증 실패는 컨텍스트 기동을 거부한다 — Spring Boot 자체의 애플리케이션 이벤트 순서(`ApplicationStartedEvent` → runner 실행, `ApplicationFailedEvent` on startup exception)가 근거. 또한 application availability state (`LivenessState`/`ReadinessState`)는 컨텍스트 refresh 완료 **이후**에만 변경되는 런타임 신호이며, startup 검증의 대체물이 아니라는 결정의 근거. |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-boot/reference/features/spring-application.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: VMware / Spring Boot 프로젝트 (공식 레퍼런스)
|
||||
- 발행일: 확인 안 됨 (WebFetch 결과 상단에 "SpringApplication :: Spring Boot 4.1.0" 표시 — 문서가 Spring Boot 4.1.0 레퍼런스 시점 버전임을 시사)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-capability-provider-selection-contract` branch 는 capability 토폴로지 검증을 "startup 시 fail-fast"로 둘지, 아니면 Spring Boot 의 `ApplicationAvailability` (readiness/liveness) 신호로 대체할지를 결정해야 한다. 본 문서는 Spring Boot 공식 레퍼런스가 (1) Liveness/Readiness state 를 컨텍스트 refresh 완료 이후의 런타임 신호로 정의하고, (2) `ApplicationRunner`/`CommandLineRunner` 도 "애플리케이션이 이미 시작된 후" 실행되며, (3) startup 중 예외는 별도의 `ApplicationFailedEvent` 경로로 분리된다는 것을 직접 진술하는지 확인하기 위해 발췌했다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Application Availability — Liveness State] "Indicates whether the application's internal state allows it to work correctly. A broken Liveness state signals that the application cannot recover and should be restarted by infrastructure (e.g., Kubernetes)."
|
||||
|
||||
> [§Application Availability — Readiness State] "Indicates whether the application is ready to handle traffic. A failing Readiness state tells the platform not to route traffic to the application, typically during startup or when the application is too busy."
|
||||
|
||||
> [§Application Events and Listeners — 이벤트 순서 목록 6번] "**AvailabilityChangeEvent** (LivenessState.CORRECT) – Application is live"
|
||||
|
||||
> [§ApplicationRunner and CommandLineRunner] "If you need to run specific code once `SpringApplication` has started, implement `ApplicationRunner` or `CommandLineRunner`:"
|
||||
|
||||
> [§Application Events and Listeners — 이벤트 순서 목록 9번] "**ApplicationFailedEvent** – If there's an exception on startup"
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SB-AVAIL-C1 | Liveness State 는 애플리케이션 내부 상태가 정상 동작 가능한지를 나타내며, broken 상태는 "복구 불가 → 인프라가 재시작해야 함"을 의미하는 런타임 헬스 신호다. | [§Application Availability] "Indicates whether the application's internal state allows it to work correctly. A broken Liveness state signals that the application cannot recover and should be restarted by infrastructure (e.g., Kubernetes)." | `official-vendor-doc` | LivenessState 의 의미론 — "복구 불가능해진 이미 살아있는 프로세스"를 인프라가 재시작하도록 신호하는 용도 | LivenessState/ReadinessState 가 startup-time capability 토폴로지 정합성 검증에 적합한 메커니즘이라거나 Spring Boot 가 그 용도로 권장한다는 것 — 문서는 이 용도를 언급하지 않음 |
|
||||
| SB-AVAIL-C2 | Readiness State 는 트래픽 처리 준비 여부를 나타내며, failing 상태는 "아직 트래픽 라우팅하지 말라"는 뜻이고 전형적으로 startup 도중이나 과부하 시 사용된다. | [§Application Availability] "Indicates whether the application is ready to handle traffic. A failing Readiness state tells the platform not to route traffic to the application, typically during startup or when the application is too busy." | `official-vendor-doc` | Readiness = "아직 트래픽 라우팅하지 마라"는 신호이며, 문서 스스로 startup 도중 상태로 명시 — 즉 컨텍스트는 이미 살아있고 프로세스도 떠 있는 상태에서의 트래픽 게이팅 | Readiness=REFUSING_TRAFFIC 설정이 컨텍스트 기동 자체를 거부(refresh 실패/프로세스 종료)하는 것과 동등하다는 것 — 트래픽만 안 보낼 뿐 컨텍스트는 이미 완성되어 떠 있음 |
|
||||
| SB-AVAIL-C3 | 애플리케이션 이벤트 순서에서 `AvailabilityChangeEvent(LivenessState.CORRECT)` 는 6번째 이벤트로, `ApplicationStartedEvent`(5번, "컨텍스트 refresh 이후, runner 이전") 다음에 발생한다. | [§Application Events and Listeners] "**AvailabilityChangeEvent** (LivenessState.CORRECT) – Application is live" (목록 6번, 5번 항목 "**ApplicationStartedEvent** – After context refresh, before runners" 뒤) | `official-vendor-doc` | Liveness 신호가 컨텍스트 refresh 완료 **이후**에만 발생한다는 시점 근거 — refresh 도중의 bean 초기화 실패는 이 이벤트 이전 단계에서 이미 별도 경로(ApplicationFailedEvent)로 분기됨 | 문서가 "capability 토폴로지 검증"이라는 개념 자체를 언급하거나 그 검증을 refresh 단계에 두라고 권고한다는 것 — 이는 이벤트 순서로부터의 추론이지 원문의 명시적 진술이 아님 |
|
||||
| SB-AVAIL-C4 | `ApplicationRunner`/`CommandLineRunner` 는 "`SpringApplication` 이 이미 시작된 후(once ... has started)" 실행하도록 설계된 확장점이다. | [§ApplicationRunner and CommandLineRunner] "If you need to run specific code once `SpringApplication` has started, implement `ApplicationRunner` or `CommandLineRunner`:" | `official-vendor-doc` | Runner 실행 시점이 컨텍스트 refresh 완료 이후(모든 싱글턴 빈이 이미 완전히 wiring된 시점)라는 것 — capability 토폴로지 검증을 Runner 에 두면 "이미 활성화된 컨텍스트"를 사후 점검하는 것이 됨 | Runner 가 예외를 던졌을 때 프로세스가 종료되는지 여부 — 이 문서 발췌에는 명시 없음 (Spring 일반 지식이지 이 페이지의 verbatim 진술 아님) |
|
||||
| SB-AVAIL-C5 | 애플리케이션 이벤트 순서에서 startup 중 예외는 정상 경로(Started→Live→Ready→AcceptingTraffic)와 분리된 `ApplicationFailedEvent` 로 별도 처리된다. | [§Application Events and Listeners] "**ApplicationFailedEvent** – If there's an exception on startup" (목록 9번) | `official-vendor-doc` | Spring Boot 자체의 라이프사이클 모델이 startup 중 예외를 "정상 가용성 상태 진행과는 별개의 실패 경로"로 취급한다는 것 — fail-fast 의 이벤트 모델적 근거 | 이 이벤트가 정확히 어떤 종료 코드/JVM exit 동작으로 이어지는지에 대한 구체 메커니즘 — 이 발췌 페이지는 `ExitCodeGenerator` 인터페이스만 별도로 언급할 뿐 `ApplicationFailedEvent` 와의 직접 연결을 verbatim 으로 진술하지 않음 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SB-AVAIL-C1`/`SB-AVAIL-C2`: LivenessState/ReadinessState 는 "이미 살아있는 프로세스"에 대한 런타임 헬스·트래픽 게이팅 신호이며, 정의상 컨텍스트가 이미 완성된 이후에 의미를 갖는다.
|
||||
- `SB-AVAIL-C3`/`SB-AVAIL-C4`: 이벤트 순서상 Liveness 신호 변경과 Runner 실행은 모두 `ApplicationStartedEvent`("After context refresh, before runners") **이후**에 위치한다 — 즉 컨텍스트 refresh 가 이미 성공적으로 끝난 다음의 일이다.
|
||||
- `SB-AVAIL-C5`: startup 중 예외는 정상 가용성 상태 전이와 분리된 별도 실패 이벤트(`ApplicationFailedEvent`)로 취급된다.
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- "capability 토폴로지 검증"이라는 ca-skeleton 고유 개념 자체 — Spring Boot 문서는 이 용어를 전혀 언급하지 않는다. 위 5개 claim 은 모두 **일반적인 SpringApplication 라이프사이클 이벤트 순서**에 대한 진술이며, 이로부터 "그러므로 capability 토폴로지 검증은 refresh 단계에서 fail-fast 로 해야 한다"는 결론은 **branch 저자의 추론**이지 원문의 직접 진술이 아니다.
|
||||
- Runner 가 예외를 던질 때 프로세스 종료/exit code 동작의 구체 메커니즘.
|
||||
- Readiness=REFUSING_TRAFFIC 이 컨텍스트 기동 실패와 "동등"하다는 주장 — 오히려 원문은 정반대(Readiness 는 컨텍스트가 이미 뜬 상태의 트래픽 게이팅)를 보여준다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 의 실제 컨텍스트 초기화 훅(`@PostConstruct`, `SmartInitializingSingleton`, `ApplicationListener<ContextRefreshedEvent>` 등 refresh 단계 내 실행 지점 중 정확히 어디에 capability 토폴로지 검증을 배치할지)은 이 문서에 없다 — 별도 Spring Framework `ApplicationContext` refresh 문서 또는 실제 구현으로 검증 필요.
|
||||
- `ApplicationFailedEvent` 발생 시 Spring Boot 가 `SpringApplication.run()` 호출자에게 예외를 어떻게 전파하는지(예: `run()` 이 예외를 던지는지)는 본 발췌에 없음.
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 해석 후보 (미검증, branch 저자 추론 — 이 문서가 직접 말한 것 아님): "이벤트 순서(ApplicationStartedEvent → runner → AvailabilityChangeEvent)가 보여주는 건 Liveness/Readiness 와 Runner 모두 **컨텍스트가 이미 완성된 이후**라는 사실이므로, capability 토폴로지처럼 '컨텍스트가 완성되기 전에 막아야 하는' 검증은 이 두 메커니즘이 아니라 컨텍스트 refresh 도중(예: bean 초기화 또는 `BeanFactoryPostProcessor`/`SmartInitializingSingleton`)에 두어야 한다"는 결론은 이 문서의 이벤트 순서 사실에서 **추론**한 것이지, 문서가 "capability 검증을 여기에 두라"고 명시적으로 권고한 것이 아니다. wiki/concepts 로 승격 시 이 구분을 명확히 유지할 것.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Spring Framework 공식 문서의 `ApplicationContext.refresh()` 생명주기(`BeanFactoryPostProcessor`, `SmartInitializingSingleton`, `ContextRefreshedEvent`) — 이 페이지(SpringApplication)는 Spring Boot 레벨 이벤트만 다루고, refresh 내부의 세부 콜백 순서는 다루지 않는다.
|
||||
- WebFetch 도구 특성상 원본 HTML 을 AI 모델이 마크다운으로 정리한 결과다. 원문 그대로 보존을 프롬프트로 요청했고 self-grep 은 이 정리된 텍스트 기준으로 통과했으나, Spring 공식 페이지의 byte-for-byte HTML 과 100% 동일하다는 보장은 없다 — 재검증 시 페이지를 직접 열어 대조 권장.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Spring Framework `ApplicationContext` 생명주기 공식 문서 추가 수집 후보)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: official-doc / Spring Boot Condition Annotations — @ConditionalOnProperty, matchIfMissing, Evaluation Order
|
||||
source_type: official-doc
|
||||
status: raw
|
||||
confidence: high
|
||||
url: https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html
|
||||
archive_url:
|
||||
related_branches: [feature-capability-provider-selection-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, architecture, spring-boot]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring Boot Condition Annotations — @ConditionalOnProperty / matchIfMissing / Evaluation Order
|
||||
|
||||
> Layer: `raw/official-docs/` — Spring Boot 4.1.0 reference "Creating Your Own Auto-configuration" 발췌. `feature-capability-provider-selection-contract` 브랜치의 "`matchIfMissing=true` 금지" 결정과 "조건 평가 시점의 한계"를 뒷받침하는 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-capability-provider-selection-contract]] | production capability 활성화를 `@ConditionalOnProperty` 로 게이트할 때 `matchIfMissing=true` 를 금지하는 결정, 그리고 조건 평가 시점/순서 의존성의 한계 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Spring Team (spring-projects / Broadcom)
|
||||
- 발행일: 미상 — living reference 문서. 페이지 내 버전 배지 기준 **Spring Boot 4.1.0** (Stable)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-capability-provider-selection-contract` 는 production capability 활성화 게이트에 `@ConditionalOnProperty` 를 쓰되 `matchIfMissing=true` 를 금지한다 (속성 미설정 = 비활성 유지). 이 raw 자료는 (1) `@ConditionalOnProperty` 의 공식 정의와 `matchIfMissing` 이 실제로 존재하는 옵션 attribute 라는 근거, (2) `@Conditional` 계열 annotation 전반의 평가 시점·순서 의존성 한계(원문은 이를 근거로 bean 존재 기반 조건을 auto-configuration class 로 한정 권고)를 보존한다. 후자는 project 가 이미 내린 "활성화는 bean 이름이 아니라 typed descriptor 기반 provider 선택으로만" 결정과도 연결된다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Understanding Auto-configured Beans] "Additional @Conditional annotations are used to constrain when the auto-configuration should apply. Usually, auto-configuration classes use @ConditionalOnClass and @ConditionalOnMissingBean annotations."
|
||||
|
||||
> [§Property Conditions] "The @ConditionalOnProperty annotation lets configuration be included based on a Spring Environment property. Use the prefix and name attributes to specify the property that should be checked. By default, any property that exists and is not equal to false is matched."
|
||||
|
||||
> [§Property Conditions] "There is also a dedicated @ConditionalOnBooleanProperty annotation specifically made for boolean properties. With both annotations you can also create more advanced checks by using the havingValue and matchIfMissing attributes."
|
||||
|
||||
> [§Bean Conditions] "You need to be very careful about the order in which bean definitions are added, as these conditions are evaluated based on what has been processed so far."
|
||||
|
||||
> [§Bean Conditions] "For this reason, we recommend using only @ConditionalOnBean and @ConditionalOnMissingBean annotations on auto-configuration classes (since these are guaranteed to load after any user-defined bean definitions have been added)."
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SB-COND-C1 | `@ConditionalOnProperty` 는 Spring `Environment` 의 property 존재 여부·값을 기준으로 configuration 포함 여부를 결정하며, `prefix`/`name` 으로 지정한 property 가 **존재하고 값이 `"false"` 가 아니면** 기본적으로 match 된다 | [§Property Conditions] "The @ConditionalOnProperty annotation... By default, any property that exists and is not equal to false is matched." | `official-vendor-doc` | `@ConditionalOnProperty` 의 기본(=`havingValue`/`matchIfMissing` 미지정) 매칭 규칙 | property 가 **부재**할 때의 기본 동작(=matchIfMissing 미지정 시 기본값)은 이 문장이 다루지 않음 — 이 문장은 "존재 시" 규칙만 정의 |
|
||||
| SB-COND-C2 | `matchIfMissing` 은 `havingValue` 와 함께 "more advanced checks" 를 만들 때 쓰는 실존하는 attribute 다 | [§Property Conditions] "With both annotations you can also create more advanced checks by using the havingValue and matchIfMissing attributes." | `official-vendor-doc` | `matchIfMissing` 이라는 옵션이 Spring Boot 공식 API 에 실재한다는 사실 | **이 페이지는 `matchIfMissing` 의 기본값이나 매칭 semantics(속성 부재 시 true/false 중 무엇으로 처리되는지)를 설명하지 않는다** — self-grep 결과 이 페이지 전체에서 `matchIfMissing` 단어는 이 한 문장에서만 등장(1회). 따라서 이 자료만으로 "matchIfMissing=true 를 금지해야 한다"는 명제를 공식 권고로 증명할 수 없음 — 이는 project/branch 의 자체 설계 판단이다 |
|
||||
| SB-COND-C3 | `@Conditional` 계열 annotation(특히 `@ConditionalOnBean`/`@ConditionalOnMissingBean`) 은 "지금까지 처리된 것"을 기준으로 평가되므로 bean definition 이 추가되는 **순서**에 매우 주의해야 한다 | [§Bean Conditions] "You need to be very careful about the order in which bean definitions are added, as these conditions are evaluated based on what has been processed so far." | `official-vendor-doc` | bean 존재/부재 기반 조건(`@ConditionalOnBean`/`@ConditionalOnMissingBean`)의 순서 의존성 일반 | 이 경고는 원문에서 "Bean Conditions" 섹션에 위치 — "Property Conditions" 섹션(`@ConditionalOnProperty`)에는 동일한 순서 의존 경고 문장이 없다. Property 기반 조건에도 동일한 순서 취약성이 있다고 이 인용만으로 단정할 수 없음 |
|
||||
| SB-COND-C4 | 순서 의존 위험 때문에 공식 문서는 `@ConditionalOnBean`/`@ConditionalOnMissingBean` 을 **auto-configuration 클래스에서만** 사용하라고 권고한다 — auto-configuration 클래스는 사용자 정의 bean definition 이 모두 추가된 후에 로드되는 것이 보장되기 때문 | [§Bean Conditions] "For this reason, we recommend using only @ConditionalOnBean and @ConditionalOnMissingBean annotations on auto-configuration classes (since these are guaranteed to load after any user-defined bean definitions have been added)." | `official-vendor-doc` | bean 존재/이름 기반 판정의 순서 취약성 근거 — "bean 이름 기반 판정 제거" 결정을 뒷받침하는 데 사용 가능 | `@ConditionalOnProperty` 같은 property 기반 조건에도 이 권고가 확장 적용된다는 뜻은 아님(범위는 명시적으로 bean 존재 기반 조건) |
|
||||
| SB-COND-C5 | auto-configuration class 는 `@AutoConfiguration`(= meta-annotated `@Configuration`) + 추가 `@Conditional` 로 적용 조건을 제한하며, 보통은 `@ConditionalOnClass` + `@ConditionalOnMissingBean` 조합을 쓴다 | [§Understanding Auto-configured Beans] "Additional @Conditional annotations are used to constrain when the auto-configuration should apply. Usually, auto-configuration classes use @ConditionalOnClass and @ConditionalOnMissingBean annotations." | `official-vendor-doc` | 이 문서가 다루는 condition annotation 의 전형적 사용 범위(=auto-configuration class 의 적용 여부 제약) | `@ConditionalOnProperty` 가 이 "usual" 조합에 포함된다는 뜻은 아님(원문이 명시한 usual 조합은 OnClass+OnMissingBean) — capability provider 선택처럼 property 기반 게이트를 쓰는 것이 이 문서의 표준 패턴이라는 주장의 근거로는 쓸 수 없음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `SB-COND-C1`: `@ConditionalOnProperty` 의 "존재 + 값≠false" 기본 매칭 규칙
|
||||
- `SB-COND-C2`: `matchIfMissing` 이 실존하는 공식 attribute 라는 사실 (semantics 는 아님)
|
||||
- `SB-COND-C3`: bean 존재 기반 조건은 bean definition 추가 순서에 의존하는 평가 시점 한계가 있음
|
||||
- `SB-COND-C4`: 그 한계 때문에 `@ConditionalOnBean`/`@ConditionalOnMissingBean` 은 auto-configuration class 사용으로 한정 권고됨
|
||||
- `SB-COND-C5`: condition annotation 의 usual 조합은 `@ConditionalOnClass`+`@ConditionalOnMissingBean`
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `matchIfMissing` 의 기본값(true/false) 자체 — 이 reference 페이지는 명시하지 않음(별도로 Javadoc 확인 필요, 본 URL 범위 밖)
|
||||
- "`matchIfMissing=true` 를 금지해야 한다"는 명제가 Spring 공식 best practice 라는 것 — 이 자료는 그런 권고 문장을 포함하지 않는다. 금지 결정은 project/branch 의 안전-기본값(fail-closed) 설계 판단으로 별도 명시해야 함
|
||||
- Bean 조건의 순서 의존 경고가 Property 조건(`@ConditionalOnProperty`)에도 문자 그대로 적용된다는 것 — 원문 절 구분상 별개 섹션
|
||||
- **내 프로젝트(ca-skeleton) 에 적용하려면 추가 확인이 필요한 것**:
|
||||
- `matchIfMissing` 의 실제 기본값과, 값을 명시하지 않았을 때(=옵션 자체를 안 쓸 때) capability descriptor 활성화가 어떻게 평가되는지 — Javadoc(`org.springframework.boot.autoconfigure.condition.ConditionalOnProperty`) 별도 조사 필요
|
||||
- property 기반 조건이 실제로 순서 독립적인지(=Environment 기반이라 bean 등록 순서와 무관한지)를 이 문서만으로 확정할 수 없음 — 필요하면 Spring 소스 또는 별도 실험으로 검증
|
||||
|
||||
## 메모
|
||||
|
||||
> 검증되지 않은 내 해석. 인용 아님.
|
||||
|
||||
- 이 페이지가 `matchIfMissing` semantics를 설명하지 않는다는 사실 자체가 중요한 발견 — branch 결정문에 "공식 문서가 matchIfMissing=false 를 기본값으로 명시한다"처럼 쓰면 과장. 정확히는 "matchIfMissing 은 옵션으로 실재하지만 그 기본값·의미는 이 reference 페이지 밖(Javadoc)에서 확인해야 한다"로 표현할 것.
|
||||
- `SB-COND-C3`/`SB-COND-C4` (bean 조건의 순서 의존성)는 matchIfMissing 자체보다는, project 가 이미 정한 "bean 이름 기반 판정 금지 → typed descriptor 기반 provider 선택" 결정을 보강하는 배경 근거로 더 적합해 보임.
|
||||
- 동일 URL을 이미 인용한 `[[raw/official-docs/adapter-spring-boot-autoconfig-custom-starter]]` (ca-tmpl 프로젝트, `AutoConfiguration.imports`/custom starter naming 위주)와는 발췌 절이 다름 — 이 문서는 Property/Bean Conditions 절에 집중.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 URL, 다른 발췌·다른 프로젝트: `[[raw/official-docs/adapter-spring-boot-autoconfig-custom-starter]]` (ca-tmpl, `AutoConfiguration.imports` + custom starter naming + `@ConditionalOnBooleanProperty` 위주)
|
||||
- 인접 주제: `[[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]]` (`@ConditionalOnProperty` 기반 adapter on/off 의 ArchUnit 정적 검사 한계)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
title: official-doc / Spring Boot Javadoc — ConditionalOnProperty (matchIfMissing default, havingValue, name, prefix)
|
||||
source_type: official-doc
|
||||
status: raw
|
||||
confidence: high
|
||||
url: https://docs.spring.io/spring-boot/api/java/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html
|
||||
archive_url:
|
||||
related_branches: [feature-capability-provider-selection-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, architecture, spring-boot]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring Boot Javadoc — ConditionalOnProperty (matchIfMissing default, havingValue, name, prefix)
|
||||
|
||||
> Layer: `raw/official-docs/` — Spring Boot 4.1.0 **Javadoc API** 페이지(`org.springframework.boot.autoconfigure.condition.ConditionalOnProperty`) 발췌. 기존 `[[raw/official-docs/spring-boot-condition-annotations]]` (reference 문서, `matchIfMissing` 을 1회만 언급하고 semantics 를 설명하지 않음 — `SB-COND-C2`)가 남긴 공백을 메우기 위해 별도 수집.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-capability-provider-selection-contract]] | D4("production capability 에 `matchIfMissing = true` 금지")의 **메커니즘** 근거 승급 — `matchIfMissing` 의 선언된 기본값이 `false` 이고, property 가 `Environment` 에 전혀 없을 때 기본적으로 조건이 match 되지 않는다는 사실이 공식 Javadoc 에 명시되어 있는지 확인 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-boot/api/java/org/springframework/boot/autoconfigure/condition/ConditionalOnProperty.html
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Spring Team — Maciej Walkowiak, Stephane Nicoll, Phillip Webb (Javadoc `@author` 태그 명시) / Broadcom
|
||||
- 발행일: 미상 — living Javadoc. 페이지 타이틀 기준 **Spring Boot 4.1.0 API**, `@since 1.1.0`
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`[[raw/branch-notes/feature-capability-provider-selection-contract]]` 의 D4("production capability 에 `matchIfMissing = true` 를 쓰지 않는다")는 기존에 `UNSUPPORTED_DECISION` 으로 라벨돼 있었다 — 이미 수집한 reference 문서(`spring-boot-condition-annotations.md`)는 `matchIfMissing` 을 36,781자 중 1회만 언급하고 기본값·semantics 를 설명하지 않기 때문이다(`SB-COND-C2` 의 Does not prove 참조). 이 Javadoc 페이지는 그 annotation element 자체의 공식 API 문서이며, `matchIfMissing` 의 선언된 기본값과 "property 부재 시 기본 동작"을 직접 서술한다. 이 자료로 D4 의 **메커니즘 축**(공식 문서가 증명하는 것)과 **정책 축**(내부 결정)을 분리할 수 있는지 확인하는 것이 수집 목적이다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [class-level description] "If the property is not contained in the Environment at all, the matchIfMissing() attribute is consulted. By default missing attributes do not match."
|
||||
|
||||
> [Element Details § matchIfMissing] "Specify if the condition should match if the property is not set. Defaults to false." — `Default: false`
|
||||
|
||||
> [Optional Element Summary / Element Details § havingValue] "The string representation of the expected value for the properties. If not specified, the property must not be equal to false."
|
||||
|
||||
> [Element Details § prefix] "A prefix that should be applied to each property. The prefix automatically ends with a dot if not specified. A valid prefix is defined by one or more words separated with dots (e.g. "acme.system.feature")."
|
||||
|
||||
> [Element Details § name] "The name of the properties to test. If a prefix has been defined, it is applied to compute the full key of each property. For instance if the prefix is app.config and one value is my-value, the full key would be app.config.my-value"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SB-COND-JD-C1 | `matchIfMissing` attribute 의 **선언된 기본값은 `false`** 다 | [Element Details § matchIfMissing] "Specify if the condition should match if the property is not set. Defaults to false." (+ `Default: false`) | `official-reference` | `@ConditionalOnProperty(matchIfMissing=...)` 를 명시적으로 지정하지 않았을 때 annotation element 자체의 기본값 | `matchIfMissing=true` 를 명시적으로 설정하는 것이 안티패턴이거나 금지해야 한다는 정책적 권고 — 이 문서는 그런 진술을 포함하지 않는다 |
|
||||
| SB-COND-JD-C2 | property 가 `Environment` 에 **전혀 존재하지 않을 때**, `matchIfMissing` 이 참조되며 **기본적으로(=미지정 시) 조건은 match 되지 않는다** — "property 미설정 = 조건 불일치"가 annotation class-level description 이 직접 서술하는 기본 동작이다 | [class-level description] "If the property is not contained in the Environment at all, the matchIfMissing() attribute is consulted. By default missing attributes do not match." | `official-reference` | property 완전 부재 상황에서 `@ConditionalOnProperty` 의 기본(=matchIfMissing 미지정) 동작. D4 의 **메커니즘** 축 근거 | 특정 capability(예: gRPC 서버, filesystem object storage)가 코드에서 이미 `matchIfMissing=true` 를 **명시적으로 override** 한 경우의 실제 동작까지 바꾸지 않는다 — 그 6곳의 실제 설정 여부는 `internal-code-fact` 로 개별 확인해야 한다 |
|
||||
| SB-COND-JD-C3 | `havingValue` 의 기본값은 빈 문자열(`""`)이며, 명시하지 않으면 property 값이 `"false"` 와 같지 않아야 match 된다 | [Element Details § havingValue] "The string representation of the expected value for the properties. If not specified, the property must not be equal to false." | `official-reference` | `havingValue` 미지정 시 매칭 규칙 (class-level description 의 "존재+값≠false" 규칙과 동일 규칙을 attribute 레벨에서 재확인) | `havingValue` 를 특정 문자열로 지정했을 때 세부 매칭 표(원문에 있으나 이 claim 의 인용 범위 밖) |
|
||||
| SB-COND-JD-C4 | `prefix` 는 각 property 에 적용되는 접두어이며, 명시하지 않으면 자동으로 점(`.`)으로 끝나고, 점으로 구분된 하나 이상의 단어로 정의된 유효 prefix 를 갖는다(예: `"acme.system.feature"`) | [Element Details § prefix] "A prefix that should be applied to each property. The prefix automatically ends with a dot if not specified. A valid prefix is defined by one or more words separated with dots (e.g. "acme.system.feature")." | `official-reference` | `@ConditionalOnProperty(prefix=...)` 의 문법·기본 동작 | prefix 를 생략했을 때(기본값 `""`) 전체 키가 어떻게 계산되는지의 구체 규칙 자체(그건 `name` 설명이 규정, SB-COND-JD-C5) |
|
||||
| SB-COND-JD-C5 | `name` 은 검사할 property 이름이며, `prefix` 가 정의돼 있으면 각 property 의 full key 계산에 적용된다(예: `prefix=app.config`, value=`my-value` → full key `app.config.my-value`) | [Element Details § name] "The name of the properties to test. If a prefix has been defined, it is applied to compute the full key of each property. For instance if the prefix is app.config and one value is my-value, the full key would be app.config.my-value" | `official-reference` | `name`/`prefix` 조합의 full key 계산 규칙 | dashed notation 강제(`my-long-property`)를 어기면 실제 binding 이 실패하는지 여부 — 원문은 "use" 를 권고할 뿐 강제 실패를 서술하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `SB-COND-JD-C1`: `matchIfMissing` 의 선언된 기본값은 `false`
|
||||
- `SB-COND-JD-C2`: property 가 `Environment` 에 전혀 없을 때 (matchIfMissing 미지정 시) 기본적으로 조건은 match 되지 않는다
|
||||
- `SB-COND-JD-C3`~`C5`: `havingValue`/`prefix`/`name` 각 attribute 의 정의와 기본값
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- "production capability 에 `matchIfMissing=true` 를 쓰지 않는다"는 정책이 Spring 공식 best practice 라는 것 — Javadoc 은 mechanism(기본값·매칭 규칙)만 서술하고, 이 attribute 를 언제 쓰거나 쓰지 말아야 하는지에 대한 권고 문장을 포함하지 않는다
|
||||
- `@Conditional` 계열 조건의 평가 시점·bean definition 순서 의존성 — 그 범위는 sibling `[[raw/official-docs/spring-boot-condition-annotations]]` 의 `SB-COND-C3`/`C4` 가 다룬다
|
||||
- `@ConditionalOnProperty` 와 `@ConditionalOnBooleanProperty` 의 세부 차이 — "See Also" 링크만 존재하고 본문 비교 서술은 없음
|
||||
- **내 프로젝트(ca-skeleton) 에 적용하려면 추가 확인이 필요한 것**:
|
||||
- D4 Open Risk 로 남아 있는 6곳(`GrpcServerConfig.java:18-22`, `ObjectStorageConfig.java:32-37` 등)이 실제로 `matchIfMissing=true` 를 명시했는지, 그것이 §Audit A5 판정대로 "속성 부재로 활성화되는 운영 표면"인지는 `internal-code-fact` 로 개별 확인해야 한다 — 이 Javadoc 은 그 판정에 필요한 **기본값 규칙**만 제공한다
|
||||
|
||||
## 메모
|
||||
|
||||
> 검증되지 않은 내 해석. 인용 아님.
|
||||
|
||||
- D4 는 이 수집으로 **2축 승급** 가능: (1) 메커니즘 — "`matchIfMissing` 기본값은 `false`, property 부재 시 기본 동작은 조건 불일치"는 이제 `official-reference`(Javadoc) 근거를 갖는다(`SB-COND-JD-C1`/`C2`). (2) 정책 — "그래서 production capability 에는 이 attribute 를 명시적으로 `true` 로 켜지 않는다"는 여전히 `internal-policy`다. Javadoc 은 attribute 를 어떻게 쓰라는 권고를 하지 않기 때문. 두 축을 하나의 `official-vendor-doc`/`official-reference` 라벨로 뭉치지 말 것.
|
||||
- 역설적으로 이 발견은 D4 의 위험 서술을 더 날카롭게 만든다: 기본값(`matchIfMissing` 미지정)이 이미 "property 부재 = 비활성"이므로, ca-tmpl 6곳이 실제로 위험한 이유는 "matchIfMissing 을 안 써서"가 아니라 "**명시적으로 override 해서**"다. §Audit A5(`GrpcServerConfig`/`ObjectStorageConfig`)가 실제로 `matchIfMissing=true` 를 썼는지 코드에서 재확인 필요 — 이 raw 문서만으로는 그 코드 사실을 증명하지 못한다.
|
||||
- self-grep 결과 이 Javadoc 페이지 전체에서 `matchIfMissing`(식별자)은 6회 등장 — 목차(1) + class description(2) + Optional Element Summary(1) + Element Details 제목/시그니처(2)이며, 그중 semantics 를 서술하는 문장은 class description 1곳 + Element Details 1곳 총 2곳뿐이다.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 annotation, 다른 문서 계층(reference guide, `matchIfMissing` 존재만 언급): `[[raw/official-docs/spring-boot-condition-annotations]]`
|
||||
- 인접 주제 (`@ConditionalOnProperty` 기반 adapter on/off 의 ArchUnit 정적 검사 한계): `[[raw/official-docs/archunit-conditional-on-property-3-layer-pattern]]`
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: "official-doc / Spring Data MongoDB — Index Creation (Automatic vs Programmatic)"
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-data/mongodb/reference/mongodb/mapping/mapping-index-management.html
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, spring-data, mongodb]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring Data MongoDB — Index Creation (Automatic vs Programmatic)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> Spring Data MongoDB Reference — Mapping › Index Creation
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | "index 는 애플리케이션 automatic index creation 에 맡기지 않고 선언적 manifest + migration runner 로 관리하며 drift 를 감지한다" 결정의 근거 후보 — automatic index creation 기본값이 버전 3.0부터 OFF 라는 것과, 명시적(programmatic) index 생성이 "Recommended" 로 표기되어 있음을 공식 문서로 확인. 단 "manifest"/"migration runner"/"drift 감지" 라는 구체 메커니즘 자체는 이 문서가 규정하지 않음 (아래 Usage Boundaries 참조) — branch-note 작성 시 `UNSUPPORTED_IMPL_DECISION` 라벨 필요 여부 검토 대상 |
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-data/mongodb/reference/mongodb/mapping/mapping-index-management.html
|
||||
- 아카이브 URL: (미확보)
|
||||
- 저자 / 조직: Spring Data Team (Broadcom / VMware)
|
||||
- 발행일: Spring Data MongoDB Reference (페이지 자체에 버전 배지 없음 — "since version 3.0" 문구로 최소 3.0 이상 대상 문서임만 확인됨)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지 / Why archived
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch 의 index 관리 결정("automatic index creation 에 맡기지 않고 선언적 manifest + migration runner + drift 감지")이 아직 외부 근거 없이 스캐폴딩된 상태였음. Spring Data MongoDB 공식 문서에서 (1) automatic index creation 기본값이 버전 3.0부터 OFF 라는 것, (2) 이를 켰을 때의 위험 고지("undesired effects on collection lifecycle and performance") 및 "Production consideration" 경고, (3) `@Indexed`/`@CompoundIndex` 선언 방식과 `IndexResolver`/`IndexOperations` 프로그래매틱 방식이 모두 존재한다는 것을 확인하기 위해 아카이브.
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim, 6개)
|
||||
|
||||
> 셀프그렙 검증 원본: `/tmp/claude-1000/-home-donghyeon-workspace-ai-tool-llm-wiki/1f67282d-364b-47cb-ba22-616233fae6ea/scratchpad/source-fetch-20260728-165953.txt` (WebFetch 결과 저장, self-grep 통과)
|
||||
|
||||
> [§Overview / Default Behavior, self-grep L8] "**Automatic index creation is turned OFF by default as of version 3.0**"
|
||||
|
||||
> [§Overview, self-grep L5] "Spring Data MongoDB can automatically create indexes for entity types annotated with `@Document`. However, **index creation must be explicitly enabled** since version 3.0 to prevent undesired effects on collection lifecycle and performance."
|
||||
|
||||
> [§Programmatic Index Creation (Recommended), self-grep L29] "Spring Data recommends **explicit index creation for application-based control**. Use `IndexResolver` with `IndexOperations` on application startup, triggered by `ContextRefreshedEvent`:"
|
||||
|
||||
> [§Index Annotations / Compound Indexes (code), self-grep L90] "@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")"
|
||||
|
||||
> [§Important Notes, self-grep L182] "**Production consideration**: Explicit index creation provides better control than automatic creation"
|
||||
|
||||
> [§Important Notes, self-grep L183] "Index creation via `IndexOperations` offers more control than annotations"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SD-MONGO-INDEX-C1 | Spring Data MongoDB 는 버전 3.0부터 automatic index creation 이 기본적으로 꺼져 있다(OFF by default) | [§Default Behavior, l.8] "**Automatic index creation is turned OFF by default as of version 3.0**" | `official-vendor-doc` | Spring Data MongoDB 3.0 이상의 `@Document` 엔티티 | 3.0 이전 버전의 정확한 기본값(암묵적으로 ON 이었음을 시사하나 이 문서가 3.0 이전 동작을 직접 재확인하지는 않음), 그리고 우리 프로젝트가 실제 3.0+ 를 쓰는지 여부 |
|
||||
| SD-MONGO-INDEX-C2 | automatic index creation 은 "collection lifecycle 과 performance 에 대한 원치 않는 영향(undesired effects)"을 막기 위해 명시적으로 활성화되어야 한다 | [§Overview, l.5] "However, **index creation must be explicitly enabled** since version 3.0 to prevent undesired effects on collection lifecycle and performance." | `official-vendor-doc` | automatic index creation 활성화 여부를 결정할 때의 공식 위험 고지 | "프로덕션에서 금지" 또는 "권장하지 않음" 이라는 명시적 문구는 없음 — "undesired effects" 라는 일반적 경고만 있고, 구체적으로 무엇이 위험한지(예: 대용량 컬렉션에서 인덱스 빌드가 쓰기를 블록하는지 등)는 이 페이지에 설명되지 않음 |
|
||||
| SD-MONGO-INDEX-C3 | "Programmatic Index Creation" 섹션은 "(Recommended)" 로 표기되어 있으며, 애플리케이션 기반 제어를 위한 명시적 index 생성을 권장한다. 구체적으로 `IndexResolver` 와 `IndexOperations` 를 `ContextRefreshedEvent` 시점에 사용하는 패턴을 제시한다 | [§Programmatic Index Creation (Recommended), l.29] "Spring Data recommends **explicit index creation for application-based control**. Use `IndexResolver` with `IndexOperations` on application startup, triggered by `ContextRefreshedEvent`:" | `official-vendor-doc` | 애플리케이션 시작 시점에 index 를 프로그래매틱하게 생성/보장하려는 설계 | "manifest 파일"이나 "migration runner"라는 개념·도구를 이 문서가 규정하지 않는다 — `ContextRefreshedEvent` 리스너에서 `resolver.resolveIndexFor(...).forEach(indexOps::ensureIndex)` 를 호출하는 것이 문서가 보여주는 유일한 패턴이며, 별도 migration 이력 관리나 drift 감지는 이 문서 범위 밖 |
|
||||
| SD-MONGO-INDEX-C4 | Spring Data MongoDB 는 `@Indexed`, `@CompoundIndex`/`@CompoundIndexes`, `@HashIndexed`, `@WildcardIndexed`, `@TextIndexed` 같은 매핑 애노테이션을 통한 선언적 index 정의 방식을 제공한다 | [§Index Annotations / Compound Indexes, l.90] `@CompoundIndex(name = "age_idx", def = "{'lastName': 1, 'age': -1}")` | `official-vendor-doc` | `@Document` 엔티티 클래스/필드에 선언하는 index 정의 | 애노테이션 방식이 deprecated 되었거나 사용을 피해야 한다는 것 — 오히려 `IndexResolver` 가 이 애노테이션들을 읽어 인덱스를 계산하는 상호보완 관계 (C5 참조) |
|
||||
| SD-MONGO-INDEX-C5 | "Important Notes" 섹션은 explicit index creation 이 automatic creation 보다 더 나은 제어를 제공한다는 production 관련 권고와, `IndexOperations`(프로그래매틱)가 annotation(선언적) 보다 더 많은 제어력을 제공한다는 비교를 명시한다 | [§Important Notes, l.182–183] "**Production consideration**: Explicit index creation provides better control than automatic creation" / "Index creation via `IndexOperations` offers more control than annotations" | `official-vendor-doc` | explicit(programmatic) index 생성 전략을 선택하는 결정의 공식 근거 | "annotation 기반 선언 방식을 쓰지 말라"는 것은 아님 — 프로그래매틱 방식이 "더 많은 제어력"을 준다는 상대적 비교일 뿐, annotation 방식이 프로덕션에 부적합하다고 말하지 않음 |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SD-MONGO-INDEX-C1`: automatic index creation 기본값은 버전 3.0부터 OFF (auto-index-creation 에 맡기지 않는다는 결정의 "이미 기본이 꺼져 있다"는 전제 근거)
|
||||
- `SD-MONGO-INDEX-C2`: 자동 생성을 켤 경우 collection lifecycle/performance 에 원치 않는 영향이 있을 수 있다는 공식 위험 고지
|
||||
- `SD-MONGO-INDEX-C3`: 명시적/프로그래매틱 index 생성이 "Recommended" 섹션 표제로 제시됨 (`IndexResolver` + `IndexOperations` + `ContextRefreshedEvent` 패턴)
|
||||
- `SD-MONGO-INDEX-C4`: `@Indexed`/`@CompoundIndex` 등 선언적 index 애노테이션의 존재와 문법
|
||||
- `SD-MONGO-INDEX-C5`: explicit(programmatic) 방식이 automatic 방식 및 annotation-only 방식보다 "더 많은 제어력"을 제공한다는 공식 비교
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **"manifest" 라는 개념** — 이 문서는 index 정의를 코드(애노테이션) 또는 `IndexResolver`/`IndexOperations` 호출로 표현하는 두 가지만 보여줄 뿐, 별도의 선언적 "manifest 파일"(YAML/JSON 등 외부 스펙 문서)이라는 아키텍처 패턴을 언급하거나 권장하지 않는다. manifest 도입은 branch 의 설계 결정이며 이 문서로 정당화되지 않는다
|
||||
- **"migration runner"** — Flyway/Liquibase 류의 버전 관리형 migration 도구·이력 테이블 개념은 이 문서에 없다. 문서가 보여주는 것은 `ContextRefreshedEvent` 시점에 `ensureIndex` 를 매번 재실행하는 idempotent-보장 패턴뿐이며, "migration" 이력 추적은 범위 밖
|
||||
- **"drift 감지"** — 실제 컬렉션 인덱스와 코드/manifest 상 정의된 인덱스 간의 불일치를 탐지하는 메커니즘은 이 문서에 없다. `IndexOperations`/`IndexResolver` 는 인덱스를 "생성/보장(ensure)"하는 API 이지 "비교/보고(diff)"하는 API 라고 문서가 명시하지 않는다
|
||||
- "자동 인덱스 생성이 프로덕션에서 금지된다" 또는 "권장되지 않는다"는 명시적 문구 — C2/C5 는 "위험 고지"와 "더 나은 제어"라는 상대적 표현만 있을 뿐, 절대적 금지 표현은 없다
|
||||
- replica-set 트랜잭션이나 change stream 과 index 관리의 상호작용 — 이 페이지는 index 생성 메커니즘만 다루며, 이 branch 의 다른 범위(트랜잭션·change stream replica-set 요건)는 별도 근거 필요
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- "선언적 manifest" 를 실제로 어떤 형식(코드 상 애노테이션 집합을 manifest 로 간주할지, 별도 YAML 스펙을 만들지)으로 정의할지는 branch 의 별도 결정이며, 이 문서만으로는 근거가 부족함 — `UNSUPPORTED_IMPL_DECISION` 라벨 + trade-off 명시 필요
|
||||
- "migration runner" 의 구체 구현(예: 자체 `IndexOperations` 래퍼가 있는 컬렉션에 인덱스 적용 이력을 별도 컬렉션에 기록할지)도 이 문서 범위 밖이므로 별도 설계·근거 필요
|
||||
- drift 감지를 위한 실제 비교 로직(`listIndexes()` 커맨드 등 MongoDB 드라이버 API 조사 필요)은 별도 raw 자료로 보강해야 함
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 이 문서는 "automatic index creation 을 쓰지 말라"는 근거로는 충분하지만(C1+C2), branch 가 결정하려는 "manifest + migration runner + drift 감지"라는 구체 아키텍처는 이 문서의 범위를 넘어선다. `/branch-spec` 단계에서 D-row 를 작성할 때, 이 raw 자료는 "자동 생성 대신 명시적 생성" 부분만 뒷받침하고 나머지(manifest 형식·migration 이력·drift 비교)는 `UNSUPPORTED_IMPL_DECISION` 으로 라벨링하거나 별도 근거(MongoDB 드라이버 `listIndexes` 공식 문서, Flyway-Mongo 유사 도구 사례 등)를 추가 수집해야 한다.
|
||||
- `IndexResolver`/`IndexOperations` 조합이 애노테이션 메타데이터를 읽어 인덱스를 계산하는 구조이므로, "manifest" 를 이 애노테이션 집합 자체로 간주하는 설계도 가능해 보이지만 이는 내 해석이며 문서가 그렇게 명명하지는 않는다 (검증 안 된 추론 — wiki 단계에서 별도 확인 필요).
|
||||
- 추가로 봐야 할 동일 출처 페이지: Spring Data MongoDB Reference 의 read preference / read concern / write concern 페이지, replica-set 트랜잭션 요건 페이지, change stream resume token 페이지 (같은 branch 의 다른 결정 근거).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — MongoDB read/write concern, change streams 공식 문서는 이 branch 를 위해 별도 수집 필요)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "official-doc / Spring Data MongoDB — Template Configuration (WriteConcern, WriteConcernResolver, ReadPreference)"
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-data/mongodb/reference/mongodb/template-config.html
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, spring-data, mongodb, write-concern]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring Data MongoDB — Template Configuration (WriteConcern, WriteConcernResolver, ReadPreference)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> Spring Data MongoDB Reference — MongoDB Support › Configuration
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — Spring Data MongoDB 공식 레퍼런스 문서 (Broadcom / VMware, Spring Data Team).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | D5(read concern 연산 단위 명시)·D6(write concern 명시)·D7(read preference 명시) 결정에서 **Spring Data MongoDB 가 그 값을 어디에 어떻게 붙이는지(적용 seam)** 를 확인. `MongoTemplate` 수준 `WriteConcern` 기본값 + `WriteConcernResolver` 연산별 전략 인터페이스 + `ReadPreference` 가 template 의 설정 가능한 property 목록에 있다는 것을 근거로 확보. **단, read concern 설정 수단은 이 문서에 부재** — D5 의 Spring Data 측 적용 seam 은 이 자료로 닫히지 않는다(아래 Usage Boundaries 참조) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-data/mongodb/reference/mongodb/template-config.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring Data Team (Broadcom / VMware)
|
||||
- 발행일: Spring Data MongoDB Reference 버전 5.1.0 (페이지 내 버전 배지 기준, 날짜 미표기 롤링 레퍼런스)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch 가 read/write concern·read preference 를 "명시" 하기로 결정했으나, `MongoTemplate` API 어디에 그 값을 붙이는지(적용 seam)의 근거가 없어 구현자가 되묻게 되는 gap 이 있었다. 이 문서는 `MongoTemplate` 수준 기본 `WriteConcern`, 연산 단위 결정을 위한 `WriteConcernResolver`, `ReadPreference` 가 template 의 설정 가능한 property 라는 것을 공식 문서로 확인하기 위해 보관. read concern 은 이 문서에 **전혀 등장하지 않아** 그 부재 자체가 중요한 기록이다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> 셀프그렙 검증 원본: `/tmp/source-fetch-mongo-template.txt` (curl 직접 fetch — WebFetch 1차 결과가 실제 원문에 없는 메서드명을 생성한 사실이 발견되어, HTML 원본을 직접 curl 로 가져와 텍스트 추출한 뒤 재검증함. 아래 메모 참조)
|
||||
|
||||
> [§Configuration, l.266] "Other optional properties that you might like to set when creating a MongoTemplate / ReactiveMongoTemplate are the default WriteResultCheckingPolicy, WriteConcern, ReadPreference and others listed below."
|
||||
|
||||
> [§Default Read Preference, l.270] "The default read preference applied to read operations if no other preference was defined via the Query."
|
||||
|
||||
> [§Default WriteConcern, l.278] "If it has not yet been specified through the driver at a higher level (such as com.mongodb.client.MongoClient), you can set the com.mongodb.WriteConcern property that the MongoTemplate uses for write operations. If the WriteConcern property is not set, it defaults to the one set in the MongoDB driver's DB or Collection setting."
|
||||
|
||||
> [§WriteConcernResolver, l.282] "For more advanced cases where you want to set different WriteConcern values on a per-operation basis (for remove, update, insert, and save operations), a strategy interface called WriteConcernResolver can be configured on MongoTemplate."
|
||||
|
||||
> [§WriteConcernResolver (interface), l.284-285] "public interface WriteConcernResolver {\n WriteConcern resolve(MongoAction action);\n}"
|
||||
|
||||
> [§WriteConcernResolver, l.289] "MongoAction contains the collection name being written to, the java.lang.Class of the POJO, the converted Document, the operation (REMOVE, UPDATE, INSERT, INSERT_LIST, or SAVE), and a few other pieces of contextual information."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SD-MONGO-TEMPLATE-C1 | `MongoTemplate`/`ReactiveMongoTemplate` 생성 시 설정 가능한 선택적 property 목록에 `WriteResultCheckingPolicy`·`WriteConcern`·`ReadPreference` 가 포함된다 | [l.266] "Other optional properties that you might like to set when creating a MongoTemplate / ReactiveMongoTemplate are the default WriteResultCheckingPolicy, WriteConcern, ReadPreference and others listed below." | `official-vendor-doc` | `ReadPreference` 를 template 레벨에서 명시할 수 있는 수단이 존재한다는 것(D7 의 "표현 수단 실재" 근거) | 실제 setter 메서드 이름(예: `setReadPreference`)이나 시그니처는 이 페이지 텍스트에 없다(self-grep 0 매치) — 목록에 있다는 사실만 확인되고 API 형태는 확인되지 않음 |
|
||||
| SD-MONGO-TEMPLATE-C2 | "기본 read preference"란 Query 로 별도 preference 가 지정되지 않았을 때 read 연산에 적용되는 값이다 | [l.270] "The default read preference applied to read operations if no other preference was defined via the Query." | `official-vendor-doc` | template 레벨 기본값과 Query 레벨 override 의 계층 관계(Query 가 우선) — D7 의 "연산 단위 표현 수단이 있는가" 판단에 사용 | read concern 에 대한 동일한 계층 서술은 이 페이지에 없다 — 이 문서 전체에 "read concern"/"ReadConcern" 문자열이 0회 등장(self-grep 확인). read concern 을 연산 단위로 명시할 template/query 수준 수단이 있는지는 이 자료로 증명되지 않는다 |
|
||||
| SD-MONGO-TEMPLATE-C3 | `MongoTemplate` 의 `WriteConcern` 은 `MongoClient` 같은 드라이버 상위 레벨에서 지정되지 않았을 때만 적용되며, 미설정 시 MongoDB 드라이버의 DB/Collection 설정값으로 폴백한다 | [l.278] "If it has not yet been specified through the driver at a higher level (such as com.mongodb.client.MongoClient), you can set the com.mongodb.WriteConcern property that the MongoTemplate uses for write operations. If the WriteConcern property is not set, it defaults to the one set in the MongoDB driver's DB or Collection setting." | `official-vendor-doc` | template 레벨 write concern 설정과 driver(MongoClient) 레벨 설정의 우선순위 — D6 "write concern 을 명시" 결정의 적용 seam 근거 | 정확한 setter 메서드 이름(`setWriteConcern`)은 이 페이지 텍스트에 없다(self-grep 0 매치) — "you can set the ... property" 라는 prose 표현만 있다. 이 default WriteConcern 은 template 전체의 기본값이지 연산 단위 값이 아니다 |
|
||||
| SD-MONGO-TEMPLATE-C4 | 연산 단위(remove/update/insert/save)로 다른 `WriteConcern` 값을 적용하려면 `WriteConcernResolver` 전략 인터페이스를 `MongoTemplate` 에 구성할 수 있다 | [l.282] "For more advanced cases where you want to set different WriteConcern values on a per-operation basis (for remove, update, insert, and save operations), a strategy interface called WriteConcernResolver can be configured on MongoTemplate." | `official-vendor-doc` | **D6 이 찾던 "연산 단위 write concern 결정 수단"의 실제 답** — write concern 한정으로 존재가 확인됨 | `WriteConcernResolver` 는 `WriteConcern` 전용이다. 동일한 전략 인터페이스가 `ReadConcern` 이나 `ReadPreference` 에도 존재하는지는 이 페이지가 증명하지 않는다(`ReadConcernResolver`/`ReadPreferenceResolver` 류의 언급 없음, self-grep 0 매치) |
|
||||
| SD-MONGO-TEMPLATE-C5 | `WriteConcernResolver` 인터페이스 시그니처는 `WriteConcern resolve(MongoAction action)` 이다 | [l.284-285] "public interface WriteConcernResolver {\n WriteConcern resolve(MongoAction action);\n}" | `official-reference` | 구현 시 준수해야 하는 정확한 인터페이스 계약 | 없음 — API 시그니처 자체의 직접 인용 |
|
||||
| SD-MONGO-TEMPLATE-C6 | `MongoAction` 은 쓰기 대상 컬렉션명, POJO 의 `java.lang.Class`, 변환된 `Document`, 연산 종류(REMOVE/UPDATE/INSERT/INSERT_LIST/SAVE), 기타 컨텍스트 정보를 담는다 | [l.289] "MongoAction contains the collection name being written to, the java.lang.Class of the POJO, the converted Document, the operation (REMOVE, UPDATE, INSERT, INSERT_LIST, or SAVE), and a few other pieces of contextual information." | `official-reference` | `WriteConcernResolver.resolve()` 구현체가 결정에 사용할 수 있는 컨텍스트 정보의 범위 | 이 컨텍스트에 read concern/read preference 관련 필드가 포함되는지는 언급되지 않는다 — `MongoAction` 은 write 연산 전용 컨텍스트로 보이며, 이 페이지는 read 연산에 대한 동등한 연산 단위 결정 메커니즘을 제공하지 않는다 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, MongoDB 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SD-MONGO-TEMPLATE-C1`: `ReadPreference` 가 `MongoTemplate` 의 설정 가능한 property 목록에 포함된다는 것
|
||||
- `SD-MONGO-TEMPLATE-C2`: template 기본 read preference 와 Query 레벨 override 의 계층 관계(Query 우선)
|
||||
- `SD-MONGO-TEMPLATE-C3`: template `WriteConcern` 과 driver(`MongoClient`) 레벨 설정의 우선순위·폴백 관계
|
||||
- `SD-MONGO-TEMPLATE-C4`·`C5`·`C6`: `WriteConcernResolver` 를 통한 **연산 단위 write concern 결정 수단**의 존재와 인터페이스 계약, 사용 가능한 컨텍스트
|
||||
- 이 자료가 증명하지 않는 것 (중요 — read concern 부재):
|
||||
- **read concern 을 `MongoTemplate`/Query 단위로 설정하는 수단** — 이 페이지 전체(`/tmp/source-fetch-mongo-template.txt`, curl 원본 텍스트)에 "read concern"/"ReadConcern" 문자열이 **0회** 등장한다(self-grep 확인). 즉 D5(read concern 연산 단위 명시) 결정의 Spring Data 측 적용 seam 은 **이 자료로 근거를 확보할 수 없다** — branch-note 에는 `UNSUPPORTED_DECISION` 후보로 승계해야 한다
|
||||
- `setWriteConcern`/`setReadPreference` 라는 정확한 Java 메서드 시그니처 — 이 페이지는 "you can set the ... property" 라는 prose 로만 설명하고 실제 setter 메서드 이름/코드를 보여주지 않는다(self-grep 0 매치)
|
||||
- `WriteConcernResolver` 와 동등한 연산별 read concern/read preference resolver — 이 페이지에 존재하지 않는다
|
||||
- Query 객체를 통한 read preference override 의 구체적 API — 이 페이지는 "Query Read Preference" 섹션(`template-query-operations.html#mongo.query.read-preference`)으로 링크만 하고 본문에서 API 형태를 보여주지 않는다
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- read concern 적용 seam은 별도 raw 자료(예: MongoDB Java 드라이버 `MongoCollection.withReadConcern()` 또는 Spring Data MongoDB 의 `ClientSession`/`SessionScoped` 관련 공식 문서)로 보강 필요 — 1 dispatch = 1 URL 원칙에 따라 이번 회차에는 포함하지 않음
|
||||
- Query 레벨 read preference override 의 정확한 API(`Query#withReadPreference` 등 존재 여부)는 `template-query-operations.html#mongo.query.read-preference` 별도 확인 필요
|
||||
- `WriteConcernResolver` 를 실제로 어디에 등록/구성(`@Bean` 설정 예시 등)하는지는 이 페이지에 없음 — `template.setWriteConcernResolver(...)` 형태로 붙이는지 여부는 미확인
|
||||
|
||||
## 메모
|
||||
|
||||
- **WebFetch 1차 결과 fabrication 발견**: 최초 `WebFetch` 호출(원문 그대로 추출 프롬프트)이 반환한 텍스트는 `template.setWriteConcern(WriteConcern.ACKNOWLEDGED);`, `template.setReadPreference(ReadPreference.secondary());` 같은 구체적 메서드 호출 코드를 포함했다. 그러나 이 페이지를 `curl` 로 직접 fetch 해 HTML 을 텍스트로 변환한 원본(`/tmp/source-fetch-mongo-template.txt`)에는 `setWriteConcern`/`setReadPreference` 문자열이 **0회** 등장한다. 즉 WebFetch 의 1차 요약(소형 모델 후처리)이 실제로 페이지에 없는 API 호출 예시를 생성(fabrication)한 것으로 판단해 폐기했고, 본 문서의 모든 인용·Claim 은 curl 원본 재검증을 거친 것만 사용했다. 이 자료 수집 과정 자체가 "WebFetch 출력을 그대로 원문으로 신뢰하면 안 된다"는 사례 기록으로도 남긴다.
|
||||
- D5(read concern) 는 이 문서로 닫히지 않는다 — branch-note 작성 시 이 gap 을 `UNSUPPORTED_DECISION` 으로 명시할 것.
|
||||
- D6(write concern 연산 단위 명시)와 D7(read preference 명시)의 Spring Data 측 seam 은 이 문서로 부분적으로 닫힌다 — write concern 은 `WriteConcernResolver` 로 완전히 닫히고, read preference 는 "template property 로 설정 가능하다"는 존재 확인까지만 닫히고 정확한 API 형태는 미확인.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/write-concern-mongodb-official]] (MongoDB 서버 측 write concern 의미) · [[raw/official-docs/read-concern-mongodb-official]] (MongoDB 서버 측 read concern 의미 — 단, Spring Data 적용 seam 은 별도) · [[raw/official-docs/read-preference-mongodb-official]] (MongoDB 서버 측 read preference 5종 모드) · [[raw/official-docs/spring-data-mongodb-index-management-official]] (같은 Spring Data MongoDB Reference 문서군)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 추가)
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
title: "official-doc / Spring Data Redis — Scripting execution (RedisScript, ScriptExecutor, EVALSHA/NOSCRIPT fallback)"
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-data/redis/reference/redis/scripting.html
|
||||
archive_url:
|
||||
related_branches: [feature-redis-atomic-program-catalog-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, persistence, redis, spring-data]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring Data Redis — Scripting execution (RedisScript, ScriptExecutor, EVALSHA/NOSCRIPT fallback)
|
||||
|
||||
> Layer: `raw/official-docs/` — Spring Data Redis 공식 reference ("Scripting" 페이지, 버전 4.1.0) 원문 발췌.
|
||||
> ca-skeleton `feature-redis-atomic-program-catalog-contract` 의 "EVALSHA 우선 실행 + NOSCRIPT 재적재를 직접 구현할지, 프레임워크(Spring Data Redis) 기존 script executor 에 위임할지" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-redis-atomic-program-catalog-contract]] | EVALSHA 우선 실행 + NOSCRIPT 재적재를 직접 구현하지 않고 Spring Data Redis 의 기본 `ScriptExecutor` 에 위임하며, `RedisScript`/`DefaultRedisScript` 위에 typed facade 를 얹는 API 설계의 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-data/redis/reference/redis/scripting.html
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: Spring (VMware/Broadcom) — Spring Data Redis 공식 reference
|
||||
- 발행일: 명시된 발행일 없음(living reference doc). fetch 시점 페이지 메타데이터 기준 버전 `4.1.0` (`data-version="4.1.0"`, GitHub 소스 `spring-projects/spring-data-redis/blob/4.1.0/...`) — Spring Boot 4.0 세대와 정렬되는 최신판
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-redis-atomic-program-catalog-contract` 는 다단계 read-decide-write 를 버전 있는 atomic Lua program 으로 제공해야 하고, EVALSHA 실행 + NOSCRIPT 시 안전 재적재가 그 핵심 메커니즘이다. Redis 자체 공식 문서(`raw/official-docs/redis-eval-scripting-atomicity.md`)는 "애플리케이션이 EVALSHA 를 호출하고 NOSCRIPT 시 SCRIPT LOAD 후 재시도해야 한다"는 프로토콜 수준 규약만 증명하고, 어떤 client 라이브러리가 이를 자동화하는지는 범위 밖이라고 명시한다. 본 자료는 그 공백을 메운다 — Spring Data Redis 의 기본 `ScriptExecutor` 가 이 EVALSHA→NOSCRIPT→EVAL 재시도를 실제로 자동 처리한다는 프레임워크 공식 근거이며, 따라서 branch 가 직접 구현할지 위임할지를 결정하는 데 직접 쓰인다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Scripting] "Scripts can be run by calling the execute methods of RedisTemplate and ReactiveRedisTemplate. Both use a configurable ScriptExecutor (or ReactiveScriptExecutor) to run the provided script. By default, the ScriptExecutor (or ReactiveScriptExecutor) takes care of serializing the provided keys and arguments and deserializing the script result. This is done through the key and value serializers of the template. There is an additional overload that lets you pass custom serializers for the script arguments and the result."
|
||||
|
||||
> [§Scripting] "The default ScriptExecutor optimizes performance by retrieving the SHA1 of the script and attempting first to run evalsha, falling back to eval if the script is not yet present in the Redis script cache."
|
||||
|
||||
> [§Scripting] "The preceding code configures a RedisScript pointing to a file called checkandset.lua, which is expected to return a boolean value. The script resultType should be one of Long, Boolean, List, or a deserialized value type. It can also be null if the script returns a throw-away status (specifically, OK)."
|
||||
|
||||
> [§Scripting, tip admonition] "It is ideal to configure a single instance of DefaultRedisScript in your application context to avoid re-calculation of the script's SHA1 on every script run."
|
||||
|
||||
> [§Scripting, 코드 예시] "ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("META-INF/scripts/checkandset.lua")); return RedisScript.of(scriptSource, Boolean.class);"
|
||||
|
||||
> [§Scripting, 코드 예시] "return redisOperations.execute(script, List.of("key"), expectedValue, newValue);"
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SDR-SCRIPT-C1 | Spring Data Redis 의 기본 `ScriptExecutor`(`RedisTemplate`/`ReactiveRedisTemplate.execute(...)` 경로)는 스크립트의 SHA1 을 조회해 먼저 `evalsha` 실행을 시도하고, 스크립트가 Redis 스크립트 캐시에 아직 없으면 `eval` 로 폴백한다 — 즉 EVALSHA 우선 + NOSCRIPT 시 재적재를 프레임워크가 자동 처리한다 | "The default ScriptExecutor optimizes performance by retrieving the SHA1 of the script and attempting first to run evalsha, falling back to eval if the script is not yet present in the Redis script cache." | `official-vendor-doc` | `RedisTemplate`/`ReactiveRedisTemplate.execute(RedisScript, keys, args...)` 경로의 기본 `ScriptExecutor`/`ReactiveScriptExecutor` 구현 | 어떤 예외/에러 타입으로 NOSCRIPT 를 감지하는지, fallback 후 서버 캐시에 재적재되는지, 커스텀 `ScriptExecutor` 구현체의 동작, cluster/샤딩 환경에서의 동작은 증명하지 않음 |
|
||||
| SDR-SCRIPT-C2 | `RedisTemplate`/`ReactiveRedisTemplate` 는 `execute` 메서드를 통해 설정 가능한 `ScriptExecutor`/`ReactiveScriptExecutor` 에 실행을 위임하며, 기본적으로 이 executor 가 전달된 key/인자를 직렬화하고 스크립트 결과를 역직렬화한다(템플릿의 key/value serializer 사용), 커스텀 직렬화기를 넘기는 오버로드도 존재한다 | "Scripts can be run by calling the execute methods of RedisTemplate and ReactiveRedisTemplate. [...] There is an additional overload that lets you pass custom serializers for the script arguments and the result." | `official-vendor-doc` | typed facade 설계 시 `execute(script, keys, args...)` 시그니처와 직렬화 책임 분담 근거 | 정확한 Java 오버로드 전체 목록, `ScriptOutputType` 열거값과의 매핑 관계는 증명하지 않음(이 페이지에 `ScriptOutputType` 자체가 등장하지 않음) |
|
||||
| SDR-SCRIPT-C3 | 예시 코드는 `execute` 가 `RedisScript`, `List<K>` 형태의 KEYS, 그리고 나머지 가변인자 형태의 ARGV 를 받는 형태임을 보여준다 | "return redisOperations.execute(script, List.of("key"), expectedValue, newValue);" | `official-vendor-doc` | keys/args 전달 방식(List + varargs) 설계 근거 | 이 예시 1건이 모든 오버로드(커스텀 직렬화 포함 포함)를 대표한다고 증명하지 않음 |
|
||||
| SDR-SCRIPT-C4 | `RedisScript` 의 `resultType` 은 `Long`, `Boolean`, `List`, 또는 역직렬화된 값 타입 중 하나여야 하며, 스크립트가 `OK` 같은 throw-away 상태를 반환하면 `null` 도 허용된다 | "The script resultType should be one of Long, Boolean, List, or a deserialized value type. It can also be null if the script returns a throw-away status (specifically, OK)." | `official-vendor-doc` | golden vector / typed facade 반환 타입 계약 설계 | `ScriptOutputType` enum 자체의 존재나 값 목록, 역직렬화 실패 시 예외 타입은 이 페이지에서 확인되지 않음 |
|
||||
| SDR-SCRIPT-C5 | `DefaultRedisScript` 는 애플리케이션 컨텍스트에 단일 인스턴스로 구성해 매 실행마다 스크립트 SHA1 을 재계산하지 않는 것이 권장(ideal)된다 | "It is ideal to configure a single instance of DefaultRedisScript in your application context to avoid re-calculation of the script's SHA1 on every script run." | `official-vendor-doc` | 카탈로그의 `ScriptDescriptor`/`RedisScript` bean 을 싱글턴으로 구성해야 하는 근거 | thread-safety 를 명시적으로 보장한다고는 말하지 않음(싱글턴 권장이 암묵적으로 시사할 뿐), 런타임 카탈로그 재적재/버저닝 전략은 다루지 않음 |
|
||||
| SDR-SCRIPT-C6 | 스크립트 본문을 classpath resource 로 두고 `ResourceScriptSource(new ClassPathResource(...))` 를 통해 `RedisScript.of(scriptSource, Boolean.class)` 로 구성하는 것이 공식 예시의 표준 패턴이다 | "ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource(\"META-INF/scripts/checkandset.lua\")); return RedisScript.of(scriptSource, Boolean.class);" | `official-vendor-doc` | 프로그램 카탈로그의 `.lua` 파일을 classpath resource 로 두고 `ResourceScriptSource` 로 로드하는 설계의 공식 근거 | 이것이 유일하게 지원되는 `ScriptSource` 구현인지, 특정 classpath 경로(`META-INF/scripts/`)가 강제 규약인지는 증명하지 않음 — 예시 1건일 뿐 |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `SDR-SCRIPT-C1`: Spring Data Redis 의 기본 `ScriptExecutor` 가 EVALSHA 우선 + NOSCRIPT 시 EVAL 폴백을 자동 처리 — branch 가 이 재적재 로직을 직접 구현할 필요가 없다는 결정의 직접 근거
|
||||
- `SDR-SCRIPT-C2`, `C3`: `execute(script, keys, args...)` 시그니처와 직렬화 책임 분담 — typed facade 가 감쌀 API 표면
|
||||
- `SDR-SCRIPT-C4`: `RedisScript` 반환 타입 제약 — golden vector 설계 시 참고
|
||||
- `SDR-SCRIPT-C5`: `DefaultRedisScript` 싱글턴 구성 권장 — 카탈로그 bean 설계 근거
|
||||
- `SDR-SCRIPT-C6`: `ResourceScriptSource` classpath resource 패턴 — 카탈로그 파일 배치 근거
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- `ScriptOutputType`, `ScriptingException` 등 구체적 결과 타입 enum·예외 타입 — 이 페이지(`redis/scripting.html`)에는 **등장하지 않는다**(self-grep 으로 부재 확인, fabrication 아님). Javadoc(`org.springframework.data.redis.core.script` 패키지) 또는 별도 fetch 필요
|
||||
- Redis Functions(FCALL) 배포 모드 지원 여부 — 이 페이지는 `eval`/`evalsha` 기반 `RedisScript` 경로만 다룬다
|
||||
- Redis Cluster 환경에서 각 노드별 스크립트 캐시가 독립적으로 유실될 수 있는 문제와 Spring Data Redis 의 대응 방식
|
||||
- fallback(`eval`) 실행 후 서버 스크립트 캐시에 실제로 재적재되어 이후 `evalsha` 가 다시 성공하는지의 명시적 서술(문맥상 당연히 그렇다고 추정되나 이 문장이 직접 말하지는 않음)
|
||||
- **내 프로젝트(ca-skeleton)에 적용하려면 추가 확인이 필요한 것**:
|
||||
- `ScriptOutputType`/`ScriptingException` 관련 Javadoc 확인 (typed facade 의 예외 처리 계층 설계에 필요)
|
||||
- `DefaultRedisScript` 가 실제로 thread-safe 한지 Javadoc/소스 확인 (싱글턴 bean 으로 구성 시 동시성 안전성 전제)
|
||||
- unsafe multi-command recipe ↔ atomic 대체 쌍의 실제 Lua 구현 detail 은 이 자료 범위 밖 — branch 자체 설계 결정
|
||||
|
||||
## 메모
|
||||
|
||||
- 이 페이지는 Redis 서버 프로토콜 수준의 EVALSHA/NOSCRIPT 규약(`raw/official-docs/redis-eval-scripting-atomicity.md`)이 남긴 공백 — "어떤 client 라이브러리가 EVALSHA→NOSCRIPT→재적재를 자동화하는가" — 을 Spring Data Redis 프레임워크 계층에서 메운다. 두 문서를 함께 인용하면 "프로토콜 규약 + 프레임워크 자동화" 근거 사슬이 완성된다.
|
||||
- `ScriptOutputType`/`ScriptingException` 부재는 fabrication 방지를 위해 의도적으로 quote 하지 않았음 — 필요 시 별도 raw-source 로 Javadoc 페이지 수집 권장.
|
||||
- fetch 시점 페이지 버전은 `4.1.0`(Spring Boot 4.0 세대). 향후 버전업 시 재확인 필요.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/redis-eval-scripting-atomicity]] — Redis 서버 자체의 EVALSHA/NOSCRIPT/KEYS 선언 규약 (프로토콜 수준, 본 문서는 프레임워크 수준)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency)
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring for Apache Kafka — Message Listener Containers (AckMode & Concurrency)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | manual acknowledgement 메커니즘(D3 — "application use case 성공 + DB 트랜잭션 커밋 이후에만 ack")을 `ContainerProperties.AckMode` 층에서 무엇으로 표현하는지의 근거. `AckMode.MANUAL`/`MANUAL_IMMEDIATE` 정의·`Acknowledgment` 호출 제약(`nack()` 스레드 제약, `acknowledge(index)` 리스너 스레드 제약)·`ConcurrentMessageListenerContainer` 의 `concurrency` 대 파티션 수 하향 조정 규칙을 확인 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/receiving-messages/message-listener-container.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
|
||||
- 문서 버전: Spring for Apache Kafka reference **4.1.0** (페이지 상단 breadcrumb "Spring for Apache Kafka 4.1.0" 확인). 페이지 자체의 별도 발행일 표기는 없고 각 기능 옆에 도입 버전만 표기됨(예: AckMode 관련 `enable.auto.commit` 강제 false 는 2.3부터, `acknowledge(index)` 부분 배치 커밋은 3.0.10부터).
|
||||
- 발행일: 명시 없음 (버전 이력만 본문에 표기)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton kafka consumer inbox 브랜치(`feature-kafka-consumer-inbox-contract`)의 D3(수동 ack, DB 커밋 이후에만 offset 커밋) 결정을 Spring Kafka 컨테이너 API 레벨의 `AckMode` 값과 `Acknowledgment` 인터페이스 계약으로 정당화하기 위함. 아울러 `ConcurrentMessageListenerContainer` 의 `concurrency` 가 파티션 수를 넘길 때 어떻게 처리되는지(D4/D6 의 "파티션당 컨슈머 1개" 전제와 인접)도 이 페이지에서 확인.
|
||||
|
||||
**요청받은 5개 확인 대상 중 이 URL 페이지에 실제로 있는 것은 2개(AckMode 열거·MANUAL/MANUAL_IMMEDIATE 정의, concurrency 하향 조정)뿐이다.** 나머지 3개(① `Acknowledgment` 를 어느 스레드에서 호출해야 하는지의 "calling consumer thread ... otherwise queued" 계열 문장, ② ack 순서 제약 "must be acknowledged in order ... does not maintain state for each record" 계열 문장, ③ `asyncAcks`/out-of-order ack 의 pause·중복 전달 trade-off 문장)은 **self-grep 결과 이 페이지에 없음을 확인**했다(아래 ## 메모 참조). 1 dispatch = 1 URL 원칙에 따라 다른 페이지 내용을 끌어와 대신 인용하지 않았다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> 원문 그대로. 이 페이지는 "Message Listener Containers" 단일 페이지이며 그 안에 "Committing Offsets" 소제목이 있다. 하위 번호 섹션이 없어 위치는 fetched text 의 line 번호로 표기한다 (self-grep 참조).
|
||||
|
||||
> [Committing Offsets] "MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." (line 447)
|
||||
|
||||
> [Committing Offsets] "MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." (line 450)
|
||||
|
||||
> [Committing Offsets] "MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." (line 454)
|
||||
|
||||
> [Committing Offsets] "The default AckMode is BATCH." (line 429)
|
||||
|
||||
> [Committing Offsets] "nack() can only be called on the consumer thread that invokes your listener." (line 480)
|
||||
|
||||
> [Committing Offsets — partial batch commit] "The method must be called on the listener thread" (line 507, `acknowledge(index)` 제약 목록 중 하나)
|
||||
|
||||
> [Using ConcurrentMessageListenerContainer] "If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." (line 412)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-ACKMODE-C1 | `AckMode.MANUAL` 은 리스너가 `Acknowledgment.acknowledge()` 를 호출할 책임을 지며, 그 이후엔 `BATCH` 와 동일한 커밋 시맨틱(poll() 이 반환한 레코드 전체 처리 후 커밋)이 적용된다 | "MANUAL: The message listener is responsible to acknowledge() the Acknowledgment." + (같은 항목의 다음 문장, self-grep 대상은 아니나 문맥) "After that, the same semantics as BATCH are applied." | `official-vendor-doc` | ca-skeleton D3 의 "use case 성공 이후에만 ack" 를 표현할 `AckMode` 값이 `MANUAL` 임을 확인 | acknowledge() 를 **언제** 호출해야 하는지(DB 커밋 성공 이후)는 애플리케이션 책임이지 프레임워크가 강제하지 않음 — 순서를 지키는 코드는 리뷰/계약 테스트로 별도 보장해야 함(branch note 의 §검증해야 할 주장 이미 인지) |
|
||||
| SPRK-ACKMODE-C2 | `AckMode.MANUAL_IMMEDIATE` 는 `Acknowledgment.acknowledge()` 호출 즉시 offset 을 커밋한다 — `MANUAL` 의 배치형 커밋과 달리 호출마다 즉시 커밋 | "MANUAL_IMMEDIATE: Commit the offset immediately when the Acknowledgment.acknowledge() method is called by the listener." | `official-vendor-doc` | `MANUAL` vs `MANUAL_IMMEDIATE` 선택 — ca-skeleton D3 는 "커밋 시점"만 결정했고 이 자료는 두 모드가 커밋 빈도(배치 대 즉시)에서 갈린다는 것을 보여줄 뿐, 어느 쪽이 D3 의 의도에 더 맞는지는 이 자료만으로 결정 불가 | 두 모드의 처리량/지연 trade-off (벤치마크 없음). `acknowledge(index)` 부분 배치 커밋(SPRK-ACKMODE-C6)은 `MANUAL_IMMEDIATE` 를 요구한다는 점에서 이 claim 과 연결되나 별도 claim으로 분리 |
|
||||
| SPRK-ACKMODE-C3 | `MANUAL`/`MANUAL_IMMEDIATE` 를 쓰려면 리스너가 `AcknowledgingMessageListener` 또는 `BatchAcknowledgingMessageListener` 를 구현해야 한다 | "MANUAL and MANUAL_IMMEDIATE require the listener to be an AcknowledgingMessageListener or a BatchAcknowledgingMessageListener." | `official-vendor-doc` | ca-skeleton consumer 구현 시 리스너 인터페이스 선택 제약 — `Acknowledgment` 를 받으려면 이 인터페이스 중 하나를 구현해야 함(구체 클래스/메서드 명명 자체는 이 자료 범위 밖, §구현 가이드 1 의 `UNSUPPORTED_IMPL_DECISION` 대상) | 어느 인터페이스(단건 vs 배치)를 골라야 하는지는 D4 의 처리 단위 결정에 종속 — 이 자료는 존재 요건만 말함 |
|
||||
| SPRK-ACKMODE-C4 | AckMode 를 명시하지 않으면 기본값은 `BATCH` 이지 `MANUAL` 이 아니다 | "The default AckMode is BATCH." | `official-vendor-doc` | ca-skeleton 이 D3 의 수동 ack 계약을 실제로 적용하려면 `AckMode` 를 **명시적으로 `MANUAL` (또는 `MANUAL_IMMEDIATE`) 로 설정해야 함** — 설정을 빠뜨리면 조용히 `BATCH`(자동 그룹 커밋)로 동작 | `BATCH` 자체가 언제 위험한지(자동 커밋과 유사한 문제)는 이 자료가 직접 논하지 않음 — 이미 수집한 `kafka-consumer-offset-commit-semantics-apache-javadoc` 의 자동 커밋 위험 논의가 별도 근거 |
|
||||
| SPRK-ACKMODE-C5 | `nack()`(2.3 부터 추가된 `Acknowledgment` 메서드)은 **리스너를 호출한 그 consumer 스레드에서만** 호출할 수 있다 | "nack() can only be called on the consumer thread that invokes your listener." | `official-vendor-doc` | `Acknowledgment` 인터페이스의 적어도 한 메서드(`nack`)가 리스너/consumer 스레드에 강하게 묶여 있다는 것을 확인 — "별도 워커 스레드에서 ack 관련 호출을 해도 되는가"라는 branch 의 질문에 **`nack()` 에 한해서는 "아니오"** 라고 명시적으로 답함 | **`acknowledge()` 자체(단건 리스너, `MANUAL` 모드)에 동일한 스레드 제약이 있다고 이 문장만으로 단정할 수 없다** — 이 페이지가 `acknowledge()` 에 대해 그런 문장을 쓰는 곳은 SPRK-ACKMODE-C6(부분 배치 커밋 변형)뿐이며, 그건 다른 API(`acknowledge(index)`, 배치 리스너, `MANUAL_IMMEDIATE` 한정)다. 사용자가 원한 "calling consumer thread ... otherwise queued" 문장은 이 페이지에 없음(미발견, 아래 Usage Boundaries) |
|
||||
| SPRK-ACKMODE-C6 | 3.0.10 부터 추가된 배치 리스너의 부분 배치 커밋(`acknowledge(index)`)은 `AckMode.MANUAL_IMMEDIATE` 를 요구하고, **"리스너 스레드에서 호출되어야 한다"** 는 제약을 명시적으로 건다(그 외에도 List 소비 요건, index 범위·단조 증가 요건이 있으며 위반 시 `IllegalArgumentException`/`IllegalStateException`) | "The method must be called on the listener thread" (같은 목록의 다른 제약: "AckMode.MANUAL_IMMEDIATE is required") | `official-vendor-doc` | `acknowledge(index)` 부분 배치 커밋을 쓸 경우의 스레드 제약 — 이 자료가 제공하는 **가장 근접한** "Acknowledgment 관련 호출은 리스너/consumer 스레드에서" 근거 | 단건 레코드 리스너의 일반 `acknowledge()` 호출(`MANUAL` 모드, 배치 아님)에도 동일 제약이 명시돼 있는지는 **이 페이지에서 확인 불가** — 그 문장은 별도 페이지("Manually Committing Offsets")에 있을 가능성이 있으나 본 dispatch 범위 밖 |
|
||||
| SPRK-ACKMODE-C7 | `ConcurrentMessageListenerContainer` 의 `concurrency` 가 배정 가능한 `TopicPartition` 수보다 크면, 각 delegate 컨테이너가 파티션 하나씩만 갖도록 concurrency 가 **하향 조정**된다 | "If the concurrency is greater than the number of TopicPartitions, the concurrency is adjusted down such that each container gets one partition." | `official-vendor-doc` | ca-skeleton 의 concurrency 설정 상한 근거 — 파티션 수를 넘는 concurrency 값은 무의미(초과분 컨테이너가 유휴 상태로 남지 않고 애초에 만들어지지 않음). D4/D6 이 전제하는 "파티션 하나는 컨슈머 그룹 안에서 한 컨테이너만 소비"라는 가정과 **정합**되는 Spring 프레임워크 레벨 동작 | 이 문장은 **Spring 컨테이너의 파티션 분배 동작**을 말할 뿐, Kafka 프로토콜 레벨에서 "파티션 하나는 그룹 내 정확히 한 consumer 가 소비한다"는 것 자체의 공식 Kafka 문서 근거(branch `SOURCE_GAP-1`, `needs-confirmation`)를 대신하지 않는다 — Spring 이 이렇게 동작하는 것은 그 Kafka 레벨 보장을 **전제로 구현**했을 뿐, 이 문서가 Kafka 프로토콜 자체를 규정하는 문서는 아님 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모든 claim 은 `official-vendor-doc` (Spring 공식 reference — RFC/IETF 표준은 아니므로 `official-standard` 로 격상하지 않음).
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-ACKMODE-C1`~`C4`: `AckMode` 값의 열거와 커밋 시점 정의, 기본값이 `BATCH` 라는 것, `MANUAL`/`MANUAL_IMMEDIATE` 사용 시 리스너 인터페이스 요건
|
||||
- `SPRK-ACKMODE-C5`: `nack()` 은 consumer/리스너 스레드에서만 호출 가능
|
||||
- `SPRK-ACKMODE-C6`: `acknowledge(index)`(부분 배치 커밋, `MANUAL_IMMEDIATE` 한정)는 리스너 스레드에서 호출돼야 한다는 명시 제약
|
||||
- `SPRK-ACKMODE-C7`: `concurrency` > 파티션 수일 때 하향 조정
|
||||
- 이 자료가 증명하지 않는 것 (branch 가 요청했으나 이 URL 페이지에서 **미발견** — self-grep 결과 0건):
|
||||
- `Acknowledgment.acknowledge()`(단건 레코드 리스너, `MANUAL` 모드) 를 **어느 스레드에서 호출해야 하는지**에 대한 일반 규칙 — 사용자가 지정한 "commit will be performed immediately if the Acknowledgment is acknowledged on the calling consumer thread; otherwise, the acks will be queued" 계열 문장은 `grep -nF`로 "calling consumer thread"/"queued" 검색 시 이 페이지에서 0건. `nack()`(C5)과 `acknowledge(index)`(C6)에 대해서만 스레드 제약이 명시돼 있고, 일반 `acknowledge()` 에 대한 동일 문장은 이 페이지 범위 밖(다른 페이지 "Manually Committing Offsets" 가능성 — 본 dispatch 는 여기서 멈춤, 1 dispatch = 1 URL)
|
||||
- ack **순서** 제약 — "acknowledgments must be acknowledged in order, because Kafka does not maintain state for each record, only a committed offset for each group/partition" 계열 문장 — `grep -nF`로 "does not maintain state"/"in order" 검색 시 미발견(단, "Out of Order Commits" 라는 기능명 자체는 이 페이지에 1회 등장 — line 482, 483: `nack()` 은 Out of Order Commits 사용 시 허용 안 됨. 이 기능의 정의 문장 자체는 이 페이지에 없음)
|
||||
- `asyncAcks`/out-of-order ack 채택 시 consumer 가 pause 되고 중복 전달 가능성이 커진다는 trade-off 문장 — `grep -ni "asyncAck"` 결과 0건
|
||||
- 파티션 단위 pause/resume API — 이미 별도 raw 문서([[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]] `SPRK-PAUSE-C5`)에서 동일하게 미발견 확인됨
|
||||
- `commitSync`/`commitAsync` 중 어느 것을 쓸지의 권고 (이 페이지는 `syncCommits` 컨테이너 프로퍼티가 그 둘을 스위칭한다는 것만 언급하고 권고는 안 함 — 이미 branch D3 의 Open Risk 로 기록됨)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `Acknowledgment.acknowledge()` 의 일반적 스레드 제약(있다면)을 확인하려면 "Manually Committing Offsets" 페이지(`Message Listeners` 섹션의 sibling 페이지, 이 문서 최하단 nav 에서 확인됨)를 별도 `wiki-source-summarizer` dispatch 로 조사해야 한다
|
||||
- ack 순서 제약과 `asyncAcks` trade-off 도 마찬가지로 별도 페이지 조사 필요 — 이 페이지에 없다고 해서 Spring Kafka 문서 전체에 없다는 뜻은 아님
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- self-grep 실행 결과 (fetched text `/tmp/.../scratchpad/source-fetch-20260728-193000.txt`, 658줄):
|
||||
- `calling consumer thread` → 0건
|
||||
- `queued` → 0건
|
||||
- `does not maintain state` → 0건
|
||||
- `asyncAck` (대소문자 무관) → 0건
|
||||
- `Out of Order Commits` → 1건 (line 482, 기능명만 등장 — 정의 문장 없음)
|
||||
- 이 페이지 자체가 "Committing Offsets" 절 안에서 `AckMode` 7종(RECORD/BATCH/TIME/COUNT/COUNT_TIME/MANUAL/MANUAL_IMMEDIATE)을 모두 정의하지만, 이 raw 문서에는 branch 의 관심사(MANUAL/MANUAL_IMMEDIATE 대비, 스레드, concurrency)에 직접 관련된 것만 발췌했다. RECORD/BATCH/TIME/COUNT/COUNT_TIME 의 정의 자체도 필요해지면 이 문서에 claim 추가만으로 확장 가능(재-fetch 불필요, 같은 텍스트 파일에 이미 있음).
|
||||
- 추가로 봐야 할 동일 출처 페이지: 사이트 nav 에서 확인된 "Manually Committing Offsets" (이 페이지 바로 다음 sibling, `Message Listeners` 섹션 하위) — `Acknowledgment.acknowledge()` 스레드 규칙과 ack 순서 제약이 있을 가능성이 높음. 별도 URL, 별도 dispatch 필요.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]]` (같은 "Message Listener Containers" 상위 카테고리, pause/resume 근거), `[[raw/official-docs/kafka-consumer-offset-commit-semantics-apache-javadoc]]` (Kafka 레벨 offset commit 시맨틱), `[[raw/official-docs/kafka-incremental-cooperative-rebalance-kip429]]` (concurrency/rebalance 인접 주제)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — DefaultErrorHandler Fatal Exceptions, BackOff Thread Suspension, and Dead Letter Topic Defaults
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/annotation-error-handling.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, dead-letter-queue, exponential-backoff]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring for Apache Kafka — DefaultErrorHandler Fatal Exceptions, BackOff Thread Suspension, and Dead Letter Topic Defaults
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Spring for Apache Kafka **4.1.0** reference ("Stable" 채널) — "Using Spring for Apache Kafka" §"Handling Exceptions".
|
||||
|
||||
## source_type
|
||||
|
||||
`official-doc` — Spring for Apache Kafka 공식 reference 문서 (docs.spring.io, Broadcom/VMware Tanzu 가 관리하는 Spring 프로젝트).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | (1) poison/역직렬화 예외 분류 시 `DefaultErrorHandler` 기본 fatal 예외 6종을 non-retryable 기준으로 삼을 근거, (2) blocking retry 의 backoff 대기가 consumer 스레드를 정지시키므로 `max.poll.interval.ms` 를 넘는 지연에는 `ContainerPausingBackOffHandler` 가 필요하다는 메커니즘 근거, (3) DLT 기본 명명(`<originalTopic>-dlt`)·파티션 요건과 recoverer 가 producer(`KafkaTemplate`/`KafkaOperations`)를 요구한다는 근거, (4) recoverer 를 명시 구성하지 않으면 재시도 소진 후 기본 동작이 "로그만"이라는 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/annotation-error-handling.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka 프로젝트)
|
||||
- 발행일: 명시 없음 (버전 4.1.0 reference, "Stable" 채널 — 문서 자체에 발행 연월일 필드 없음)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-consumer-inbox-contract` 브랜치가 poison exception 분류·blocking retry backoff·DLT 명명·기본 recoverer 동작을 결정할 때 필요한 **공식 기본값 사실**을 확보하기 위함. 어떤 예외를 "재시도 무의미"로 볼지, backoff 를 얼마로 잡을지, DLT 토픽명을 어떻게 override 할지는 이 문서가 아니라 branch 의 별도 결정이며, 이 문서는 그 결정이 마주치는 **프레임워크 기본 동작의 경계**만 증명한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§DefaultErrorHandler — Skipping Retries for Specific Exceptions] "The DefaultErrorHandler considers certain exceptions to be fatal, and retries are skipped for such exceptions; the recoverer is invoked on the first failure. The exceptions that are considered fatal, by default, are:
|
||||
>
|
||||
> DeserializationException
|
||||
> MessageConversionException
|
||||
> ConversionException
|
||||
> MethodArgumentResolutionException
|
||||
> NoSuchMethodException
|
||||
> ClassCastException
|
||||
>
|
||||
> since these exceptions are unlikely to be resolved on a retried delivery." (원문 lines 435–450, 열거형 리스트이므로 elide 미적용 — 6개 예외명 자체가 claim 의 핵심 내용이라 축약하지 않고 원문 순서대로 보존)
|
||||
|
||||
> [§Back Off Handlers] "Error handlers such as the DefaultErrorHandler use a BackOff to determine how long to wait before retrying a delivery. Starting with version 2.9, you can configure a custom BackOffHandler. The default handler simply suspends the thread until the back off time passes (or the container is stopped). The framework also provides the ContainerPausingBackOffHandler which pauses the listener container until the back off time passes and then resumes the container. This is useful when the delays are longer than the max.poll.interval.ms consumer property." (원문 lines 344–348)
|
||||
|
||||
> [§Publishing Dead-letter Records] "The recoverer requires a KafkaTemplate<Object, Object>, which is used to send the record." (원문 line 1043)
|
||||
|
||||
> [§Publishing Dead-letter Records] "By default, the dead-letter record is sent to a topic named <originalTopic>-dlt (the original topic name suffixed with -dlt) and to the same partition as the original record. Therefore, when you use the default resolver, the dead-letter topic must have at least as many partitions as the original topic." (원문 lines 1046–1047)
|
||||
|
||||
> [§DefaultErrorHandler] "The default recoverer logs the failed record after retries are exhausted. You can use a custom recoverer, or one provided by the framework such as the DeadLetterPublishingRecoverer." (원문 line 411; 같은 subsection line 362 "By default, after ten failures, the failed record is logged (at the ERROR level)." 도 동일 사실을 뒷받침)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-ERRH-C1 | `DefaultErrorHandler` 는 6종 예외(`DeserializationException`, `MessageConversionException`, `ConversionException`, `MethodArgumentResolutionException`, `NoSuchMethodException`, `ClassCastException`)를 기본으로 fatal 로 분류해 재시도를 건너뛰고 첫 실패에서 바로 recoverer 를 호출한다 | "The DefaultErrorHandler considers certain exceptions to be fatal, and retries are skipped for such exceptions; the recoverer is invoked on the first failure." + 6종 나열 + "since these exceptions are unlikely to be resolved on a retried delivery." (lines 435–450) | `official-vendor-doc` | Spring for Apache Kafka 4.1.0 `DefaultErrorHandler`(및 `DefaultAfterRollbackProcessor`, 동일 목록이 line 908–923 에 재등장)의 기본 classification 맵 | 이 6종이 ca-skeleton 이 실제로 채택해야 할 poison-exception 전체 목록이라는 것은 증명하지 않는다 — 프레임워크는 `addNotRetryableExceptions()`/`setClassifications()` 로 추가·교체를 명시적으로 지원하므로, 프로젝트별 poison 기준은 별도 branch 결정 |
|
||||
| SPRK-ERRH-C2 | `DefaultErrorHandler` 의 기본 `BackOffHandler` 는 backoff 시간이 지날 때까지 **consumer 스레드를 정지(suspend)**시키며, 이 지연이 `max.poll.interval.ms` 보다 길 때를 위해 프레임워크가 별도로 `ContainerPausingBackOffHandler`(container 자체를 pause 후 resume)를 제공한다 | "The default handler simply suspends the thread until the back off time passes (or the container is stopped)." + "The framework also provides the ContainerPausingBackOffHandler ... This is useful when the delays are longer than the max.poll.interval.ms consumer property." (lines 346–348) | `official-vendor-doc` | Spring for Apache Kafka 4.1.0 `DefaultErrorHandler`/`BackOffHandler` 기본 동작과 `max.poll.interval.ms` 초과 우려가 있는 backoff 설정 | 이 인용은 "지연이 `max.poll.interval.ms` 를 넘으면 반드시 rebalance 가 발생한다"는 인과 관계를 **명시적으로 진술하지 않는다** — 그 인과는 Kafka consumer group 프로토콜(broker 측 rebalance 로직) 일반 지식이며 본 페이지의 claim 범위 밖. 본 페이지가 직접 증명하는 것은 "기본 handler 는 스레드를 정지시킨다"와 "그래서 `ContainerPausingBackOffHandler` 가 존재한다"까지다 |
|
||||
| SPRK-ERRH-C3 | 기본 리졸버 사용 시 dead-letter 레코드는 `<originalTopic>-dlt`(원본 토픽명 + `-dlt` suffix) 토픽의 **원본과 동일한 partition** 으로 전송되며, 따라서 DLT 토픽은 원본 토픽 이상의 partition 수를 가져야 한다 | "By default, the dead-letter record is sent to a topic named <originalTopic>-dlt (the original topic name suffixed with -dlt) and to the same partition as the original record." + "the dead-letter topic must have at least as many partitions as the original topic." (lines 1046–1047) | `official-vendor-doc` | `DeadLetterPublishingRecoverer` 의 기본(커스텀 리졸버 미지정) destination 계산 | 커스텀 `BiFunction<ConsumerRecord, Exception, TopicPartition>` 리졸버를 지정하면 이 명명 규칙이 적용되지 않는다는 것도 같은 문서가 별도로 보여준다(예: line 1055–1063, line 1159 `"topic-dlt"` 커스텀 예시) — 즉 이 claim 은 **기본값**에 한정되며 ca-skeleton 이 커스텀 리졸버를 쓸지는 별도 결정 |
|
||||
| SPRK-ERRH-C4 | `DeadLetterPublishingRecoverer` 는 레코드를 보내기 위해 producer 참조(`KafkaTemplate<Object, Object>`, 또는 `KafkaOperations<K, V>` 타입 파라미터를 받는 오버로드)를 요구한다 — producer 없이는 구성할 수 없다 | "The recoverer requires a KafkaTemplate<Object, Object>, which is used to send the record." (line 1043) + 코드 예시 "public ErrorHandler eh(KafkaOperations<String, String> template) {" (line 1147) + "Map<Class<?>, KafkaOperations<?, ?>> templates = new LinkedHashMap<>();" (line 1113, 멀티 템플릿 오버로드) | `official-vendor-doc` | `DeadLetterPublishingRecoverer` 의 생성자 요구사항 (단일 템플릿 및 멀티 템플릿 오버로드 모두) | 본문 서술(line 1043)은 정확히 `KafkaTemplate<Object, Object>` 라는 단어를 쓰고, "KafkaOperations" 라는 단어 자체는 코드 예시(line 1113, 1147)에서만 등장한다 — "recoverer 가 정확히 `KafkaOperations` 인터페이스 타입으로 문서화되어 있다"는 문장은 본문 prose 인용만으로는 과잉 해석이며, 코드 예시 조합으로 뒷받침한 것임을 구분할 것 (자세한 것은 `## 메모` 참조) |
|
||||
| SPRK-ERRH-C5 | recoverer 를 명시적으로 구성하지 않으면, `DefaultErrorHandler` 는 재시도가 소진된 뒤 기본적으로 **실패 레코드를 로그만 남기고**(기본값 10회 실패 후 ERROR 레벨) DLT 등으로 보내지 않는다 | "The default recoverer logs the failed record after retries are exhausted. You can use a custom recoverer, or one provided by the framework such as the DeadLetterPublishingRecoverer." (line 411) + "By default, after ten failures, the failed record is logged (at the ERROR level)." (line 362) | `official-vendor-doc` | `DefaultErrorHandler` 에 커스텀 recoverer/`BackOff` 를 지정하지 않은 기본 구성 | "10회 실패" 라는 수치는 기본 `FixedBackOff` 설정에서 파생되는 값이며, `BackOff`(재시도 횟수·지연)를 다른 값으로 구성하면 실패까지 걸리는 시도 횟수도 달라진다 — "항상 10회" 라는 뜻은 아니다 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모두 `official-vendor-doc` — Spring for Apache Kafka 는 Spring 프로젝트 공식 reference 문서이며 RFC/표준(`official-standard`)은 아니다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-ERRH-C1`: `DefaultErrorHandler` 기본 fatal 예외 6종과 "재시도 skip + 첫 실패에 recoverer 호출" 동작
|
||||
- `SPRK-ERRH-C2`: 기본 `BackOffHandler` 가 consumer 스레드를 정지시킨다는 것과, 그로 인해 `max.poll.interval.ms` 초과 우려가 있을 때 `ContainerPausingBackOffHandler` 가 제공된다는 것
|
||||
- `SPRK-ERRH-C3`: 기본 리졸버의 DLT 명명(`<originalTopic>-dlt`) + 동일 partition 전송 + partition 수 요건
|
||||
- `SPRK-ERRH-C4`: `DeadLetterPublishingRecoverer` 가 producer(`KafkaTemplate`/`KafkaOperations` 계열) 없이는 구성 불가하다는 것
|
||||
- `SPRK-ERRH-C5`: recoverer 미구성 시 기본 동작이 "로그만"이라는 것
|
||||
- 이 자료가 증명하지 **않는** 것:
|
||||
- **backoff 지연이 `max.poll.interval.ms` 를 넘으면 반드시 rebalance 가 발생한다는 인과 자체.** 본 페이지는 "스레드 정지" 사실과 "그래서 `ContainerPausingBackOffHandler` 가 유용하다"는 권고만 진술한다 — rebalance 트리거 메커니즘은 Kafka consumer group 프로토콜의 별도 공식 문서로 보강 필요(`SPRK-ERRH-C2` Does not prove 참조).
|
||||
- ca-skeleton 이 실제로 채택해야 할 poison-exception 전체 목록 — 6종은 Spring 기본값일 뿐, 확장/교체가 명시적으로 지원된다.
|
||||
- "recoverer 가 `KafkaOperations` 인터페이스로 문서화되어 있다"는 prose 수준 주장 — 본문 prose 는 `KafkaTemplate<Object, Object>` 로 서술하고, `KafkaOperations` 는 코드 예시에서만 파라미터 타입으로 등장한다.
|
||||
- DLT 파티션 수·명명 override 시의 동작 — 커스텀 리졸버를 쓰면 `<originalTopic>-dlt` 규칙이 적용되지 않는다는 것도 같은 문서가 별도 예시로 보여준다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 이 실제로 설정할 `max.poll.interval.ms` 값과 backoff 지연 상한이 서로 충돌하지 않는지 — Kafka consumer 공식 설정 문서(`max.poll.interval.ms`, `max.poll.records`)로 별도 확인
|
||||
- poison exception 목록에 ca-skeleton 도메인 역직렬화/스키마 예외를 추가할지 여부 — branch 자체 결정
|
||||
- DLT 파티션 수를 원본과 동일하게 프로비저닝할지, 토픽 생성 자동화(Kafka Streams/Admin API)로 강제할지 — 별도 인프라 결정
|
||||
|
||||
## 메모
|
||||
|
||||
- `KafkaOperations` vs `KafkaTemplate`: 본문 prose 는 `KafkaTemplate<Object, Object>` 를 요구한다고 명시하고, `KafkaOperations` 라는 단어는 이 페이지의 코드 예시(멀티 템플릿 맵, 커스텀 destination resolver 예시)에서만 파라미터 타입으로 등장한다. `KafkaTemplate` 이 `KafkaOperations` 인터페이스의 구현체라는 것은 Spring Kafka 일반 지식이지만, **이 페이지 자체가 그 상속 관계를 설명하는 문장은 없다** — 다음 문서 작업자는 이 구분을 유지할 것 (branch 의 D-row 에서 "producer 타입은 `KafkaTemplate` 구현체(또는 `KafkaOperations` 파라미터로 받는 오버로드)" 로 정확히 표현 권장).
|
||||
- 추가로 봐야 할 동일 출처 페이지: Kafka consumer 공식 문서의 `max.poll.interval.ms`/`max.poll.records` 설정 — `SPRK-ERRH-C2` 의 "얼마나 길어야 rebalance 위험인지" 를 별도로 보강해야 함. Non-Blocking Retries(`@RetryableTopic`) 페이지도 blocking retry 대안 비교 시 참고.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/kafka-message-delivery-semantics-design]], [[raw/official-docs/spring-kafka-sending-messages-kafkatemplate]] — 같은 Spring for Apache Kafka 4.1.0 reference 의 인접 주제(전달 시맨틱, producer 전송 결과)
|
||||
- 추가 확보 필요 (아직 raw 에 없음): Kafka consumer 공식 문서의 `max.poll.interval.ms`/`max.poll.records` 설정 페이지 — `SPRK-ERRH-C2` 보강용
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — ErrorHandlingDeserializer (Poison Record Handling)
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/serdes.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, dead-letter-queue]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring for Apache Kafka — ErrorHandlingDeserializer (Poison Record Handling)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton consumer 가 역직렬화 실패(poison message)를 "리스너 호출 이전 단계에서 감지해 error handler/DLT 경로로 회수"하는 방식을 채택하는 근거 — `ErrorHandlingDeserializer` 가 실패 시 null 값 + `DeserializationException` 헤더(원인 + raw bytes)를 실어 보내고, 컨테이너가 리스너 대신 error handler 를 호출한다는 공식 메커니즘 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/serdes.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
|
||||
- 문서 버전: Spring for Apache Kafka reference **4.1.0** (페이지 상단 breadcrumb "Spring for Apache Kafka 4.1.0" 및 페이지 메타 `content="4.1.0"` 확인). `ErrorHandlingDeserializer` 절 본문 자체에 버전 도입 표기는 없으나, `Validator` 추가 기능은 "Starting with version 3.1"로 명시됨
|
||||
- 발행일: 페이지 자체 발행일 표기 없음 (기능별 "Starting with version N.N" 문구만 본문에 존재)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton kafka consumer inbox 브랜치(`feature-kafka-consumer-inbox-contract`)의 "rebalance·`max.poll` 처리, poison/역직렬화 실패 분류" 범위에서, poison message 를 리스너 도달 이전 단계(deserializer 레벨)에서 걸러 error handler/DLT 로 회수하는 설계를 공식 메커니즘으로 정당화하기 위함. `ErrorHandlingDeserializer` 가 무엇을 반환하고 컨테이너가 어떻게 반응하는지의 원문 계약을 근거로 남긴다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> 원문 그대로. 이 페이지는 단일 섹션("Using ErrorHandlingDeserializer")이며 하위 번호 섹션이 없어, 위치는 fetched text(`/tmp/source-fetch-20260728-171842.txt`)의 line 번호로 표기한다 (self-grep 참조).
|
||||
|
||||
> [Using ErrorHandlingDeserializer] "When a deserializer fails to deserialize a message, Spring has no way to handle the problem, because it occurs before the poll() returns." (line 578)
|
||||
|
||||
> [Using ErrorHandlingDeserializer] "If the delegate fails to deserialize the record content, the ErrorHandlingDeserializer returns a null value and a DeserializationException in a header that contains the cause and the raw bytes." (line 581)
|
||||
|
||||
> [Using ErrorHandlingDeserializer] "When you use a record-level MessageListener, if the ConsumerRecord contains a DeserializationException header for either the key or value, the container's ErrorHandler is called with the failed ConsumerRecord." "The record is not passed to the listener." (line 582–583)
|
||||
|
||||
> [Using ErrorHandlingDeserializer] "You can use the DefaultKafkaConsumerFactory constructor that takes key and value Deserializer objects and wire in appropriate ErrorHandlingDeserializer instances that you have configured with the proper delegates. Alternatively, you can use consumer configuration properties (which are used by the ErrorHandlingDeserializer) to instantiate the delegates. The property names are ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS and ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS." (line 593–595)
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-EHD-C1 | 표준 Kafka `Deserializer` 는 `poll()` 반환 이전에 발생하는 역직렬화 실패를 리스너 레벨에서 처리할 방법이 없고, 이 문제를 해결하기 위해 `ErrorHandlingDeserializer` 가 도입되었다 | "When a deserializer fails to deserialize a message, Spring has no way to handle the problem, because it occurs before the poll() returns." + "To solve this problem, the ErrorHandlingDeserializer has been introduced." | `official-vendor-doc` | poison message 문제의 근본 원인(왜 일반 deserializer 로는 리스너 레벨 에러 처리가 불가능한지)과 `ErrorHandlingDeserializer` 도입 동기 | `ErrorHandlingDeserializer` 외 다른 해결책(예: try-catch 를 감싼 custom deserializer)의 존재·우열 비교 |
|
||||
| SPRK-EHD-C2 | 위임(delegate) deserializer 가 레코드 내용 역직렬화에 실패하면, `ErrorHandlingDeserializer` 는 `null` 값과, cause + raw bytes 를 담은 `DeserializationException` 헤더를 반환한다 | "If the delegate fails to deserialize the record content, the ErrorHandlingDeserializer returns a null value and a DeserializationException in a header that contains the cause and the raw bytes." | `official-vendor-doc` | `ErrorHandlingDeserializer` 가 key 또는 value deserializer 로 설정된 경우의 실패 시 반환 값·헤더 계약 | batch listener 컨테이너에서 이 헤더가 동일하게 자동 노출/처리되는지 (문서 후반 별도 절 "Batch Listener Error Handling" 에서 수동 검사 코드로 별도 처리됨 — 자동 아님) |
|
||||
| SPRK-EHD-C3 | record-level `MessageListener` 사용 시, `ConsumerRecord` 에 `DeserializationException` 헤더(key 또는 value)가 있으면 컨테이너의 `ErrorHandler` 가 실패한 `ConsumerRecord` 와 함께 호출되고, 그 레코드는 리스너로 전달되지 않는다 | "When you use a record-level MessageListener, if the ConsumerRecord contains a DeserializationException header for either the key or value, the container's ErrorHandler is called with the failed ConsumerRecord." + "The record is not passed to the listener." | `official-vendor-doc` | record-level(단일 레코드) `@KafkaListener` 의 poison record 라우팅 경로 — 리스너 도달 이전에 error handler 로 우회된다는 계약. 본 branch 의 "리스너 호출 이전 단계에서 감지해 error handler/DLT 경로로 회수" 결정의 직접 근거 | DLT 로의 실제 라우팅(예: `DefaultErrorHandler` + `DeadLetterPublishingRecoverer` 조합)은 이 페이지에 명시되지 않음 — 별도 자료("Handling Exceptions" 페이지) 확인 필요. batch listener 의 라우팅 경로도 별도(문서 후반 절 참조) |
|
||||
| SPRK-EHD-C4 | 위임 deserializer 는 `DefaultKafkaConsumerFactory` 생성자로 직접 wiring 하거나, `ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS` / `ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS` 컨슈머 설정 프로퍼티로 지정할 수 있다 | "You can use the DefaultKafkaConsumerFactory constructor that takes key and value Deserializer objects and wire in appropriate ErrorHandlingDeserializer instances that you have configured with the proper delegates. Alternatively, you can use consumer configuration properties (which are used by the ErrorHandlingDeserializer) to instantiate the delegates. The property names are ErrorHandlingDeserializer.KEY_DESERIALIZER_CLASS and ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS." | `official-vendor-doc` | `ErrorHandlingDeserializer` 에 실제 위임 deserializer(예: `JsonDeserializer`)를 지정하는 두 가지 설정 방법(생성자 vs 프로퍼티) | Spring Boot `spring.kafka.*` 프로퍼티에서 이 설정이 어떤 정확한 키로 자동 매핑되는지(이 페이지는 raw `ConsumerConfig` 프로퍼티 예시만 제공) |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모든 claim 은 `official-vendor-doc` (Spring 공식 reference — RFC/IETF 표준이 아니므로 `official-standard` 로 격상하지 않음)이다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-EHD-C1`: 일반 deserializer 로는 poll() 이전 실패를 리스너 레벨에서 처리 불가능하다는 문제와 `ErrorHandlingDeserializer` 도입 동기
|
||||
- `SPRK-EHD-C2`: 위임 deserializer 실패 시 null + `DeserializationException` 헤더 반환 계약
|
||||
- `SPRK-EHD-C3`: record-level 리스너에서 실패 레코드가 리스너 대신 컨테이너 `ErrorHandler` 로 라우팅된다는 계약 — **리스너 호출 이전 감지** 결정의 직접 근거
|
||||
- `SPRK-EHD-C4`: delegate deserializer 를 지정하는 두 가지 설정 방법(생성자 wiring / 컨슈머 프로퍼티)
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- DLT(Dead Letter Topic)로의 실제 라우팅 구현(`DefaultErrorHandler` + `DeadLetterPublishingRecoverer` 조합의 상세 동작) — 이 페이지에는 등장하지 않음
|
||||
- batch listener 컨테이너에서의 동일 계약(별도 수동 헤더 검사 코드 필요 — 문서 후반 "Batch Listener Error Handling" 절에서 별도로 다룸, 본 인용 범위 밖)
|
||||
- 이 메커니즘이 ca-skeleton 의 handler/schema/version allowlist 설계와 어떻게 결합되어야 하는지
|
||||
- retry topic 또는 지연 재시도 정책과의 상호작용(이 페이지 미언급 — Non-Blocking Retries 페이지 별도 확인 필요)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- DLT 라우팅과 감사된 replay 를 위한 실제 error handler 구성(`DefaultErrorHandler`/`DeadLetterPublishingRecoverer`)은 "Handling Exceptions" 공식 페이지를 별도 `wiki-source-summarizer` dispatch 로 조사한 뒤 확정
|
||||
- batch listener 를 채택할 경우 poison record 검출 코드를 리스너 본문에 직접 작성해야 한다는 것(자동 아님)을 `/branch-spec` 결정에 반영할지 여부
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- 사용자가 최초 요청한 인용 문구는 backtick(code font) 표기를 포함했으나, 실제 원문 HTML 은 `<code>` 태그로 감싼 것이지 별도 마크다운 backtick 문자가 존재하지 않는다. 본 raw 문서의 verbatim 인용은 backtick 을 제거한 원문 텍스트 그대로이며, 의미는 사용자 요청과 100% 일치함 (self-grep 확인 완료).
|
||||
- WebFetch 도구의 1차 결과는 AI 요약 모델을 거쳐 문장이 재구성되어("delegates to a real deserializer and returns...") 있었다 — verbatim 요구사항에 부적합해 폐기하고, `curl` 로 원본 HTML 을 직접 받아 태그만 제거한 텍스트로 self-grep 을 재실행했다.
|
||||
- 추가로 봐야 할 동일 출처 페이지: "Handling Exceptions" (DLT/DefaultErrorHandler/DeadLetterPublishingRecoverer 실제 구성) — 별도 URL, 별도 dispatch 필요.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]]`, `[[raw/official-docs/spring-kafka-non-blocking-retry-topic-ordering-loss]]`
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — Pausing and Resuming Listener Containers
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/pause-resume.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, bounded-queue]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring for Apache Kafka — Pausing and Resuming Listener Containers
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | bounded queue 포화 시 consumer 를 컨슈머 그룹에서 이탈시키지 않고 소비만 멈추는 backpressure 메커니즘으로 컨테이너 `pause()`/`resume()` 을 채택하는 근거 — pause 중에도 `poll()` 이 계속 호출되어 rebalance 를 회피한다는 공식 동작과 pause/resume 반영 시점(poll 경계 vs `pauseImmediate`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/pause-resume.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
|
||||
- 문서 버전: Spring for Apache Kafka reference **4.1.0** (페이지 상단 breadcrumb "Spring for Apache Kafka 4.1.0" 확인. 본문에 인용된 기능 자체는 2.1.3 / 2.1.5 / 2.9 에서 도입되었고 4.1.0 문서에도 동일 내용으로 유지됨 — 페이지 자체의 별도 발행일 표기는 없음)
|
||||
- 발행일: 명시 없음 (버전 이력만 본문에 표기)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton kafka consumer inbox 브랜치(`feature-kafka-consumer-inbox-contract`)의 bounded queue backpressure 설계에서 "큐 포화 시 소비를 멈추되 그룹에서 이탈하지 않는다"는 요구를, 컨테이너 `pause()`/`resume()` 이 `poll()` 을 계속 호출해 rebalance 를 회피한다는 공식 메커니즘으로 정당화하기 위함. 아울러 pause/resume 이 언제 반영되는지(poll 경계 vs `pauseImmediate`)를 근거로 남긴다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> 원문 그대로. 이 페이지는 단일 섹션("Pausing and Resuming Listener Containers")이며 하위 번호 섹션이 없어, 위치는 fetched text 의 line 번호로 표기한다 (self-grep 참조).
|
||||
|
||||
> [Pausing and Resuming Listener Containers] "A pause() takes effect just before the next poll(); a resume() takes effect just after the current poll() returns." (line 263)
|
||||
|
||||
> [Pausing and Resuming Listener Containers] "When a container is paused, it continues to poll() the consumer, avoiding a rebalance if group management is being used, but it does not retrieve any records." (line 264)
|
||||
|
||||
> [Pausing and Resuming Listener Containers] "Starting with version 2.9, a new container property pauseImmediate, when set to true, causes the pause to take effect after the current record is processed." (line 273)
|
||||
|
||||
> [Pausing and Resuming Listener Containers] "By default, the pause takes effect when all the records from the previous poll have been processed." (line 274)
|
||||
|
||||
> [Pausing and Resuming Listener Containers — Checking Pause Status] "isConsumerPaused() returns true if all Consumer instances have actually paused." (line 269)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-PAUSE-C1 | 컨테이너 `pause()`/`resume()` 은 즉시 적용되지 않고 poll() 경계에서 반영된다 — pause 는 다음 poll() 직전에, resume 은 현재 poll() 반환 직후에 적용 | "A pause() takes effect just before the next poll(); a resume() takes effect just after the current poll() returns." | `official-vendor-doc` | 기본(non-`pauseImmediate`) listener container 의 pause/resume 타이밍 | `pauseImmediate=true` 일 때의 타이밍(→ C3), 파티션 단위 pause API 의 타이밍(본 URL 에 없음 → C5) |
|
||||
| SPRK-PAUSE-C2 | 컨테이너가 paused 상태여도 `poll()` 호출은 계속되어, group management 사용 시 rebalance 를 회피하지만 레코드는 가져오지 않는다 | "When a container is paused, it continues to poll() the consumer, avoiding a rebalance if group management is being used, but it does not retrieve any records." | `official-vendor-doc` | consumer group management(자동 파티션 할당)를 사용하는 컨테이너의 pause 상태 | manual partition assignment(그룹 관리 미사용) 시 동작 여부, `max.poll.interval.ms` 초과 안전성(본문 미언급) |
|
||||
| SPRK-PAUSE-C3 | 2.9 버전부터 `pauseImmediate=true` 로 설정하면 pause 가 현재 레코드 처리 직후 적용되고, 기본값(`false`)은 이전 poll 의 모든 레코드 처리가 끝난 뒤 적용된다 | "Starting with version 2.9, a new container property pauseImmediate, when set to true, causes the pause to take effect after the current record is processed." + "By default, the pause takes effect when all the records from the previous poll have been processed." | `official-vendor-doc` | `pauseImmediate` 옵션의 존재와 기본값 대비 동작 차이 | 어느 설정이 ca-skeleton 백프레셔 반응 속도 요구에 적합한지는 이 문서만으로 결정 불가 — branch 설계 판단 별도 필요 |
|
||||
| SPRK-PAUSE-C4 | `isPauseRequested()` 는 pause 호출 여부만 알려주고, `isConsumerPaused()` 라야 모든 Consumer 인스턴스가 실제로 정지했는지 확인 가능 | "isConsumerPaused() returns true if all Consumer instances have actually paused." | `official-vendor-doc` | pause 완료 여부를 헬스체크/모니터링 로직으로 확인하는 용도 | 요청부터 실제 정지까지 걸리는 시간 상한(본문 미언급) |
|
||||
| SPRK-PAUSE-C5 | 파티션 단위 pause/resume API(`pausePartition`/`resumePartition`)는 **본 URL(container-level pause-resume 페이지)에 등장하지 않는다** — self-grep 결과 0건. 사이트 내비게이션에 "Pausing and Resuming Partitions on Listener Containers" 라는 별도 페이지명만 확인됨(별도 URL, 본 dispatch 범위 밖) | 미발견 — `grep -nF 'pausePartition'` / `grep -nF 'resumePartition'` 모두 0건 (fetched text 전체). 내비게이션 텍스트에 "Pausing and Resuming Partitions on Listener Containers" 링크명만 존재 | `needs-confirmation` | 없음 — 이 claim 은 "본 URL 범위 안에 파티션 단위 API 근거가 없다"는 부재 확인용 | 파티션 단위 API 의 실제 존재·동작(별도 URL "Pausing and Resuming Partitions on Listener Containers" 를 별도 dispatch 로 조사해야 확인 가능) |
|
||||
| SPRK-PAUSE-C6 | 컨테이너 `pause()`/`resume()` API 자체는 2.1.3 버전에 추가되었다 | "Version 2.1.3 added pause() and resume() methods to listener containers." | `official-vendor-doc` | 컨테이너 레벨 pause/resume API 의 도입 시점과 안정성(4.1.0 문서에도 동일 유지) | 파티션 레벨 API 의 도입 시점(별도 페이지, 미확인) |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모든 claim 은 `official-vendor-doc` (Spring 공식 reference — RFC/IETF 표준은 아니므로 `official-standard` 로 격상하지 않음) 또는 미발견 확인용 `needs-confirmation` (C5) 이다.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-PAUSE-C1`: 컨테이너 pause/resume 이 poll() 경계에서 반영되는 타이밍
|
||||
- `SPRK-PAUSE-C2`: paused 상태에서도 poll() 이 계속되어 group management 사용 시 rebalance 를 회피
|
||||
- `SPRK-PAUSE-C3`: `pauseImmediate` 옵션 유무에 따른 반영 시점 차이
|
||||
- `SPRK-PAUSE-C4`: pause 요청 여부와 실제 정지 여부를 구분하는 API 존재
|
||||
- `SPRK-PAUSE-C6`: 컨테이너 pause/resume API 도입 버전
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 파티션 단위 pause/resume(`pausePartition`/`resumePartition`) 의 존재·동작 (`SPRK-PAUSE-C5` — 별도 페이지, 본 URL 범위 밖)
|
||||
- manual partition assignment(그룹 관리 미사용) 환경에서의 rebalance 회피 여부
|
||||
- `max.poll.interval.ms` 초과 위험이나 consumer heartbeat 동작과의 상호작용 (본문 미언급)
|
||||
- manual ack 모드(`MANUAL`/`MANUAL_IMMEDIATE`)와 pause/resume 의 상호작용 (본문 미언급)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton bounded queue 구현에서 `pauseImmediate` 를 켤지 기본값을 쓸지의 trade-off (branch-note 에서 `UNSUPPORTED_IMPL_DECISION` 또는 명시적 사용자 trade-off 로 결정)
|
||||
- 파티션 단위 pause API 가 필요한지 여부는 "Pausing and Resuming Partitions on Listener Containers" 페이지를 별도 `wiki-source-summarizer` dispatch 로 조사한 뒤 확정
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- 이 페이지는 Spring for Apache Kafka reference 4.1.0 버전 기준 렌더링(내비게이션 breadcrumb 확인). 기능 자체(2.1.3/2.1.5/2.9 도입)는 오래 안정화된 기능이며 4.1.0 문서에도 동일 문구로 유지.
|
||||
- 추가로 봐야 할 동일 출처 페이지: 사이트 내비게이션에서 확인된 "Pausing and Resuming Partitions on Listener Containers" (파티션 단위 pause/resume API — `pausePartition`/`resumePartition` 등) — 별도 URL, 별도 dispatch 필요.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/spring-kafka-sending-messages-kafkatemplate]]`
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — Non-Blocking Retry Topic Pattern (Ordering Loss)
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/retrytopic/how-the-pattern-works.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, retry-policy, dead-letter-queue]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring for Apache Kafka — Non-Blocking Retry Topic Pattern (Ordering Loss)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Spring for Apache Kafka reference **4.1.0** (페이지 상단 버전 표기 기준, 2026-07-28 확인).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | ca-skeleton consumer 재시도 전략 선택 — non-blocking retry topic 체인이 파티션 정체를 피하는 대신 Kafka 의 순서 보장을 잃는다는 공식 자인이, per-aggregate(파티션 키) 순서 보장을 유지해야 하는 계약에서 이 대안을 언제 배제/채택할지의 선택 조건 근거 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/retrytopic/how-the-pattern-works.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
|
||||
- 발행일: (reference 페이지 자체에 발행일 미표기 — 버전 4.1.0 기준 문서)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton 의 `adapter:inbound:messaging-kafka` consumer 가 non-blocking retry topic 체인(retry topic + DLT)을 재시도 전략으로 채택할지 결정하려면, 이 패턴이 **Kafka 의 순서 보장을 잃는다**는 공식 자인을 근거로 per-aggregate 순서 보장 요구가 있는 leaf 에서 배제 조건을 판단해야 한다. 이 raw 문서는 그 배제/채택 판단의 1차 근거다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§How the Pattern Works] "If message processing fails, the message is forwarded to a retry topic with a back off timestamp."
|
||||
|
||||
> [§How the Pattern Works] "The retry topic consumer then checks the timestamp and if it’s not due it pauses the consumption for that topic’s partition."
|
||||
|
||||
> [§How the Pattern Works] "If the message processing fails again the message will be forwarded to the next retry topic, and the pattern is repeated until a successful processing occurs, or the attempts are exhausted, and the message is sent to the Dead Letter Topic (if configured)."
|
||||
|
||||
> [§How the Pattern Works] "To illustrate, if you have a "main-topic" topic, and want to set up non-blocking retry with an exponential backoff of 1000ms with a multiplier of 2 and 4 max attempts, it will create the main-topic-retry-1000, main-topic-retry-2000, main-topic-retry-4000 and main-topic-dlt topics and configure the respective consumers."
|
||||
|
||||
> [§How the Pattern Works] "By using this strategy you lose Kafka’s ordering guarantees for that topic."
|
||||
|
||||
> 참고: 원문은 straight apostrophe(`'`)가 아니라 curly apostrophe(U+2019, `’`)를 사용한다 (`it’s`, `topic’s`, `Kafka’s`). 위 인용은 원문 문자 그대로(U+2019) 보존했으며, self-grep 검증도 동일 문자로 수행했다 — 아래 검증 로그 참조.
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-RETRYTOPIC-C1 | non-blocking retry topic 패턴은 처리 실패 메시지를 back-off timestamp 와 함께 별도 재시도 토픽으로 포워딩하고, 재시도 토픽 컨슈머는 그 timestamp 가 도래하기 전까지 **해당 토픽 파티션의 소비 자체를 일시 중지**한다 | [§How the Pattern Works] "If message processing fails, the message is forwarded to a retry topic with a back off timestamp." + "The retry topic consumer then checks the timestamp and if it’s not due it pauses the consumption for that topic’s partition." | `official-vendor-doc` | Spring for Apache Kafka 의 `@RetryableTopic`/`RetryTopicConfiguration` non-blocking retry 기능 (reference 4.1.0) | 이 메커니즘이 raw Kafka client(Spring 미사용) 컨슈머에도 그대로 적용된다는 것 |
|
||||
| SPRK-RETRYTOPIC-C2 | 재시도가 반복 실패하면 다음 재시도 토픽으로 계속 포워딩되다가, 시도 횟수 소진 시(그리고 DLT 가 설정된 경우) Dead Letter Topic 으로 전송된다 | [§How the Pattern Works] "If the message processing fails again the message will be forwarded to the next retry topic, and the pattern is repeated until a successful processing occurs, or the attempts are exhausted, and the message is sent to the Dead Letter Topic (if configured)." | `official-vendor-doc` | 동일 non-blocking retry 기능의 종료 조건 | DLT 전송이 항상 강제된다는 것 — 원문의 "(if configured)"는 DLT 가 선택 설정임을 명시 |
|
||||
| SPRK-RETRYTOPIC-C3 | 이 전략(non-blocking retry topic 체인)을 사용하면 **해당 토픽의 Kafka 순서 보장을 잃는다** — 공식 문서가 직접 자인하는 trade-off | [§How the Pattern Works] "By using this strategy you lose Kafka’s ordering guarantees for that topic." | `official-vendor-doc` | non-blocking retry topic 전략을 채택하는 임의 토픽 — per-aggregate(파티션 키) 순서 보장이 필요한 leaf 에서 이 전략을 배제/채택할지 판단하는 결정적 근거 | blocking retry 가 전반적으로 우월하다는 것은 아님 — 순서 손실의 정량적 영향(예: 얼마나 자주, 어느 범위까지)은 명시하지 않으며, 파티션 키 설계로 순서 손실을 완화할 수 있는지도 이 문장만으로는 증명 못 함 |
|
||||
| SPRK-RETRYTOPIC-C4 | 예시 설정(1000ms 백오프, multiplier 2, 최대 4회 시도)에서 프레임워크는 `<topic>-retry-<backoffMs>` 형태의 재시도 토픽들과 `<topic>-dlt` 토픽을 **자동 생성**하고 리스너까지 구성한다 | [§How the Pattern Works] "it will create the main-topic-retry-1000, main-topic-retry-2000, main-topic-retry-4000 and main-topic-dlt topics and configure the respective consumers." | `official-vendor-doc` | 기본(커스터마이징 없는) 토픽 명명 및 자동 생성 동작의 예시 | 이 예시가 보여주는 것은 특정 백오프 설정(1000ms×2, 4회)에서의 네이밍이며, `-retry`/`-dlt` 단독 접미사가 모든 설정에서 고정된 기본값이라는 일반 규칙은 별도 "Topic Naming" reference 페이지 소관 — 이 URL(1 dispatch = 1 URL 원칙) 로는 확인하지 않음, **미발견** |
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-RETRYTOPIC-C1`, `SPRK-RETRYTOPIC-C2`: non-blocking retry topic 패턴의 동작 메커니즘(timestamp 기반 pause/resume, 시도 소진 시 DLT)
|
||||
- `SPRK-RETRYTOPIC-C3`: 이 전략이 해당 토픽의 Kafka 순서 보장을 깨뜨린다는 공식 자인 — per-aggregate 순서 보장이 필요한 consumer 설계에서 이 대안을 배제할지 판단하는 결정적 근거
|
||||
- `SPRK-RETRYTOPIC-C4`: 특정 백오프 설정 예시에서의 토픽 자동 생성/명명 동작
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- blocking retry(동일 파티션 내 backoff) 전략이 순서를 보존하면서도 처리량/지연 측면에서 더 우월하다는 것 — 이 문서는 trade-off 의 한쪽(순서 손실)만 자인할 뿐 대안 비교를 제공하지 않음
|
||||
- `-retry`/`-dlt` 접미사가 모든 backoff 설정에서 고정된 기본값이라는 일반 규칙 (Topic Naming 별도 페이지 소관, 이 dispatch 범위 밖)
|
||||
- AckMode·`seekAfterError` 관련 문장(원문에 존재하나 본 raw 문서는 ca-skeleton 결정 근거로 요청된 순서 손실/패턴 동작/명명에 집중해 별도 Claim 화하지 않음 — 필요 시 후속 raw 문서로 분리)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton `adapter:inbound:messaging-kafka` 의 실제 파티션 키 설계(예: aggregate id 기반)가 이 순서 손실 trade-off 를 어느 leaf 에서 감수 가능한지/불가능한지 branch-local 결정(D-row)으로 명시 필요
|
||||
- 이 전략을 채택할 경우 `InboxStorePort` 멱등 계약과의 상호작용(재시도 토픽에서 재처리되는 메시지의 멱등 판정) 검증 필요 — sibling branch `feature-idempotency-ownership-protocol-contract` 소관
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 해석 후보(미검증): 순서 손실은 "해당 토픽" 단위로 발생하므로, 만약 재시도 토픽을 원본과 동일 파티션 키로 구성한다면 같은 key 의 메시지끼리는 재시도 토픽 내에서 여전히 순서가 섞일 수 있음 — 이 문서만으로는 파티션 키 유지 여부에 따른 완화 가능성을 확인할 수 없음 (검증 필요)
|
||||
- 추가로 봐야 할 동일 출처 페이지: `retrytopic/topic-naming.html`(기본 접미사 커스터마이징 규칙), `retrytopic/features.html`(추가 옵션), `retrytopic/dlt-strategies.html`(DLT 처리 전략) — 각각 별도 dispatch 로 수집
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc / company-tech-blog: (아직 없음 — Kafka 공식 consumer configs·rebalance 문서는 `feature-kafka-consumer-inbox-contract` 의 "추가 수집 필요" 항목으로 남아 있음)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — Pausing and Resuming Partitions on Listener Containers
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/pause-resume-partitions.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-consumer-inbox-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka, bounded-queue]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / Spring for Apache Kafka — Pausing and Resuming Partitions on Listener Containers
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 별도 작성. 원본은 raw에 영구 보관.
|
||||
> 자매 문서(컨테이너 레벨 pause/resume): [[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]] — 이 문서는 그 문서와 별도 URL("Pausing and Resuming **Partitions** on Listener Containers")을 다루며, 인접 페이지 중복 수집이 아니다.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-consumer-inbox-contract]] | D4("순서 단위는 파티션 — 파티션별 독립 bounded queue")와 D5("포화 시 pause")를 **파티션 단위**로 구현할 수 있는 근거 — 컨테이너 전체가 아니라 특정 `TopicPartition` 만 pause/resume 하는 공식 API(`pausePartition`/`resumePartition`)의 존재·이름·반영 시점을 확인. 아울러 **rebalance 로 파티션이 재배정될 때 pause 상태가 어떻게 되는지는 이 문서도 다루지 않는다는 부재를 확인**(SPRK-PAUSEPART-C5) — 브랜치가 이미 §구현 가이드 3(`onPartitionsAssigned` 행)에서 "재배정된 파티션의 pause 상태를 반드시 초기화(resume)"라고 적어 둔 계약의 근거가 이 문서로도 보강되지 않는다는 뜻 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/pause-resume-partitions.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka reference)
|
||||
- 문서 버전: Spring for Apache Kafka reference **4.1.0** (페이지 breadcrumb "Spring for Apache Kafka 4.1.0" 확인). 본문에 인용된 기능 자체는 버전 **2.7** 도입이며 4.1.0 문서에도 동일 문구로 유지됨. 페이지 자체의 별도 발행일 표기는 없음
|
||||
- 발행일: 명시 없음 (버전 이력만 본문에 표기)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
ca-skeleton kafka consumer inbox 브랜치(`feature-kafka-consumer-inbox-contract`)가 D4("파티션별 독립 bounded queue")와 D5("포화 시 pause") 조합을 실제로 **그 파티션만** pause 하는 형태로 구현하려면 컨테이너 전체 pause 가 아니라 파티션 단위 pause API 가 필요하다. 이 문서가 그 API(`pausePartition`/`resumePartition`)의 존재·시그니처·반영 시점·상태 조회 API 를 공식으로 확인해 준다. 동시에, 브랜치가 이미 세운 "재배정 시 pause 상태 초기화" 계약의 근거를 이 문서에서 찾을 수 있는지 확인하는 것이 이 조사의 두 번째 목적이었다 — 결과는 "부재"(아래 SPRK-PAUSEPART-C5).
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> 원문 그대로. 이 페이지는 단일 섹션("Pausing and Resuming Partitions on Listener Containers")이며 하위 번호 섹션이 없어, 위치는 fetched plain-text 의 line 번호로 표기한다 (self-grep 참조). 전체 본문은 이 5개 문장이 전부다(두 개 `<p>` 블록, 나머지는 페이지 네비게이션).
|
||||
|
||||
> [Pausing and Resuming Partitions on Listener Containers] "Since version 2.7 you can pause and resume the consumption of specific partitions assigned to that consumer by using the pausePartition(TopicPartition topicPartition) and resumePartition(TopicPartition topicPartition) methods in the listener containers." (line 142)
|
||||
|
||||
> [Pausing and Resuming Partitions on Listener Containers] "The pausing and resuming take place respectively before and after the poll() similar to the pause() and resume() methods." (line 143)
|
||||
|
||||
> [Pausing and Resuming Partitions on Listener Containers] "The isPartitionPauseRequested() method returns true if pause for that partition has been requested." (line 144)
|
||||
|
||||
> [Pausing and Resuming Partitions on Listener Containers] "The isPartitionPaused() method returns true if that partition has effectively been paused." (line 145)
|
||||
|
||||
> [Pausing and Resuming Partitions on Listener Containers] "Also since version 2.7 ConsumerPartitionPausedEvent and ConsumerPartitionResumedEvent instances are published with the container as the source property and the TopicPartition instance." (line 146)
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRK-PAUSEPART-C1 | Spring Kafka listener container 는 버전 2.7부터 **파티션 단위** pause/resume API 를 제공한다 — `pausePartition(TopicPartition topicPartition)` / `resumePartition(TopicPartition topicPartition)` | "Since version 2.7 you can pause and resume the consumption of specific partitions assigned to that consumer by using the pausePartition(TopicPartition topicPartition) and resumePartition(TopicPartition topicPartition) methods in the listener containers." | `official-vendor-doc` | 2.7 이상 Spring Kafka listener container 에서 특정 `TopicPartition` 만 대상으로 pause/resume 하는 API 존재 | 이 API 가 파티션 단위 bounded queue(D4) 와 어떻게 배선되어야 하는지(스켈레톤 구현 세부) — 문서는 API 존재만 말함 |
|
||||
| SPRK-PAUSEPART-C2 | 파티션 단위 pause/resume 도 컨테이너 레벨과 동일하게 poll() 경계에서 반영된다 — pause 는 poll() 이전, resume 은 poll() 이후 | "The pausing and resuming take place respectively before and after the poll() similar to the pause() and resume() methods." | `official-vendor-doc` | 파티션 단위 pause/resume 의 타이밍이 컨테이너 레벨(`SPRK-PAUSE-C1`, [[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]])과 동형이라는 것 | `pauseImmediate` 옵션이 파티션 단위 pause 에도 동일 적용되는지는 본문에 명시 없음(컨테이너 레벨 문서에만 있음) |
|
||||
| SPRK-PAUSEPART-C3 | 파티션 pause **요청** 여부(`isPartitionPauseRequested()`)와 **실제 정지** 여부(`isPartitionPaused()`)는 별개 API 로 구분된다 | "The isPartitionPauseRequested() method returns true if pause for that partition has been requested." + "The isPartitionPaused() method returns true if that partition has effectively been paused." | `official-vendor-doc` | 파티션 단위 pause 요청과 실제 정지 완료를 구분해 확인하는 용도 — 컨테이너 레벨의 `isPauseRequested()`/`isConsumerPaused()` 쌍(`SPRK-PAUSE-C4`)과 동형 패턴 | 요청부터 실제 정지까지 걸리는 시간 상한(본문 미언급) |
|
||||
| SPRK-PAUSEPART-C4 | 파티션 단위 pause/resume 발생 시 `ConsumerPartitionPausedEvent`/`ConsumerPartitionResumedEvent` 가 컨테이너를 `source`, 해당 `TopicPartition` 을 이벤트 데이터로 발행한다 (버전 2.7~) | "Also since version 2.7 ConsumerPartitionPausedEvent and ConsumerPartitionResumedEvent instances are published with the container as the source property and the TopicPartition instance." | `official-vendor-doc` | 파티션 pause/resume 을 애플리케이션 이벤트 리스너로 관측하는 용도(모니터링·로깅) | 이벤트 발행이 동기인지 비동기인지, 리스너 예외 발생 시 pause 자체가 롤백되는지(본문 미언급) |
|
||||
| SPRK-PAUSEPART-C5 | **rebalance·파티션 재배정 시 파티션 단위 pause 상태가 어떻게 되는지는 본 문서에 언급이 없다** — self-grep 결과 "rebalance"/"reassign"/"revoke"/"onPartitionsAssigned"/"onPartitionsRevoked" 어휘가 본문(2개 단락)에 전혀 등장하지 않는다 | 미발견 — `grep -ni "rebalan\|reassign\|revoke\|assign"` 를 fetched plain-text 전체(215줄)에 실행한 결과, 실제 본문(142~146줄) 범위에는 0건. 페이지 좌측 네비게이션의 "Rebalancing Listeners"/"Enforcing Consumer Rebalance"(84~85줄)는 **다른 페이지로의 링크 텍스트**일 뿐 본 페이지 본문이 아님 | `needs-confirmation` | 이 claim 은 "본 URL 범위 안에 rebalance-시 파티션 pause 상태 근거가 없다"는 부재 확인용이며, 그 자체로는 아무것도 증명하지 않음 | 파티션이 재배정될 때 이전 pause 상태가 유지되는지 초기화되는지의 실제 동작(별도 소스 필요 — Spring Kafka 소스 코드, `ConsumerRebalanceListener` 관련 공식 페이지, 또는 실제 통합 테스트로 확인) |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모든 claim 은 `official-vendor-doc` (Spring 공식 reference) 또는 미발견 확인용 `needs-confirmation`(C5) 이다. company-tech-blog 근거는 이 문서에 없다.
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRK-PAUSEPART-C1`: 파티션 단위 pause/resume API(`pausePartition`/`resumePartition`)가 2.7부터 listener container 에 존재
|
||||
- `SPRK-PAUSEPART-C2`: 파티션 단위 pause/resume 도 poll() 경계에서 반영
|
||||
- `SPRK-PAUSEPART-C3`: pause 요청과 실제 정지를 구분하는 API(`isPartitionPauseRequested()`/`isPartitionPaused()`) 존재
|
||||
- `SPRK-PAUSEPART-C4`: 파티션 pause/resume 이벤트(`ConsumerPartitionPausedEvent`/`ConsumerPartitionResumedEvent`) 발행
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **rebalance·파티션 재배정 시 파티션 단위 pause 상태의 운명(유지되는가 초기화되는가)** — `SPRK-PAUSEPART-C5`. 이 문서(파티션 레벨)와 자매 문서(컨테이너 레벨, `[[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]]`)를 모두 확인했으나 **양쪽 다 이 주제를 다루지 않는다**
|
||||
- manual partition assignment(그룹 관리 미사용) 환경에서 파티션 단위 pause API 의 동작 여부(본문 미언급)
|
||||
- `pauseImmediate` 옵션이 파티션 단위 pause 에도 적용되는지(본문에 없음 — 컨테이너 레벨 문서에만 존재)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 의 `feature-kafka-consumer-inbox-contract` §구현 가이드 3 의 `onPartitionsAssigned` 행이 "새로 배정된 파티션의 pause 상태를 반드시 초기화(resume)"를 `KIP429-C3` + `SPRK-PAUSE-C1` 근거로 명시하고 있는데, **이 두 claim 모두 그 구체적 동작(재배정 시 pause 상태 리셋)을 직접 말하지 않는다** (`KIP429-C3` 은 COOPERATIVE 가 소유 파티션을 유지한다는 것만, `SPRK-PAUSE-C1` 은 컨테이너 레벨 poll 경계 타이밍만 말함). 본 문서로도 이 gap 이 메워지지 않았으므로, 그 branch-note 행은 **`UNSUPPORTED_IMPL_DECISION` 재라벨 후보**다 — 재라벨 여부와 trade-off 명시는 branch 소유자(=`/branch-spec` 재실행 또는 사용자)가 판단할 사항이며, 본 raw 문서는 근거 부재 사실만 보고한다.
|
||||
- 실제 동작 확인이 필요하면 Spring Kafka 소스 코드(`KafkaMessageListenerContainer` 의 `pausedPartitions` 필드가 `onPartitionsRevoked`/`onPartitionsAssigned` 콜백에서 어떻게 갱신되는지) 또는 별도 통합 테스트로 검증해야 함 — 이는 이 raw 문서의 범위 밖(공식 reference 문서에는 없음).
|
||||
|
||||
## 메모
|
||||
|
||||
> 나중에 wiki로 옮길 때 참고할 짧은 메모. 검증되지 않은 내 추론은 여기에 두지 말 것.
|
||||
|
||||
- 이 페이지 본문은 단 두 개 `<p>` 블록, 5개 문장이 전부다(원본 HTML 확인 — 557~565번째 줄 범위, admonition/note 박스 없음). Spring 문서치고 이례적으로 짧은 페이지.
|
||||
- 컨테이너 레벨 자매 문서([[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]])와 함께, "Spring Kafka 공식 문서가 rebalance 시 pause 상태 처리를 명시하지 않는다"는 것이 **두 페이지 모두**에서 확인됨 — 우연이 아니라 이 문서군이 다루는 범위 밖일 가능성이 높음(다른 페이지, 예: "Rebalancing Listeners"/`ConsumerAwareRebalanceListener` 관련 페이지를 살펴봐야 할 수 있음 — 별도 dispatch 대상, 이번 범위 아님).
|
||||
- 추가로 봐야 할 동일 출처 페이지: "Rebalancing Listeners" (사이트 네비게이션에서 확인, line 84) — `ConsumerRebalanceListener`/`ConsumerAwareRebalanceListener` 관련 공식 API 가 partition pause 상태와 상호작용하는지 확인하려면 이 페이지를 별도 dispatch 로 조사해야 함.
|
||||
|
||||
## Related
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/spring-kafka-listener-container-pause-resume-backpressure]] (컨테이너 레벨 pause/resume — 자매 문서), [[raw/official-docs/kafka-incremental-cooperative-rebalance-kip429]] (KIP-429 cooperative rebalance)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: official-doc / Spring for Apache Kafka — KafkaTemplate Send Result (SendResult, get(), flush())
|
||||
source_type: official-doc
|
||||
url: https://docs.spring.io/spring-kafka/reference/kafka/sending-messages.html
|
||||
archive_url:
|
||||
related_branches: [feature-kafka-producer-runtime-contract]
|
||||
related_projects: [ca-skeleton]
|
||||
tags: [official-doc, ca-skeleton, messaging, kafka]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# Spring for Apache Kafka — KafkaTemplate Send Result (SendResult, get(), flush())
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서)의 **원문 발췌·출처 기록**.
|
||||
> 문서 버전: Spring for Apache Kafka **4.1.0** reference — "Sending Messages" §"Using KafkaTemplate".
|
||||
|
||||
## source_type
|
||||
|
||||
`official-doc` — Spring for Apache Kafka 공식 reference 문서 (docs.spring.io).
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-kafka-producer-runtime-contract]] | ca-skeleton 의 messaging 포트가 `void send` 대신 acknowledgement-aware 한 전송 결과(broker 가 확인한 topic/partition/offset)를 반환해야 하는지, 그 결과 타입을 Spring/Kafka SDK 타입(`SendResult`)으로 노출할지 프로젝트 자체 값 타입으로 감쌀지 판단할 **메커니즘 근거**(반환 타입, 구성 property, 동기 대기 방법, flush 시맨틱, 실패 전달 방식) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://docs.spring.io/spring-kafka/reference/kafka/sending-messages.html
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: Spring team (Broadcom / VMware Tanzu — Spring for Apache Kafka 프로젝트)
|
||||
- 발행일: 명시 없음 (버전 4.1.0 reference, "Stable" 채널)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-kafka-producer-runtime-contract` 브랜치가 messaging 포트의 `send()` 반환 타입을 정할 때 필요한 **메커니즘 사실**(반환 타입이 무엇인지, 무엇을 담는지, 어떻게 동기 대기하는지, flush/실패가 어떻게 전달되는지)을 공식 문서에서 확보하기 위함. 단, "포트가 SDK 타입을 그대로 반환해도 되는가"라는 **아키텍처 판단 자체는 이 문서가 증명하지 않는다** — 이는 ca-skeleton 의 framework-neutral 포트 원칙(별도 canonical 근거)과 대조해서 branch 가 별도로 결정해야 한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Using KafkaTemplate — Overview, 코드 시그니처] "CompletableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record);"
|
||||
|
||||
> [§Using KafkaTemplate, 서술문] "Notice that the send methods return a CompletableFuture<SendResult>."
|
||||
|
||||
> [§Using KafkaTemplate] "SendResult has two properties, a ProducerRecord and RecordMetadata."
|
||||
|
||||
> [§Using KafkaTemplate] "The Throwable can be cast to a KafkaProducerException; its producerRecord property contains the failed record."
|
||||
|
||||
> [§Using KafkaTemplate] "If you wish to block the sending thread to await the result, you can invoke the future’s get() method; using the method with a timeout is recommended."
|
||||
|
||||
> [§Using KafkaTemplate] "If you have set a linger.ms, you may wish to invoke flush() before waiting or, for convenience, the template has a constructor with an autoFlush parameter that causes the template to flush() on each send."
|
||||
|
||||
> [§Using KafkaTemplate] "Flushing is only needed if you have set the linger.ms producer property and want to immediately send a partial batch."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| SPRING-KAFKA-SEND-C1 | `KafkaTemplate` 의 `send`/`sendDefault` 전 오버로드는 `CompletableFuture<SendResult<K, V>>` 를 반환한다 (`void` 반환이 아니다) | "CompletableFuture<SendResult<K, V>> send(ProducerRecord<K, V> record);" / "Notice that the send methods return a CompletableFuture<SendResult>." | `official-vendor-doc` | Spring for Apache Kafka 4.1.0 `KafkaTemplate.send()`/`sendDefault()` 전 오버로드 | future 가 **정확히 언제** 완료되는지(broker acks 시점, in-flight retry 종료 시점 등)는 본문이 명시적으로 진술하지 않는다 — "broker 가 확인한 결과"라는 timing 주장은 이 인용만으로 증명되지 않으며, Kafka producer 자체의 `acks` 설정 공식 문서로 별도 보강 필요 |
|
||||
| SPRING-KAFKA-SEND-C2 | `SendResult<K,V>` 는 `ProducerRecord` 와 `RecordMetadata` 두 property 로 구성된다 | "SendResult has two properties, a ProducerRecord and RecordMetadata." | `official-vendor-doc` | `SendResult<K,V>` 타입의 구조 | 이 문장 자체는 `RecordMetadata` 가 partition/offset 필드를 갖는다고 명시하지 않는다(본문은 "See the Kafka API documentation for information about those objects" 로 위임). 또한 "포트가 이 SDK 타입을 그대로 반환해도 된다"는 아키텍처 판단은 증명하지 않는다 |
|
||||
| SPRING-KAFKA-SEND-C3 | send 결과를 동기적으로 기다리려면 반환된 future 의 `get()` 을 호출하며, timeout 있는 오버로드 사용이 권장된다 | "If you wish to block the sending thread to await the result, you can invoke the future’s get() method; using the method with a timeout is recommended." | `official-vendor-doc` | `CompletableFuture<SendResult<K,V>>` 를 동기적으로 대기하는 호출 패턴 일반 | 이 문장은 조건부("if you wish to block")이며, blocking 이 기본 권장 패턴이라는 뜻은 아니다 — 같은 섹션은 `whenComplete` 비동기 콜백을 먼저 예시로 제시한다. 포트가 반드시 blocking 방식을 채택해야 한다는 근거는 아니다 |
|
||||
| SPRING-KAFKA-SEND-C4 | `linger.ms` 를 설정한 경우 대기 전 `flush()` 를 명시 호출하거나 `autoFlush` 생성자 파라미터로 매 send 마다 자동 flush 할 수 있으며, flush 는 partial batch 를 즉시 보내고 싶을 때만 필요하다 | "If you have set a linger.ms, you may wish to invoke flush() before waiting or, for convenience, the template has a constructor with an autoFlush parameter that causes the template to flush() on each send." / "Flushing is only needed if you have set the linger.ms producer property and want to immediately send a partial batch." | `official-vendor-doc` | `KafkaTemplate` 의 `flush()` 메서드와 `autoFlush` 생성자 파라미터 | flush 호출이 다른 스레드(동일 producer 공유)에 지연을 유발할 수 있다는 성능 경고는 다른 섹션("Using DefaultKafkaProducerFactory")의 별개 진술이며 이 claim 범위 밖이다. graceful shutdown 시 flush 순서를 어떻게 강제할지는 이 문서가 규정하지 않는다 |
|
||||
| SPRING-KAFKA-SEND-C5 | send future 가 예외로 완료되면 그 `Throwable` 은 `KafkaProducerException` 으로 캐스팅 가능하고, 그 `producerRecord` property 가 실패한 레코드를 담는다 | "The Throwable can be cast to a KafkaProducerException; its producerRecord property contains the failed record." | `official-vendor-doc` | `whenComplete` 콜백의 `ex` 인자, 또는 동기 `get()` 호출 시 `ExecutionException.getCause()` 로 관찰되는 실패 케이스 | 어떤 하부 원인(네트워크 오류, 브로커 거부, 직렬화 실패 등)들이 `KafkaProducerException` 으로 래핑되는지에 대한 전체 목록은 제공하지 않는다 |
|
||||
|
||||
### Strength 참고
|
||||
|
||||
모두 `official-vendor-doc` — Spring for Apache Kafka 는 Spring 프로젝트 공식 reference 문서이며 RFC/표준(`official-standard`)은 아니다.
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `SPRING-KAFKA-SEND-C1`: `send()`/`sendDefault()` 의 반환 타입이 `CompletableFuture<SendResult<K, V>>` 라는 것(즉 fire-and-forget `void` 가 아니라는 것)
|
||||
- `SPRING-KAFKA-SEND-C2`: `SendResult` 가 `ProducerRecord` + `RecordMetadata` 를 담는다는 구조적 사실
|
||||
- `SPRING-KAFKA-SEND-C3`: 동기 대기 방법이 `future.get()`(timeout 권장)이라는 것
|
||||
- `SPRING-KAFKA-SEND-C4`: `flush()`/`autoFlush` 의 존재와 사용 조건(`linger.ms` 설정 시 partial batch 즉시 전송)
|
||||
- `SPRING-KAFKA-SEND-C5`: 실패 시 예외가 `KafkaProducerException` + `producerRecord` 로 전달된다는 것
|
||||
- 이 자료가 증명하지 **않는** 것:
|
||||
- **"messaging 포트가 SDK 타입(`SendResult`, `RecordMetadata`)을 그대로 반환해도 되는가" 라는 아키텍처 판단.** 이 문서는 Spring Kafka 라이브러리 사용법을 설명할 뿐, ca-skeleton 의 framework-neutral 포트 원칙과의 정합성은 다루지 않는다. `SendResult` 를 그대로 포트 반환 타입으로 노출할지, 프로젝트 자체 값 타입(예: `topic`/`partition`/`offset` 만 담는 record)으로 감쌀지는 branch 가 별도 canonical 근거(포트 설계 원칙 문서)로 결정해야 한다.
|
||||
- future 가 "broker ack 시점"에 정확히 완료된다는 timing 보장 — 본문은 이를 명시적으로 진술하지 않는다(§핵심 인용/C1 참조). 이 timing 주장은 Kafka producer 의 `acks` 설정 공식 문서로 별도 검증 필요.
|
||||
- `RecordMetadata` 가 정확히 어떤 필드(partition, offset, timestamp 등)를 갖는지 — 본문은 "Kafka API documentation" 을 참조하라고 위임할 뿐 필드를 나열하지 않는다.
|
||||
- blocking(`get()`)이 권장되는 기본 패턴이라는 것 — 문서는 조건부("if you wish to block")로만 서술하며 비동기 콜백을 먼저 제시한다.
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- ca-skeleton 의 messaging 포트 인터페이스 원칙(SDK 타입 노출 금지 여부)을 규정하는 canonical 문서와 대조
|
||||
- `RecordMetadata` 의 실제 필드 목록 — Kafka client 공식 Javadoc/문서로 별도 인용 필요
|
||||
- `acks=all` 등 delivery semantics 설정이 이 future 의 완료 시점에 미치는 영향 — Kafka producer configs 공식 문서로 별도 인용 필요
|
||||
|
||||
## 메모
|
||||
|
||||
- 이 문서는 "무엇을 반환하는가/무엇을 담는가/어떻게 대기하는가"라는 **메커니즘** 근거이며, "무엇을 반환해야 하는가"라는 **설계 결정**의 근거가 아니다. branch-note 의 D-row 에서 이 구분을 유지할 것.
|
||||
- 추가로 봐야 할 동일 출처 페이지: Kafka producer configs 공식 문서(`acks`, `enable.idempotence`, `delivery.timeout.ms`) — 같은 branch 의 다른 결정(acks=all + idempotent producer 설정)의 근거로 별도 raw 문서 필요.
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: (아직 없음 — Kafka producer configs 공식 문서 추가 시 링크)
|
||||
- 이 자료를 인용한 wiki 요약: (생성 시 링크)
|
||||
@@ -0,0 +1,91 @@
|
||||
---
|
||||
title: TanStack Query — persistQueryClient / dehydrate·hydrate
|
||||
source_type: official-doc
|
||||
url: https://tanstack.com/query/latest/docs/framework/react/plugins/persistQueryClient
|
||||
archive_url:
|
||||
related_branches: [feature-frontend-cache-tier-cross-tab-invalidation-contract]
|
||||
related_projects: [ca-skeleton-frontend]
|
||||
tags: [frontend, server-state, cache, persistence, tanstack-query]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# TanStack Query — persistQueryClient / dehydrate·hydrate
|
||||
|
||||
> Layer: `raw/` — 외부 자료의 **원문 발췌·출처 기록**.
|
||||
|
||||
## 활용 branch (필수, 최소 1개+)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| `[[raw/branch-notes/feature-frontend-cache-tier-cross-tab-invalidation-contract]]` | version 불일치 시 **부분 복원 없이 전량 폐기**(`D1`), 복원과 렌더의 순서(`D6`), 영속 대상 선별과 직렬화 책임(`D7`) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://tanstack.com/query/latest/docs/framework/react/plugins/persistQueryClient
|
||||
- 보조 URL: https://tanstack.com/query/latest/docs/framework/react/reference/hydration
|
||||
- 아카이브 URL:
|
||||
- 저자 / 조직: TanStack (TanStack Query 공식 문서)
|
||||
- 발행일: (문서 지속 갱신, v5 기준)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
이 repo 의 기존 TanStack 발췌([[raw/official-docs/tanstack-query-server-state-official]])는 server state 원칙만 담고 있어 **캐시 영속**을 다루지 않는다. `CachePersistencePort` 를 설계하면서 세 가지를 확인해야 했다. (a) 라이브러리가 버전 불일치 캐시를 부분 복원하는지 폐기하는지, (b) 복원이 렌더와 경합하는지, (c) 어떤 query 가 기본적으로 영속 대상인지. 세 가지 모두 공식 문서가 직접 답한다.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§persistQueryClient — buster] "If the cache that is found does not also have that buster string, it will be discarded."
|
||||
|
||||
> [§persistQueryClient — maxAge] "The max-allowed age of the cache in milliseconds. If a persisted cache is found that is older than this time, it will be **silently** discarded (defaults to 24 hours)"
|
||||
|
||||
> [§persistQueryClient — How it works] "If data is found to be any of the following: 1. expired (see `maxAge`) 2. busted (see `buster`)... the persister `removeClient()` is called and the cache is immediately discarded."
|
||||
|
||||
> [§persistQueryClient] "`createSyncStoragePersister` and `createAsyncStoragePersister` throttle this action to happen at most every 1 second to save on potentially expensive writes."
|
||||
|
||||
> [§persistQueryClient — PersistQueryClientProvider] "However, restoring is asynchronous, because all persisters are async by nature, which means that if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time."
|
||||
|
||||
> [§hydration — dehydrate] "It only includes currently successful queries by default."
|
||||
|
||||
> [§hydration — dehydrate Options] "`shouldDehydrateQuery` — Whether to dehydrate queries." / "Defaults to only including successful queries"
|
||||
|
||||
> [§hydration — dehydrate Returns] "This result is not in serialized form, you need to do that yourself if desired"
|
||||
|
||||
> [§hydration — hydrate Limitations] "If the queries you're trying to hydrate already exist in the queryCache, `hydrate` will only overwrite them if the data is newer than the data present in the cache."
|
||||
|
||||
## 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | 저장된 캐시의 buster 문자열이 일치하지 않으면 그 캐시는 **폐기**된다 | [§buster] "If the cache that is found does not also have that buster string, it will be discarded." | `official-reference` | `persistQueryClient` 복원 경로 | buster 를 **무엇으로 구성**해야 하는지 — 문자열 내용은 소비자가 정한다 |
|
||||
| C2 | buster 불일치·만료 시 `removeClient()` 가 호출되고 캐시는 즉시 폐기된다 — 부분 복원 경로가 없다 | [§How it works] "the persister `removeClient()` is called and the cache is immediately discarded" | `official-reference` | 복원 실패 처리 | 폐기가 사용자에게 고지되어야 한다는 뜻은 아님 |
|
||||
| C3 | `maxAge` 기본값은 24시간이며 초과분은 **조용히** 폐기된다 | [§maxAge] "it will be **silently** discarded (defaults to 24 hours)" | `official-reference` | 라이브러리 기본값 | 24시간이 우리 계약의 TTL 이라는 뜻은 아님 — 우리 값은 `FE-REG-STORAGE` 가 정한다 |
|
||||
| C4 | 번들 제공 persister 는 write 를 **최대 1초에 1회**로 throttle 한다 | [§persistQueryClient] "throttle this action to happen at most every 1 second to save on potentially expensive writes" | `official-reference` | `createSyncStoragePersister`·`createAsyncStoragePersister` | 우리 custom adapter 의 적정 간격이 1초라는 뜻은 아님 — 번들 구현의 선택값이다 |
|
||||
| C5 | 복원은 비동기이며, 복원 중 렌더하면 query mount·fetch 와 **경합**이 발생할 수 있다 | [§PersistQueryClientProvider] "if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time" | `official-reference` | 모든 async persister | 경합이 항상 눈에 보이는 오류로 나타난다는 뜻은 아님 |
|
||||
| C6 | `dehydrate` 는 기본적으로 **성공한 query 만** 포함한다 | [§dehydrate] "It only includes currently successful queries by default." | `official-reference` | 영속 대상 선별 | error/pending query 를 영속하면 안 된다는 금지 규칙까지는 아님 — 기본값일 뿐 |
|
||||
| C7 | `dehydrate` 결과는 직렬화된 형태가 아니며 직렬화는 소비자 책임이다 | [§dehydrate Returns] "This result is not in serialized form, you need to do that yourself if desired" | `official-reference` | 영속 adapter | 어떤 직렬화 형식을 써야 하는지 |
|
||||
| C8 | `hydrate` 는 이미 캐시에 있는 query 를 **더 새로운 데이터일 때만** 덮어쓴다 | [§hydrate Limitations] "`hydrate` will only overwrite them if the data is newer than the data present in the cache" | `official-reference` | 복원 시 병합 | "더 새롭다"의 판정 기준이 무엇인지 |
|
||||
|
||||
## 적용 경계
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `C1`·`C2`: 버전 불일치 캐시의 처리는 **부분 복원이 아니라 전량 폐기**가 라이브러리의 기존 동작이다
|
||||
- `C5`: 복원과 렌더를 겹치면 경합이 생기므로 gating 이 필요하다
|
||||
- `C6`~`C8`: 영속 대상 기본 선별, 직렬화 책임 위치, 복원 시 병합 규칙
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- 파티션 키를 `releaseId`·`configSchemaVersion`·`apiContractVersion` **세 값으로** 구성해야 한다는 것 — `C1` 은 buster 가 문자열이라는 것만 말한다. 구성은 project decision 이다
|
||||
- `CachePersistencePort` 를 `QueryCachePort` 와 분리해야 한다는 것 — 문서는 단일 `queryClient` 를 전제한다
|
||||
- 우리 adapter 의 throttle 값 (`C4` 는 번들 persister 의 선택값)
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `C3` 의 24시간 기본값과 `FE-REG-STORAGE` 의 `QUERY_CACHE_SNAPSHOT` TTL 이 충돌하지 않는지
|
||||
- `C8` 의 "newer" 판정이 우리 파티션 폐기 규칙보다 먼저 적용되는지 (폐기가 우선이면 무관)
|
||||
|
||||
## 메모
|
||||
|
||||
- 인용 1 해석 후보 (미검증): `C1`+`C2` 는 branch 의 `D1`("불일치 시 복원하지 않고 폐기, 부분 복원 금지")이 **자체 발명이 아니라 채택한 라이브러리의 기존 동작과 같은 방향**임을 보여준다. 다만 세 값 tuple 구성은 여전히 project-local 이다.
|
||||
- 인용 2 해석 후보 (미검증): `C5` 때문에 `CachePersistencePort.restore()` 는 bootstrap 단계에서 await 되어야 하고, 복원 실패는 부팅을 막지 않고 메모리 캐시로 진행해야 한다(§8.2 `CACHE_PERSISTENCE_FAILURE` 와 정합).
|
||||
- 추가로 봐야 할 동일 출처 페이지: `createAsyncStoragePersister`, `broadcastQueryClient` experimental plugin
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: `[[raw/official-docs/tanstack-query-server-state-official]]`, `[[raw/official-docs/mdn-broadcastchannel-storage-event]]`
|
||||
- 이 자료를 인용한 wiki 요약: 생성 전
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
title: official-doc / MongoDB — Multi-Document Transactions (Replica Set / Sharded Cluster Requirements, Concern Defaults)
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/core/transactions/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, transaction]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Multi-Document Transactions (Replica Set / Sharded Cluster Requirements, Concern Defaults)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — MongoDB 공식 매뉴얼(레퍼런스) 문서.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | (1) multi-document transaction 은 replica-set/sharded 요건 검증을 통과할 때만 활성화한다 — FCV 최소버전(Replica Set 4.0 / Sharded Cluster 4.2), WiredTiger storage engine 요건, `writeConcernMajorityJournalDefault: false` 샤드 배제 근거. (2) transaction 은 시간·크기 한계를 가지므로 무제한 사용을 전제하지 않는다 — "runtime limit" 이 공식적으로 별도 usage consideration 으로 취급된다는 근거(단, 구체 수치는 이 URL 범위 밖, 아래 Usage Boundaries 참고) |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/core/transactions/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: MongoDB, Inc. (공식 매뉴얼)
|
||||
- 발행일: (문서 자체에 명시 없음 — 버전별 롤링 업데이트되는 레퍼런스 페이지. 2026-07-28 fetch 시점 기준 "Starting in MongoDB 8.1" 문구 포함 확인)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch가 "multi-document transaction 은 replica-set/sharded 요건 검증을 통과할 때만 활성화하고 standalone 에서는 사용하지 않는다"와 "transaction 은 시간·크기 한계를 가지므로 무제한 사용을 전제하지 않는다"를 결정하는 데 필요한 FCV/storage-engine 배포 요건, read/write concern 기본값 결정 체인, runtime limit 서술의 공식 근거로 보관.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Transactions and Atomicity — intro] "In MongoDB, an operation on a single document is atomic." [...] "multi-document transactions are not necessary for many practical use cases."
|
||||
|
||||
> [§Feature Compatibility Version (FCV)] "To use transactions, the featureCompatibilityVersion for all members of the deployment must be at least:" — 표: Replica Set → `4.0`, Sharded Cluster → `4.2`
|
||||
|
||||
> [§Storage Engines] "Transactions are supported on replica sets and sharded clusters where:" [...] "the secondary members use either the WiredTiger storage engine or the in-memory storage engines."
|
||||
|
||||
> [§Shard Configuration Restriction] "You cannot run transactions on a sharded cluster that has a shard with writeConcernMajorityJournalDefault set to false, such as a shard with a voting member that uses the in-memory storage engine."
|
||||
|
||||
> [§Transactions and Read Concern — MongoDB Defaults] "By default, the client-level read concern is "local" for reads on the primary."
|
||||
|
||||
> [§Transactions and Write Concern — MongoDB Defaults] "If the transaction-level write concern and the session-level write concern are unset, the transaction-level write concern defaults to the client-level write concern of:" — `w: "majority"` (MongoDB 5.0+) 또는 `w: 1` (이전 버전)
|
||||
|
||||
> [§Transactions and Write Concern — Tip] "Don't explicitly set the write concern for the individual write operations inside a transaction. Setting write concerns for the individual write operations inside a transaction returns an error."
|
||||
|
||||
> [§Transactions and Sessions] "If a session ends and it has an open transaction, the transaction aborts."
|
||||
|
||||
> [§Transactions and Atomicity — Important] "For additional transactions usage considerations (such as runtime limit and oplog size limit), see also Production Considerations."
|
||||
|
||||
> [§Transactions and Write Concern — Note] "Regardless of the write concern specified for the transaction, the driver applies w: "majority" as the write concern when retrying commitTransaction."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
> 이 자료가 **직접 말하는 것만** claim 으로 분리한다. 내 프로젝트에 적용한 결론은 여기 쓰지 않는다.
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-TXN-C1 | 단일 document 에 대한 연산은 원자적이며, 이 때문에 많은 실무 사용 사례에서 multi-document transaction 이 필수가 아니다 | [§Transactions and Atomicity] "In MongoDB, an operation on a single document is atomic." [...] "multi-document transactions are not necessary for many practical use cases." | `official-vendor-doc` | 임베디드 document/array 로 관계를 표현할 수 있는 일반적인 스키마 설계 | 특정 도메인(예: 여러 collection 에 걸친 정산·재고 이동)이 transaction 없이 처리 가능한지는 이 문장이 증명하지 않음 — 스키마별 개별 판단 필요 |
|
||||
| MONGO-TXN-C2 | Transaction 을 사용하려면 배포의 모든 멤버가 최소 FCV 를 만족해야 한다 — Replica Set 은 `4.0` 이상, Sharded Cluster 는 `4.2` 이상 | [§Feature Compatibility Version (FCV)] "To use transactions, the featureCompatibilityVersion for all members of the deployment must be at least:" (표: Replica Set `4.0` / Sharded Cluster `4.2`) | `official-vendor-doc` | replica-set 또는 sharded-cluster 로 배포된 MongoDB 전반 | standalone(단일 `mongod`, replica set 아님) 배포에서 transaction 시도 시 정확히 어떤 에러/거부 메시지가 발생하는지는 이 문장 자체가 규정하지 않음 — FCV 표가 애초에 standalone 을 대상에서 제외하고 있을 뿐 |
|
||||
| MONGO-TXN-C3 | Transaction 은 replica set 과 sharded cluster 에서 지원되며, primary 는 WiredTiger storage engine 을 사용해야 하고 secondary 는 WiredTiger 또는 in-memory storage engine 을 사용해야 한다 | [§Storage Engines] "Transactions are supported on replica sets and sharded clusters where:" [...] "the secondary members use either the WiredTiger storage engine or the in-memory storage engines." | `official-vendor-doc` | replica-set/sharded-cluster 배포의 storage engine 조합 검증 | standalone 인스턴스에 대한 명시적 언급은 없음 — "replica sets and sharded clusters where" 로 지원 범위를 positively 한정할 뿐, standalone 을 명시적으로 거부한다고 말하지는 않음 |
|
||||
| MONGO-TXN-C4 | `writeConcernMajorityJournalDefault` 가 `false` 로 설정된 shard(예: in-memory storage engine 을 쓰는 voting member 를 가진 shard)가 있는 sharded cluster 에서는 transaction 을 실행할 수 없다 | [§Shard Configuration Restriction] "You cannot run transactions on a sharded cluster that has a shard with writeConcernMajorityJournalDefault set to false, such as a shard with a voting member that uses the in-memory storage engine." | `official-vendor-doc` | sharded cluster 배포에서 shard 별 `writeConcernMajorityJournalDefault` 설정 검증 | replica set(비-sharded) 배포에 동일 제약이 적용되는지는 이 문장이 명시하지 않음 — 문장 자체가 sharded cluster 로 범위를 한정함 |
|
||||
| MONGO-TXN-C5 | Transaction 의 read concern 은 transaction-level → session-level → client-level 순으로 기본값이 결정되며, client-level read concern 의 기본값은 primary 읽기 기준 `"local"` 이다 | [§Transactions and Read Concern] "By default, the client-level read concern is "local" for reads on the primary." | `official-vendor-doc` | read concern 을 명시하지 않고 transaction 을 시작하는 모든 경우 | `"majority"`/`"snapshot"` 을 명시적으로 설정했을 때의 동작까지는 이 한 문장이 다루지 않음(별도 절에서 각각 정의됨, 이번 raw 에는 claim 화하지 않음) |
|
||||
| MONGO-TXN-C6 | Transaction 의 write concern 은 transaction-level → session-level → client-level 순으로 기본값이 결정되며, client-level write concern 기본값은 MongoDB 5.0 이상에서 `w: "majority"`, 그 이전 버전에서는 `w: 1` 이다 | [§Transactions and Write Concern] "If the transaction-level write concern and the session-level write concern are unset, the transaction-level write concern defaults to the client-level write concern of:" (`w: "majority"` MongoDB 5.0+ / `w: 1` 이전) | `official-vendor-doc` | write concern 을 명시하지 않고 transaction 을 커밋하는 모든 경우(버전에 따라 분기) | arbiter 를 포함한 배포에서 이 기본값이 달라지는 정확한 조건은 이 문장 자체에 없음 — 링크된 "Implicit Default Write Concern" 별도 페이지 확인 필요(`write-concern-mongodb-official.md` C8 의 동일 gap 참고) |
|
||||
| MONGO-TXN-C7 | Transaction 내부의 개별 write 연산에는 명시적 write concern 을 설정할 수 없으며, 설정 시 에러가 발생한다 — write concern 은 반드시 transaction/session/client 레벨에서만 설정한다 | [§Transactions and Write Concern] "Don't explicitly set the write concern for the individual write operations inside a transaction. Setting write concerns for the individual write operations inside a transaction returns an error." | `official-vendor-doc` | transaction 내부에서 실행되는 모든 write 연산의 write concern 설정 위치 제약 | 이 제약을 어겼을 때 반환되는 정확한 에러 코드/메시지 문자열은 이 문장이 제공하지 않음 |
|
||||
| MONGO-TXN-C8 | Transaction 은 session 에 연결되며, session 이 열린 transaction 을 가진 채 종료되면 그 transaction 은 abort 된다 | [§Transactions and Sessions] "If a session ends and it has an open transaction, the transaction aborts." | `official-vendor-doc` | 세션 종료(연결 끊김, 명시적 session 종료 등)로 인한 transaction abort 조건 | 이것은 **세션 종료로 인한 abort** 이며, 실행 시간(runtime) 초과로 인한 abort(`transactionLifetimeLimitSeconds` 등)와는 별개의 트리거다 — 시간 초과 abort 의 구체 파라미터/기본값/동작은 이 URL 에 없음(C9 참고, Usage Boundaries 에 gap 명시) |
|
||||
| MONGO-TXN-C9 | MongoDB 공식 문서는 transaction 의 "runtime limit" 과 "oplog size limit" 을 별도의 usage consideration 으로 명시적으로 인정하며, 상세는 Production Considerations 페이지로 위임한다 | [§Transactions and Atomicity] "For additional transactions usage considerations (such as runtime limit and oplog size limit), see also Production Considerations." | `needs-confirmation` | transaction 이 "무제한 사용을 전제하지 않는다"는 원칙적 근거(런타임 한계가 공식적으로 존재한다는 사실 자체) | **구체적인 시간 한계 값(예: `transactionLifetimeLimitSeconds` 기본값 60초)과 초과 시 정확한 abort 동작은 이 URL 에 없다** — 별도 페이지(`Production Considerations for Transactions`, https://www.mongodb.com/docs/manual/core/transactions-production-consideration/)의 fetch 가 필요하며, 1 dispatch = 1 URL 원칙에 따라 이번 raw 에는 포함하지 않음 |
|
||||
| MONGO-TXN-C10 | Transaction 커밋에 어떤 write concern 이 지정되었든 관계없이, driver 가 `commitTransaction` 을 재시도할 때는 항상 `w: "majority"` write concern 을 적용한다 | [§Transactions and Write Concern] "Regardless of the write concern specified for the transaction, the driver applies w: "majority" as the write concern when retrying commitTransaction." | `needs-confirmation` | driver 가 `commitTransaction` 명령 자체를 재시도하는 경우의 write concern 승격 규칙 | **일반적인 TransientTransactionError 라벨 감지 → 전체 transaction 재시도 루프 패턴**은 이 문장이 다루지 않는다 — 그 패턴은 드라이버별 에러 처리 가이드(별도 페이지)의 범위이며 이 URL 에는 없음 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, MongoDB 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGO-TXN-C1`: 단일 document 연산은 이미 원자적이라 다수 실무 사용 사례에서 transaction 이 필수가 아니라는 공식 서술
|
||||
- `MONGO-TXN-C2`~`C4`: transaction 활성화의 배포 형태 요건 — FCV 최소버전(Replica Set 4.0 / Sharded Cluster 4.2), storage engine 조합(primary WiredTiger 필수), `writeConcernMajorityJournalDefault: false` shard 배제
|
||||
- `MONGO-TXN-C5`~`C7`: read concern(기본 `"local"`)·write concern(기본 `w:"majority"` 또는 `w:1`, 버전 의존) 기본값 결정 체인과 "개별 write op 에 명시적 write concern 금지" 제약
|
||||
- `MONGO-TXN-C8`: session 종료로 인한 transaction abort — 시간 초과와는 별개의 abort 트리거이지만 "transaction 이 무한정 열려있지 않는다"는 원칙의 부분 근거
|
||||
- `MONGO-TXN-C9`: "runtime limit" 이 공식 문서에서 별도 usage consideration 으로 인정된다는 사실 — 단 구체 수치는 없음
|
||||
- `MONGO-TXN-C10`: `commitTransaction` 재시도 시 write concern 승격 규칙 — 단 일반 transient error 재시도 패턴 전체는 아님
|
||||
- 이 자료가 증명하지 않는 것:
|
||||
- **`transactionLifetimeLimitSeconds` 의 정확한 기본값과, 시간 한계 초과 시의 구체적 abort 동작·에러 코드** — 이 URL 은 "runtime limit" 을 언급만 하고 상세를 `Production Considerations for Transactions` 페이지로 위임한다. 그 페이지는 이번 dispatch 에 포함되지 않음(1 dispatch = 1 URL)
|
||||
- **TransientTransactionError / UnknownTransactionCommitResult 라벨 기반의 일반 재시도 루프 패턴** — 이 URL 은 `commitTransaction` 재시도 시 write concern 규칙 하나만 언급하며, 드라이버별 에러 처리 가이드에 있는 전체 재시도 패턴은 다루지 않음
|
||||
- standalone(비-replica-set) `mongod` 인스턴스에서 transaction 시도 시 정확한 거부 메커니즘(기동 시 거부 vs 런타임 에러) — 이 URL 은 지원 범위를 replica-set/sharded-cluster 로 positively 서술할 뿐, standalone 명시적 거부 문구는 없음
|
||||
- 특정 드라이버(Java Reactive Streams, Spring Data MongoDB 등)가 transaction 을 어떤 API 로 노출하는지 — 이 자료는 서버/개념 문서
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `transactionLifetimeLimitSeconds` 기본값과 초과 시 동작 — 별도 raw 자료(`transactions-production-consideration-mongodb-official` 가칭) dispatch 필요
|
||||
- ca-skeleton 이 실제로 채택할 MongoDB 배포가 replica set 인지 sharded cluster 인지(양쪽 FCV 요건이 다름)
|
||||
- Spring Data MongoDB(또는 채택할 드라이버)가 이 기본값들을 어떤 설정 키로 노출하는지
|
||||
|
||||
## 메모
|
||||
|
||||
- 사용자가 명시적으로 요구한 "transactionLifetimeLimitSeconds 같은 실행 시간 한계와 초과 시 abort 동작" 및 "transient transaction error / retry 권고"는 이 URL(`/docs/manual/core/transactions/`)의 fetch 결과에 상세 수치·패턴으로 존재하지 않았다. 대신 이 페이지가 "runtime limit" 을 별도 considerations 로 인정하고 Production Considerations 페이지로 위임한다는 사실(C9)과, `commitTransaction` 재시도 시 write concern 규칙(C10)만 verbatim 으로 확보했다. Self-Grep 통과분만 claim 화했고, 구체 수치는 fabrication 방지를 위해 claim 에 포함하지 않았다.
|
||||
- WebFetch 로 1차 처리한 결과가 원문 표(FCV 표 등)를 산문으로 재구성하면서 일부 수치 매핑이 모호해질 위험이 있어, curl 로 원본 HTML 을 직접 받아 self-grep 검증했다(WebFetch 결과는 참고용으로만 사용, 최종 인용은 curl 원문 기준).
|
||||
- 추가로 봐야 할 동일 출처 페이지: `https://www.mongodb.com/docs/manual/core/transactions-production-consideration/` (runtime limit 구체 수치, `transactionLifetimeLimitSeconds`), 드라이버별 transient transaction error 처리 가이드(예: `https://www.mongodb.com/docs/drivers/node/current/fundamentals/transactions/` 등 언어별 페이지)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/write-concern-mongodb-official]] (write concern `w`/`j`/`wtimeout` 상세), [[raw/official-docs/read-preference-mongodb-official]] (read preference 5종 모드 및 트랜잭션 내 `primary` 고정 제약)
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: official-doc / MongoDB — Production Considerations for Transactions (Runtime & Oplog Size Limits)
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/core/transactions-production-consideration/
|
||||
archive_url:
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
related_projects: []
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, transaction]
|
||||
created: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Production Considerations for Transactions (Runtime & Oplog Size Limits)
|
||||
|
||||
> Layer: `raw/` — 외부 자료(공식 문서 / 대기업 기술 블로그)의 **원문 발췌·출처 기록**.
|
||||
> 본 템플릿은 `raw/official-docs/` 와 `raw/company-tech-blogs/` 두 폴더가 공유.
|
||||
> 검증된 요약은 `/ingest` 후 `wiki/concepts/`에 `source-summary-template` 형식으로 별도 작성. 원본은 raw에 영구 보관.
|
||||
|
||||
## source_type 허용값
|
||||
|
||||
`source_type: official-doc` — MongoDB 공식 매뉴얼(레퍼런스) 문서. 형제 자료 [[raw/official-docs/transactions-mongodb-official]]의 "Production Considerations 페이지 참조" gap 을 닫기 위해 그 페이지 자체를 dispatch.
|
||||
|
||||
## Parent / 활용 branch
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | (부분) multi-document transaction 은 시간·크기 한계를 가지므로 무제한 사용을 전제하지 않고, 한계 초과 시 동작을 계약에 명시한다 — runtime limit("less than one minute" 기본값 + `transactionLifetimeLimitSeconds` 초과 시 periodic cleanup 에 의한 abort)과 oplog entry 크기 한계(16MB, transaction 전체가 아닌 entry 단위) 근거. **주의**: 이 URL 은 "transient transaction error 재시도 책임을 애플리케이션 계약으로 못박는다" 결정은 정당화하지 못한다 — `TransientTransactionError`/`UnknownTransactionCommitResult` 라벨이 이 URL 본문에 없음(Self-Grep 0 매치, 아래 Usage Boundaries 참조). 이 결정은 별도 URL(드라이버별 에러 처리 가이드) dispatch 필요 |
|
||||
|
||||
## 출처
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/core/transactions-production-consideration/
|
||||
- 아카이브 URL: (미제공)
|
||||
- 저자 / 조직: MongoDB, Inc. (공식 매뉴얼)
|
||||
- 발행일: (문서 자체에 명시 없음 — 버전별 롤링 업데이트되는 레퍼런스 페이지. 2026-07-28 fetch 시점 기준 breadcrumb 상 "Database Manual 8.3 (Current)" 확인)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 왜 저장했는지
|
||||
|
||||
`feature-mongo-runtime-baseline-contract` branch 가 "transaction 은 시간·크기 한계를 가지므로 무제한 사용을 전제하지 않는다"를 구체 수치(runtime limit 기본값, oplog entry 크기 제한)로 뒷받침하기 위해 보관. 형제 자료 [[raw/official-docs/transactions-mongodb-official]]의 Claim `MONGO-TXN-C9`가 "구체 수치는 이 URL 범위 밖"이라며 위임한 페이지가 바로 이 문서.
|
||||
|
||||
## 핵심 인용
|
||||
|
||||
> [§Availability] "MongoDB standalone deployments do not support transactions. To use transactions, your deployment must be a multiple node replica set."
|
||||
|
||||
> [§Runtime Limit] "By default, a transaction must have a runtime of less than one minute. You can modify this limit using transactionLifetimeLimitSeconds for the mongod instances." [...] "Transactions that exceeds this limit are considered expired and will be aborted by a periodic cleanup process."
|
||||
|
||||
> [§Oplog Size Limit] "MongoDB creates as many oplog entries as necessary to the encapsulate all write operations in a transaction, instead of a single entry for all write operations in the transaction." [...] "each oplog entry still must be within the BSON document size limit of 16MB."
|
||||
|
||||
> [§WiredTiger Cache] "When you encounter an error during individual operation in the transaction, abort and retry the transaction."
|
||||
|
||||
> [§WiredTiger Cache — Note] "If a transaction is too large to ever fit in the WiredTiger cache, the transaction aborts and returns a TransactionTooLargeForCache error."
|
||||
|
||||
## Claims Extracted
|
||||
|
||||
| Claim ID | Claim | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-TXN-PROD-C1 | MongoDB standalone 배포는 transaction 을 지원하지 않으며, transaction 을 쓰려면 multiple-node replica set 이어야 한다 | [§Availability] "MongoDB standalone deployments do not support transactions. To use transactions, your deployment must be a multiple node replica set." | `official-vendor-doc` | 모든 MongoDB 배포 형태(standalone vs replica set/sharded cluster) 판단 | sharded cluster 고유 추가 제약(별도 "Production Considerations (Sharded Clusters)" 페이지로 위임됨)까지는 다루지 않음 |
|
||||
| MONGO-TXN-PROD-C2 | Transaction 은 기본적으로 1분(one minute) 미만의 runtime 을 가져야 하며, 이 한계는 `transactionLifetimeLimitSeconds` 파라미터(sharded cluster 는 모든 shard replica set member 에 동일 적용)로 조정 가능하고, 한계를 초과한 transaction 은 expired 로 간주되어 periodic cleanup 프로세스에 의해 abort 된다 | [§Runtime Limit] "By default, a transaction must have a runtime of less than one minute. You can modify this limit using transactionLifetimeLimitSeconds for the mongod instances." [...] "Transactions that exceeds this limit are considered expired and will be aborted by a periodic cleanup process." | `official-vendor-doc` | replica-set/sharded-cluster 배포의 `mongod` 인스턴스 런타임 한계 | **`transactionLifetimeLimitSeconds` 의 정확한 기본 정수값(예: 60)은 이 문장에 없다** — 원문은 "less than one minute" 이라고만 서술하며 정수 초 표기는 없음. 정확한 기본 정수값은 이 URL 범위 밖(파라미터 레퍼런스 페이지 확인 필요, 아래 Usage Boundaries 참조) |
|
||||
| MONGO-TXN-PROD-C3 | Transaction 이 여러 write 연산을 포함해도 단일 oplog entry 가 아니라 필요한 만큼의 여러 oplog entry 로 기록되며, 이로써 이전에 있던 "transaction 전체 16MB 크기 제한"은 제거되지만, 개별 oplog entry 각각은 여전히 16MB BSON document 크기 제한을 지켜야 한다 | [§Oplog Size Limit] "MongoDB creates as many oplog entries as necessary to the encapsulate all write operations in a transaction, instead of a single entry for all write operations in the transaction." [...] "each oplog entry still must be within the BSON document size limit of 16MB." | `official-vendor-doc` | transaction 내 여러 write 연산의 oplog 기록 방식과 크기 한계 | transaction 이 포함할 수 있는 write 연산의 "개수" 자체에 대한 상한은 이 문장이 규정하지 않음 — 다루는 것은 개별 oplog entry 의 BSON 크기 한계뿐 |
|
||||
| MONGO-TXN-PROD-C4 | Transaction 이 WiredTiger cache 에 담기기에 너무 크면 `TransactionTooLargeForCache` 에러와 함께 abort 된다 | [§WiredTiger Cache — Note] "If a transaction is too large to ever fit in the WiredTiger cache, the transaction aborts and returns a TransactionTooLargeForCache error." | `official-vendor-doc` | WiredTiger storage engine 을 사용하는 배포에서 대용량 transaction 실행 시 실패 모드 | 이 에러가 발생하는 정확한 크기 임계값(캐시 크기 대비 비율 등)은 이 문장에 없음 |
|
||||
| MONGO-TXN-PROD-C5 | WiredTiger cache pressure 를 막기 위해, transaction 내 개별 연산에서 에러를 만나면 그 transaction 을 abort 하고 재시도(retry)하라고 권고한다 | [§WiredTiger Cache] "When you encounter an error during individual operation in the transaction, abort and retry the transaction." | `official-vendor-doc` | WiredTiger cache pressure 완화 목적의 일반적 "에러 시 abort+retry" 운영 권고 | **이것은 `TransientTransactionError`/`UnknownTransactionCommitResult` 에러 라벨 기반의 표준 재시도 프로토콜이 아니다.** 이 URL 본문에는 두 라벨 모두 존재하지 않는다(Self-Grep 확인, 0 매치) — 이 문장은 "왜 abort+retry 를 하는가"(cache pressure 완화)를 말할 뿐, 애플리케이션이 라벨을 어떻게 감지해 전체 transaction 을 재시도해야 하는지의 프로토콜은 다루지 않음. 그 프로토콜은 드라이버별 에러 처리 가이드(별도 URL)의 범위 |
|
||||
|
||||
### Strength 허용값
|
||||
|
||||
- `official-standard` — RFC, 표준 사양, 언어/프로토콜 표준
|
||||
- `official-vendor-doc` — Spring, Keycloak, AWS, Google, MongoDB 등 공식 벤더 문서
|
||||
- `official-reference` — 공식 reference/API 문서
|
||||
- `company-case-study` — 대기업/실무 기술 블로그의 특정 사례
|
||||
- `engineering-blog` — 개인/팀 블로그의 엔지니어링 해설
|
||||
- `tutorial` — 튜토리얼/가이드. 일반화 금지
|
||||
- `needs-confirmation` — 원문만으로는 적용 판단 불가
|
||||
|
||||
## Usage Boundaries
|
||||
|
||||
- 이 자료가 직접 증명하는 것:
|
||||
- `MONGO-TXN-PROD-C1`: standalone 배포는 transaction 미지원, multi-node replica set 필요
|
||||
- `MONGO-TXN-PROD-C2`: transaction runtime 기본 한계는 "1분 미만"이며 `transactionLifetimeLimitSeconds` 로 조정, 초과 시 periodic cleanup 이 abort
|
||||
- `MONGO-TXN-PROD-C3`: oplog entry 는 transaction 당 여러 개로 기록되어 transaction 전체 16MB 제한은 없지만, entry 단위 16MB BSON 제한은 여전히 존재
|
||||
- `MONGO-TXN-PROD-C4`: cache 초과 시 `TransactionTooLargeForCache` 에러로 abort
|
||||
- `MONGO-TXN-PROD-C5`: 개별 연산 에러 시 abort+retry 하라는 cache-pressure 완화 목적의 일반 권고
|
||||
- 이 자료가 증명하지 않는 것 (사용자가 명시적으로 요청했으나 이 URL 에 부재 — Self-Grep 확인, 0 매치, 절대 추정하지 않음):
|
||||
- **`transactionLifetimeLimitSeconds` 의 정확한 기본 정수값(초)**. 원문은 "less than one minute" 이라고만 서술한다. "60초"라는 숫자는 이 URL 어디에도 없다 — 별도 파라미터 레퍼런스 페이지(`/docs/manual/reference/parameters/#mongodb-parameter-param.transactionLifetimeLimitSeconds`) 확인 필요
|
||||
- **`TransientTransactionError` 라벨** — 이 URL 본문에 전혀 등장하지 않는다(grep 0 매치)
|
||||
- **`UnknownTransactionCommitResult` 라벨** — 이 URL 본문에 전혀 등장하지 않는다(grep 0 매치)
|
||||
- **"transaction 을 짧게 유지하라"(작은 문서 수/짧은 실행 시간) 는 명시적 권고 문장** — 이 URL 에 그런 문장은 없다(grep 0 매치, "keep...short"/"small number of documents" 류 문구 부재). 가장 근접한 내용은 WiredTiger Cache 섹션의 "에러 시 abort+retry" 권고(`MONGO-TXN-PROD-C5`)뿐이며, 이는 "짧게 유지하라"는 사전 설계 권고가 아니라 "에러 발생 후 사후 대응" 권고다
|
||||
- 내 프로젝트에 적용하려면 추가 확인이 필요한 것:
|
||||
- `transactionLifetimeLimitSeconds` 기본 정수값 — 파라미터 레퍼런스 페이지 별도 dispatch
|
||||
- `TransientTransactionError`/`UnknownTransactionCommitResult` 라벨과 애플리케이션 재시도 계약 — 드라이버별 에러 처리 가이드(예: MongoDB Manual "Transactions and Sessions" 또는 언어별 driver 문서) 별도 dispatch 필요
|
||||
- "transaction 을 짧게 유지하라"는 실무 권고 자체가 필요하면 별도 출처(공식 문서 다른 페이지 또는 기술 블로그) 확보 필요 — 이 URL 은 근거가 아님
|
||||
|
||||
## 메모
|
||||
|
||||
- 사용자가 요청한 5개 항목 중 (1) runtime limit 기본값 서술과 (2) oplog entry 16MB 한계는 이 URL 에서 verbatim 확보했다. 단 (1)의 정확한 초 단위 숫자는 원문에 없어 "less than one minute"로만 기록했다.
|
||||
- (3) `TransientTransactionError`, (4) `UnknownTransactionCommitResult`, (5) "짧게 유지하라" 권고는 이 URL 본문에 **부재**함을 self-grep(0 매치)으로 확인했다. 추정하지 않고 Claims Extracted 에서 제외했으며 Usage Boundaries 에 gap 으로 명시했다.
|
||||
- WebFetch 1차 결과가 표/목록을 산문으로 재구성하며 "TransactionTooLargeForCache" 등 일부 세부를 누락했고 TransientTransactionError 관련 문구를 임의로 언급하지 않았음(다행히 fabrication 없음)을 확인 후, curl 로 원본 HTML 을 직접 받아 self-grep 검증했다(WebFetch 결과는 참고용, 최종 인용은 curl 원문 기준) — 형제 raw 문서와 동일한 방법론.
|
||||
- 추가로 봐야 할 동일 출처 페이지: `https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.transactionLifetimeLimitSeconds` (정확한 기본 정수값), 드라이버별 transient transaction error 처리 가이드(예: `https://www.mongodb.com/docs/manual/core/transactions-in-applications/` 또는 언어별 driver 문서 — `TransientTransactionError`/`UnknownTransactionCommitResult` 라벨 확인용), `https://www.mongodb.com/docs/manual/core/transactions-production-consideration-sharded-clusters/` (sharded cluster 전용 추가 고려사항)
|
||||
|
||||
## 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: [[raw/official-docs/transactions-mongodb-official]] (multi-document transaction 배포 요건·concern 기본값 — 이 문서의 gap `MONGO-TXN-C9`/`C10`을 이 자료가 부분적으로 닫음), [[raw/official-docs/write-concern-mongodb-official]], [[raw/official-docs/read-preference-mongodb-official]]
|
||||
- 이 자료를 인용한 wiki 요약: `[[wiki/concepts/...]]` (생성 시)
|
||||
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: "official-doc / MongoDB — Write Concern (w / j / wtimeout)"
|
||||
source_type: official-doc
|
||||
url: https://www.mongodb.com/docs/manual/reference/write-concern/
|
||||
archive_url:
|
||||
status: raw
|
||||
confidence: high
|
||||
tags: [official-doc, ca-skeleton, persistence, mongodb, write-concern]
|
||||
related_projects: [ca-skeleton]
|
||||
related_branches: [feature-mongo-runtime-baseline-contract]
|
||||
created: 2026-07-28
|
||||
last_reviewed: 2026-07-28
|
||||
---
|
||||
|
||||
# official-doc / MongoDB — Write Concern (w / j / wtimeout)
|
||||
|
||||
> Layer: `raw/official-docs/` — MongoDB 공식 Manual reference (`write-concern`) 원문 발췌.
|
||||
> `feature-mongo-runtime-baseline-contract` 의 "MongoDB write 는 write concern 을 명시하고 기본값에 의존하지 않는다" + "journaling(j) / wtimeout 을 명시한다" 결정 근거.
|
||||
|
||||
## Parent / 활용 branch (필수)
|
||||
|
||||
| Branch | 이 자료가 정당화하는 결정 |
|
||||
|---|---|
|
||||
| [[raw/branch-notes/feature-mongo-runtime-baseline-contract]] | MongoDB write 는 write concern 을 명시하고 기본값(`w: "majority"` 여부)에 의존하지 않는다 + journaling(`j`) / `wtimeout` 을 명시한다 — `w`/`j`/`wtimeout` 옵션 의미, 서버 implicit default, `wtimeout` 초과 시 write 가 rollback 되지 않는다는 실패 의미의 verbatim 근거 |
|
||||
|
||||
## 컨텍스트
|
||||
|
||||
ca-skeleton 은 MongoDB 를 optional adapter(문서 저장소 축)로 채택할 수 있다. 본 branch 는 write concern 을 애플리케이션/드라이버 레벨에서 **명시**할지, 서버 implicit default 에 의존할지를 결정해야 한다. 이 판단에는 (1) `w` 값의 의미(숫자 vs `"majority"`), (2) `j` 가 무엇을 보장하는지, (3) `wtimeout` 초과 시 실제로 무슨 일이 일어나는지(=write 취소 여부), (4) 서버가 실제로 어떤 값을 implicit default 로 쓰는지에 대한 공식 정의가 필요하다.
|
||||
|
||||
## 출처 / Source
|
||||
|
||||
- 원본 URL: https://www.mongodb.com/docs/manual/reference/write-concern/
|
||||
- 아카이브 URL: (미수집)
|
||||
- 저자 / 조직: MongoDB, Inc.
|
||||
- 발행일: MongoDB Manual (현재 버전 — 본문에 "Starting in MongoDB 8.0" 문구 포함되어 있어 8.0 이후 반영분 포함된 최신 manual 로 판단)
|
||||
- 마지막 확인일: 2026-07-28
|
||||
|
||||
## 핵심 인용 / Key quotes (verbatim)
|
||||
|
||||
> [§Write Concern Definition] "Write concern describes the level of acknowledgment requested from MongoDB for write operations"
|
||||
|
||||
> [§`w` Option (Acknowledgment Count)] "requests acknowledgment that the write operation has propagated to a specified number of `mongod` instances or to `mongod` instances with specified tags."
|
||||
|
||||
> [§`w` Values — `"majority"` row] "calculated majority of data-bearing voting members have durably written the change to their local oplog"
|
||||
|
||||
> [§`j` Option (Journal Durability)] "requests acknowledgment from MongoDB that the write operation has been written to the on-disk journal."
|
||||
|
||||
> [§`wtimeout` Option (Time Limit)] "`wtimeout` specifies a time limit, in milliseconds, for a write operation to propagate to enough members to achieve the write concern after the operation succeeds on the primary."
|
||||
|
||||
> [§`wtimeout` Option — Key behaviors] "MongoDB returns a write concern error after the specified limit, even if the required write concern will eventually succeed"
|
||||
|
||||
> [§`wtimeout` Option — Key behaviors] "When these write operations return, MongoDB does not undo successful data modifications performed before the write concern exceeded the `wtimeout` time limit"
|
||||
|
||||
> [§Implicit Default Write Concern] "The implicit default write concern is `{ w: "majority" }`"
|
||||
|
||||
## Claims Extracted / 추출된 주장
|
||||
|
||||
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
||||
|---|---|---|---|---|---|
|
||||
| MONGO-WRITECONCERN-C1 | Write concern 은 MongoDB 가 write 작업에 대해 요청하는 acknowledgment(확인 응답) 수준을 기술하는 개념이다 | [§Write Concern Definition] "Write concern describes the level of acknowledgment requested from MongoDB for write operations" | `official-vendor-doc` | standalone `mongod` / replica set / sharded cluster 전반의 write 요청 | 특정 드라이버(Java/Node 등)가 write concern 을 설정하는 구체 API 형태 — 이 인용은 서버 개념 정의만 다룸 |
|
||||
| MONGO-WRITECONCERN-C2 | `w` 옵션은 write 가 지정된 개수의 `mongod` 인스턴스(또는 지정 태그를 만족하는 인스턴스)로 전파되었음을 확인해달라는 요청이다 | [§`w` Option] "requests acknowledgment that the write operation has propagated to a specified number of `mongod` instances or to `mongod` instances with specified tags." | `official-vendor-doc` | `w: <number>` 형태 값의 일반 의미 | 각 멤버가 durable write 로 카운트되는 순서·우선순위(예: arbiter 제외 여부)의 세부 규칙 — 본 인용 범위 밖 |
|
||||
| MONGO-WRITECONCERN-C3 | `w: "majority"` 는 데이터를 보유한 투표권 멤버들의 계산된 과반수가 자신의 local oplog 에 변경사항을 durable 하게 기록했음을 확인하는 것을 요청한다 | [§`w` Values] "calculated majority of data-bearing voting members have durably written the change to their local oplog" | `official-vendor-doc` | `w: "majority"` write concern 을 사용하는 모든 replica set 배포 | journal(`j`) 까지 포함해서 확인하는지는 이 문장 자체엔 없음 — `writeConcernMajorityJournalDefault` 설정에 따라 별도 결정됨 (C8 참고) |
|
||||
| MONGO-WRITECONCERN-C4 | `j` 옵션은 write 작업이 on-disk journal 에 기록되었음을 MongoDB 로부터 확인받는 것을 요청한다 | [§`j` Option] "requests acknowledgment from MongoDB that the write operation has been written to the on-disk journal." | `official-vendor-doc` | `j: true` 를 명시한 write 요청 전반 | journal 없이 write 가 유실될 수 있는 구체적 장애 시나리오(OS crash 등) 자체는 이 문장이 다루지 않음 |
|
||||
| MONGO-WRITECONCERN-C5 | `wtimeout` 은 write 작업이 primary 에서 성공한 뒤 write concern 을 달성할 만큼 충분한 멤버로 전파되는 데 허용되는 시간 제한(밀리초)을 지정한다 | [§`wtimeout` Option] "`wtimeout` specifies a time limit, in milliseconds, for a write operation to propagate to enough members to achieve the write concern after the operation succeeds on the primary." | `official-vendor-doc` | `wtimeout` 이 명시된 모든 write 요청 | `w<=1` 일 때는 `wtimeout` 이 적용되지 않는다는 것 — 본 인용은 그 예외 조건 자체를 포함하지 않음(별도 bullet) |
|
||||
| MONGO-WRITECONCERN-C6 | `wtimeout` 시간 제한 초과 시, 요청한 write concern 이 결국 충족될 것이었다 해도 MongoDB 는 write concern error 를 반환한다 | [§`wtimeout` Option — Key behaviors] "MongoDB returns a write concern error after the specified limit, even if the required write concern will eventually succeed" | `official-vendor-doc` | `wtimeout` 을 명시한 write 요청의 클라이언트 응답 성격 | 이 에러를 받은 애플리케이션이 재시도해야 하는지, 재시도 시 idempotent 하게 처리되는지는 이 문장이 규정하지 않음 — 애플리케이션 레벨 결정 |
|
||||
| MONGO-WRITECONCERN-C7 | `wtimeout` 이 초과되어 write 작업이 반환되더라도, MongoDB 는 write concern 이 초과되기 전에 이미 적용된 성공적인 data modification 을 되돌리지(undo) 않는다 | [§`wtimeout` Option — Key behaviors] "When these write operations return, MongoDB does not undo successful data modifications performed before the write concern exceeded the `wtimeout` time limit" | `official-vendor-doc` | `wtimeout` 을 명시한 모든 write 작업의 실패 모드 — **`wtimeout` 은 write 자체를 취소하는 옵션이 아니라, 확인 응답 대기의 시간 제한일 뿐임을 증명** | 이 write 가 다른 replica 로 반드시 복제된다는 durability 보장 자체까지는 증명하지 않음 — primary 에 적용된 변경을 rollback 하지 않는다는 것만 증명 |
|
||||
| MONGO-WRITECONCERN-C8 | 대부분의 MongoDB 배포에서 implicit(암묵적) default write concern 은 `{ w: "majority" }` 이다 | [§Implicit Default Write Concern] "The implicit default write concern is `{ w: "majority" }`" | `official-vendor-doc` | arbiter 가 없거나, arbiter 가 있어도 non-arbiter 수가 투표 노드 과반수를 초과하는 replica set 배포 | arbiter 를 포함한 특정 topology(예: non-arbiter ≤ majority(voting nodes))에서는 default 가 `{ w: 1 }` 로 낮아지는 예외 공식이 별도로 존재함 — 이 문장 자체는 그 예외 공식을 포함하지 않음(별도 코드 블록, 이번 raw 에 verbatim self-grep 미시도) |
|
||||
|
||||
## Usage Boundaries / 적용 경계
|
||||
|
||||
- **이 자료가 직접 증명하는 것**:
|
||||
- `MONGO-WRITECONCERN-C1`: write concern 의 일반 정의
|
||||
- `MONGO-WRITECONCERN-C2`~`C3`: `w` 옵션 값의 의미(숫자 propagation count / `"majority"` = 과반수 durable oplog write)
|
||||
- `MONGO-WRITECONCERN-C4`: `j` 옵션 = on-disk journal 기록 확인
|
||||
- `MONGO-WRITECONCERN-C5`: `wtimeout` = 시간 제한(ms) 정의
|
||||
- `MONGO-WRITECONCERN-C6`~`C7`: `wtimeout` 초과 시 write concern error 를 반환하지만, 이미 적용된 write 를 rollback/undo 하지 않는다는 실패 모드
|
||||
- `MONGO-WRITECONCERN-C8`: 대부분 배포의 implicit default 는 `{ w: "majority" }`
|
||||
- **이 자료가 증명하지 않는 것**:
|
||||
- arbiter 가 있는 replica set 에서 default 가 `{ w: 1 }` 로 낮아지는 정확한 공식 조건(코드 블록 형태 — 이번 raw 에서 verbatim 인용/self-grep 미포함, 별도 확인 필요)
|
||||
- MongoDB Atlas 등 managed 서비스가 self-managed 배포와 동일한 implicit default 를 쓰는지 여부
|
||||
- 특정 드라이버(Java Reactive Streams / Spring Data MongoDB 등)가 write concern 을 어떤 설정 키·API 로 노출하는지 — 이 자료는 서버 개념만 정의, 드라이버 바인딩은 별도 자료 필요
|
||||
- 트랜잭션 레벨 write concern 설정 방식(문서 본문에 "멀티 도큐먼트 트랜잭션은 개별 operation 이 아니라 트랜잭션 레벨에서 write concern 을 설정해야 한다"는 별도 주의사항이 있으나, 이번 raw 에는 별도 claim 으로 등록하지 않음 — 아래 메모 참고)
|
||||
- **ca-skeleton 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
||||
- ca-skeleton 이 실제로 채택할 replica set 구성(arbiter 유무)에 따라 서버 implicit default 가 `{ w: "majority" }` 인지 `{ w: 1 }` 인지 확인 필요 — `C8` 의 예외 공식 verbatim 별도 수집
|
||||
- Spring Data MongoDB(또는 채택할 드라이버)의 `WriteConcern` 설정 API/property 이름 — 별도 official-doc 필요
|
||||
- `j`/`wtimeout` 을 애플리케이션에서 명시할 구체 값(예: `wtimeout` 몇 ms) 은 이 자료가 아니라 branch 의 §구현 가이드에서 결정 — 본 자료는 "명시하지 않으면 무엇에 의존하게 되는지"와 "초과 시 무슨 일이 일어나는지"까지만 증명
|
||||
|
||||
## 메모 / Notes
|
||||
|
||||
- 이번 WebFetch 결과는 소형 모델이 원문을 markdown(볼드 `**`) 으로 재구성한 형태였다. Self-Grep 시 `**` 강조 마커가 인용 중간에 끼어 있는 경우 grep -F 매칭이 깨져(`w` 옵션 정의 인용 1차 시도 실패), 볼드 구간 경계를 벗어나지 않는 부분 문자열로 재선정해 재검증 통과시켰다. 원문 페이지 자체의 실제 HTML 은 이번에 별도 curl 로 대조하지 않음 — 다음 재검증 시 curl 직접 fetch 로 교차 확인 권장.
|
||||
- 미수집: 멀티 도큐먼트 트랜잭션의 write concern 설정 위치(transaction-level vs operation-level) 관련 문장. `wtimeout: 0` 이 "wtimeout 옵션 없는 것과 동일"이라는 문장도 이번엔 별도 claim 화하지 않음(2차 우선순위) — 필요 시 재수집.
|
||||
- 다음 fetch 후보: MongoDB 공식 `journaling` 페이지(journal 미기록 시 실제 유실 시나리오), `replica-set-election` 페이지(arbiter 예외 공식의 정확한 조건).
|
||||
|
||||
## Related / 관련
|
||||
|
||||
- 같은 주제 다른 official-doc: 없음 (vault 내 최초 MongoDB 공식 문서)
|
||||
- 인용하는 branch: [[raw/branch-notes/feature-mongo-runtime-baseline-contract]]
|
||||
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract]]
|
||||
- 인용한 wiki 요약: (미작성)
|
||||
Reference in New Issue
Block a user