Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/schema-and-data-contracts/case/case-grpc-policy-f03.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다
- 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5
  (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를
  techviz 로 만들었다
- 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs
  돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다.
  Concept 이 인용한 코드가 SSOT 에 없어 뺐다
- candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 12:39:20 +09:00

4.7 KiB

kind, slug, title, topic, project, status, sourceRevision, rootTreeNode, evidenceCapturedOn, assets, evidence, source, module, priority
kind slug title topic project status sourceRevision rootTreeNode evidenceCapturedOn assets evidence source module priority
CASE grpc-policy-f03 직렬 스트림 기록기의 가장 오래된 것 버리기가 잘못된 메시지의 바이트를 뺀다 schema-and-data-contracts clean-architecture-backend-template 게시 전 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 case:grpc-policy-f03 2026-09-01
key file
grpc-policy-f03 ../../../final/evidence/rendered/grpc-policy-f03.svg
key file
grpc-policy-f03-diagram ../../../final/assets/diagrams/grpc-policy-f03.svg
../../../final/evidence/raw/grpc-policy-f03.txt
원본 분석 절은 final/document.md#a20-grpc-policy#L249 이다.
grpc-policy P2

직렬 스트림 기록기의 가장 오래된 것 버리기가 잘못된 메시지의 바이트를 뺀다

버려지는 것은 꺼낸 봉투인데 빼는 값은 새 메시지의 크기다. 봉투는 크기를 성분으로 담지 않으므로 이 지점에서 버려지는 크기를 알 방법이 없다.

문제

버려지는 것은 꺼낸 봉투인데 빼는 값은 새 메시지의 크기다.

봉투는 크기를 성분으로 담지 않으므로 이 지점에서 버려지는 크기를 알 방법이 없다.

결론

계산을 따라가면 이렇다.

한 번의 DROP_OLDEST 마다 queuedBytes 는 nextBytes 만큼 빠졌다가 enqueue 에서 같은 값만큼 다시 더해진다 — 순변화 0.

그런데 큐의 실제 내용은 nextBytes - droppedBytes 만큼 바뀐다.

그 차이가 매 낙차마다 쌓인다.

방향은 둘 다 틀렸다.

들어오는 메시지가 버려지는 것보다 크면 추적값이 실제보다 낮아져 바이트 경계가 늦게 발화한다(메모리).

반대면 실제보다 높아져 경계가 이르게 발화한다(불필요한 종료·낙차).

누적 바이트는 흐름 제어 정책의 판정 입력이고, 바이트 경계의 존재 이유가 javadoc 에 있다 — 개수 경계만 있으면 메모리 한도를 가장 큰 메시지가 정한다.

검증 환경

OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : 큐에서 빼는 대상과 계수기에서 차감하는 값의 대조, 봉투 성분 확인 소스 수정 : x

재현 조건

원문은 final/document.md#a20-grpc-policy#L249 에 있다.

본문

버려지는 것은 꺼낸 봉투인데 빼는 값은 새 메시지의 크기다. 봉투는 크기를 성분으로 담지 않으므로 이 지점에서 버려지는 크기를 알 방법이 없다.

두 자리가 가리키는 대상

:::evidence key="grpc-policy-f03-diagram" alt="큐에서 빠지는 것 쪽에 가장 오래된 봉투와 그 봉투의 바이트가 놓이고 차감되는 값 쪽에 새 메시지와 그 바이트가 놓인다" caption="두 자리가 가리키는 대상" zoom="false" :::

계산을 따라가면

한 번의 DROP_OLDEST 마다 queuedBytesnextBytes 만큼 빠졌다가 enqueue 에서 같은 값만큼 다시 더해진다 — 순변화 0. 그런데 큐의 실제 내용은 nextBytes - droppedBytes 만큼 바뀐다. 그 차이가 매 낙차마다 쌓인다.

빼는 값과 버리는 대상

:::evidence key="grpc-policy-f03" alt="분석 문서 final/document.md#a20-grpc-policy 에서 이 기록의 근거 절을 그대로 잘라낸 15줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md#a20-grpc-policy 발췌 — 15줄" zoom="true" :::

방향은 둘 다 틀렸다

들어오는 메시지가 버려지는 것보다 크면 추적값이 실제보다 낮아져 바이트 경계가 늦게 발화한다(메모리). 반대면 실제보다 높아져 경계가 이르게 발화한다(불필요한 종료·낙차). 누적 바이트는 흐름 제어 정책의 판정 입력이고, 바이트 경계의 존재 이유가 javadoc 에 있다 — 개수 경계만 있으면 메모리 한도를 가장 큰 메시지가 정한다.

flush 가 오차를 끊는다

flush() 가 큐를 비우면서 queuedBytes = 0L 로 되돌리므로 오차가 flush 를 건너 누적되지는 않는다. 그래서 이것은 영구 드리프트가 아니라 한 flush 주기 안의 폭주 구간에서 바이트 경계를 잘못 판정하는 결함이다. 낙차가 일어나는 상황이 곧 소비자가 못 따라가는 상황이고, 그때 flush 간격이 가장 길어진다.

확인하지 못한 것

실제 스트림으로 버리기를 유발해 바이트 오차를 관측하지 않았다. 봉투가 크기를 성분으로 담지 않는다는 것으로 판정했다.