# 넘침 분기 전체 GrpcSerializedStreamWriter.java:70 if (!state.acceptsMessages()) { GrpcSerializedStreamWriter.java:71 return GrpcStreamWriteResult.REJECTED_CLOSED; GrpcSerializedStreamWriter.java:72 } GrpcSerializedStreamWriter.java:73 long nextBytes = payloadSizer.getAsLong(); GrpcSerializedStreamWriter.java:74 GrpcFlowControlDecision decision = GrpcSerializedStreamWriter.java:75 flowControl.decide(queue.size(), queuedBytes, nextBytes, transportReady); GrpcSerializedStreamWriter.java:76 return switch (decision.action()) { GrpcSerializedStreamWriter.java:77 case TERMINATE -> { GrpcSerializedStreamWriter.java:78 state = GrpcStreamWriterState.DRAINING; GrpcSerializedStreamWriter.java:79 yield GrpcStreamWriteResult.OVERFLOW_TERMINATING; GrpcSerializedStreamWriter.java:80 } GrpcSerializedStreamWriter.java:81 case DROP_OLDEST -> { GrpcSerializedStreamWriter.java:82 GrpcStreamEnvelope dropped = queue.pollFirst(); GrpcSerializedStreamWriter.java:83 if (dropped != null) { GrpcSerializedStreamWriter.java:84 queuedBytes = Math.max(0L, queuedBytes - nextBytes); GrpcSerializedStreamWriter.java:85 droppedMessages++; GrpcSerializedStreamWriter.java:86 } GrpcSerializedStreamWriter.java:87 enqueue(kind, payload, snapshotVersion, resumeToken, nextBytes); GrpcSerializedStreamWriter.java:88 yield GrpcStreamWriteResult.DROPPED; GrpcSerializedStreamWriter.java:89 } GrpcSerializedStreamWriter.java:90 case PAUSE, PROCEED -> { GrpcSerializedStreamWriter.java:91 enqueue(kind, payload, snapshotVersion, resumeToken, nextBytes); GrpcSerializedStreamWriter.java:92 yield GrpcStreamWriteResult.ACCEPTED; GrpcSerializedStreamWriter.java:93 } GrpcSerializedStreamWriter.java:94 }; GrpcSerializedStreamWriter.java:95 } GrpcSerializedStreamWriter.java:96 GrpcSerializedStreamWriter.java:97 private void enqueue( 크기를 어디서 얻는가 : GrpcSerializedStreamWriter.java:30 private final LongSupplier payloadSizer; GrpcSerializedStreamWriter.java:41 * @param payloadSizer the serialized size of the next payload, for the byte bound GrpcSerializedStreamWriter.java:48 LongSupplier payloadSizer) { GrpcSerializedStreamWriter.java:53 || payloadSizer == null) { GrpcSerializedStreamWriter.java:60 this.payloadSizer = payloadSizer; GrpcSerializedStreamWriter.java:73 long nextBytes = payloadSizer.getAsLong(); # 봉투가 담는 것 GrpcStreamEnvelope.java:16 public record GrpcStreamEnvelope( GrpcStreamEnvelope.java:17 GrpcStreamId streamId, GrpcStreamEnvelope.java:18 long sequence, GrpcStreamEnvelope.java:19 Kind kind, GrpcStreamEnvelope.java:20 Optional snapshotVersion, GrpcStreamEnvelope.java:21 Optional resumeToken, GrpcStreamEnvelope.java:22 Optional terminationReason, GrpcStreamEnvelope.java:23 Optional payload) { GrpcStreamEnvelope.java:24 GrpcStreamEnvelope.java:25 /** What a stream message is. */ # 누적 바이트가 무엇에 쓰이는가 — decide 의 분기 전부 GrpcFlowControlPolicy.java:56 boolean overflowsCount = queuedMessages >= maxQueuedMessages; GrpcFlowControlPolicy.java:57 boolean overflowsBytes = queuedBytes + nextMessageBytes > maxQueuedBytes; GrpcFlowControlPolicy.java:58 if (overflowsCount || overflowsBytes) { GrpcFlowControlPolicy.java:59 return switch (slowConsumerPolicy) { GrpcFlowControlPolicy.java:60 case TERMINATE -> GrpcFlowControlPolicy.java:61 new GrpcFlowControlDecision( GrpcFlowControlPolicy.java:62 GrpcFlowControlDecision.Action.TERMINATE, GrpcFlowControlPolicy.java:63 queuedMessages, GrpcFlowControlPolicy.java:64 queuedBytes, GrpcFlowControlPolicy.java:65 overflowsCount GrpcFlowControlPolicy.java:66 ? "queue is at its " + maxQueuedMessages + "-message bound" GrpcFlowControlPolicy.java:67 : "queue would exceed its " + maxQueuedBytes + "-byte bound"); GrpcFlowControlPolicy.java:68 case DROP_OLDEST -> GrpcFlowControlPolicy.java:69 new GrpcFlowControlDecision( GrpcFlowControlPolicy.java:70 GrpcFlowControlDecision.Action.DROP_OLDEST, GrpcFlowControlPolicy.java:71 queuedMessages, GrpcFlowControlPolicy.java:72 queuedBytes, GrpcFlowControlPolicy.java:73 "the profile permits loss; dropping the oldest queued message"); GrpcFlowControlPolicy.java:74 }; GrpcFlowControlPolicy.java:75 } GrpcFlowControlPolicy.java:76 if (!transportReady || queuedMessages >= highWaterMarkMessages) { GrpcFlowControlPolicy.java:77 return new GrpcFlowControlDecision( GrpcFlowControlPolicy.java:78 GrpcFlowControlDecision.Action.PAUSE, GrpcFlowControlPolicy.java:79 queuedMessages, GrpcFlowControlPolicy.java:80 queuedBytes, GrpcFlowControlPolicy.java:81 transportReady GrpcFlowControlPolicy.java:82 ? "queue reached its high-water mark of " + highWaterMarkMessages GrpcFlowControlPolicy.java:83 : "the transport is not ready for more"); GrpcFlowControlPolicy.java:84 } GrpcFlowControlPolicy.java:85 return new GrpcFlowControlDecision( GrpcFlowControlPolicy.java:86 GrpcFlowControlDecision.Action.PROCEED, queuedMessages, queuedBytes, "within bounds"); GrpcFlowControlPolicy.java:87 } 바이트 경계를 두는 이유 : GrpcFlowControlPolicy.java:6 *

Both a message count and a byte count, because either alone is unbounded in the other GrpcFlowControlPolicy.java:7 * dimension: a thousand-message bound with no byte bound is a memory limit set by the largest GrpcFlowControlPolicy.java:8 * message anyone ever sends, and a byte bound with no count bound is unbounded queue overhead. # 이 타입을 참조하는 자리 — 경로 제한 없이 저장소 전체 $ git grep -n GrpcSerializedStreamWriter (pathspec 없음) docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3679:- Create: `modules/grpc/grpc-policy/src/main/jav docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3682:- Test: `modules/grpc/grpc-policy/src/test/java/ docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3698:class GrpcSerializedStreamWriterTest { docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3701: var writer = GrpcSerializedStreamWriter. docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3716:./gradlew :modules:grpc:grpc-policy:test --tests docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3728:public final class GrpcSerializedStreamWriter docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3731: private GrpcSerializedStreamWriter(int capac docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3736: public static GrpcSerializedStreamWriter docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3738: return new GrpcSerializedStreamWriter<>( docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3763:./gradlew :modules:grpc:grpc-policy:test --tests docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md:3772:git add 'modules/grpc/grpc-policy/src/main/java/ docs/adr/ADR-GRPC-005-server-streaming-single-writer-and-resume.md:20:**A bounded queue drained by one writer.** `GrpcSe main · GrpcSerializedStreamWriter.java:24 public final class GrpcSerializedStreamWriter { main · GrpcSerializedStreamWriter.java:43 public GrpcSerializedStreamWriter( test · GrpcSerializedStreamWriterTest.java:15 class GrpcSerializedStreamWriterTest { test · GrpcSerializedStreamWriterTest.java:22 private GrpcSerializedStreamWriter writer( test · GrpcSerializedStreamWriterTest.java:24 return new GrpcSerializedStreamWriter<>( test · GrpcSerializedStreamWriterTest.java:31 GrpcSerializedStreamWriter writer = writer(GrpcFlowControlP test · GrpcSerializedStreamWriterTest.java:47 GrpcSerializedStreamWriter writer = test · GrpcSerializedStreamWriterTest.java:87 GrpcSerializedStreamWriter writer = test · GrpcSerializedStreamWriterTest.java:103 GrpcSerializedStreamWriter writer = test · GrpcSerializedStreamWriterTest.java:116 GrpcSerializedStreamWriter writer = writer(GrpcFlowControl test · GrpcSerializedStreamWriterTest.java:129 GrpcSerializedStreamWriter writer = writer(GrpcFlowControl 자기 파일 밖 main 참조 : 0 개 파일 # 대응 시험이 크기 공급자에 주는 값 GrpcSerializedStreamWriterTest.java:22 private GrpcSerializedStreamWriter writer( GrpcSerializedStreamWriterTest.java:23 GrpcFlowControlPolicy policy, long messageSize) { GrpcSerializedStreamWriterTest.java:24 return new GrpcSerializedStreamWriter<>( GrpcSerializedStreamWriterTest.java:25 STREAM, GrpcStreamSequence.forNewStream(STREAM), policy, sent::add, () -> messageSize); GrpcSerializedStreamWriterTest.java:26 } GrpcSerializedStreamWriterTest.java:31 GrpcSerializedStreamWriter writer = writer(GrpcFlowControlPolicy.stable(), 16L); GrpcSerializedStreamWriterTest.java:49 new GrpcFlowControlPolicy(1000, 1_000_000L, 999, GrpcSlowConsumerPolicy.TERMINATE), 1L); GrpcSerializedStreamWriterTest.java:88 writer(new GrpcFlowControlPolicy(1, 1024L, 1, GrpcSlowConsumerPolicy.TERMINATE), 8L); GrpcSerializedStreamWriterTest.java:104 writer(new GrpcFlowControlPolicy(1, 1024L, 1, GrpcSlowConsumerPolicy.DROP_OLDEST), 8L); GrpcSerializedStreamWriterTest.java:116 GrpcSerializedStreamWriter writer = writer(GrpcFlowControlPolicy.stable(), 8L); GrpcSerializedStreamWriterTest.java:129 GrpcSerializedStreamWriter writer = writer(GrpcFlowControlPolicy.stable(), 8L); DROP_OLDEST 를 쓰는 시험이 단언하는 것 : GrpcSerializedStreamWriterTest.java:101 @DisplayName("a lossy profile drops the oldest and reports it rather than hiding it") GrpcSerializedStreamWriterTest.java:102 void aLossyProfileReportsWhatItDropped() { GrpcSerializedStreamWriterTest.java:103 GrpcSerializedStreamWriter writer = GrpcSerializedStreamWriterTest.java:104 writer(new GrpcFlowControlPolicy(1, 1024L, 1, GrpcSlowConsumerPolicy.DROP_OLDEST), 8L); GrpcSerializedStreamWriterTest.java:105 writer.write(GrpcStreamEnvelope.Kind.LIVE, "first", null, null, true); GrpcSerializedStreamWriterTest.java:106 GrpcSerializedStreamWriterTest.java:107 assertThat(writer.write(GrpcStreamEnvelope.Kind.LIVE, "second", null, null, true)) GrpcSerializedStreamWriterTest.java:108 .isEqualTo(GrpcStreamWriteResult.DROPPED); GrpcSerializedStreamWriterTest.java:109 assertThat(writer.droppedMessages()).isEqualTo(1); GrpcSerializedStreamWriterTest.java:110 assertThat(writer.queuedMessages()).isEqualTo(1); GrpcSerializedStreamWriterTest.java:111 } GrpcSerializedStreamWriterTest.java:112 # 프로브 — 두 방향과 TERMINATE 가능성 # 실제 큐는 writer 의 queue 필드를 읽어 봉투마다 원래 크기를 되찾아 합산한다 가. 작은 것을 버리며 큰 것을 넣는다 — 과소 계상 정책 : 메시지 상한 3 · 바이트 상한 1000000 · DROP_OLDEST n insize result writerBytes realQueue err 1 100 ACCEPTED 100 100 0 2 100 ACCEPTED 200 200 0 3 100 ACCEPTED 300 300 0 4 1000 DROPPED 1000 1200 -200 5 1000 DROPPED 1000 2100 -1100 6 1000 DROPPED 1000 3000 -2000 7 1000 DROPPED 1000 3000 -2000 8 1000 DROPPED 1000 3000 -2000 9 1000 DROPPED 1000 3000 -2000 남은 메시지 3 · 버린 수 6 · 상태 OPEN 나. 큰 것을 버리며 작은 것을 넣는다 — 과대 계상 정책 : 메시지 상한 3 · 바이트 상한 25000 · DROP_OLDEST n insize result writerBytes realQueue err 1 10000 ACCEPTED 10000 10000 0 2 10000 ACCEPTED 20000 20000 0 3 10 ACCEPTED 20010 20010 0 4 10 DROPPED 20010 10020 9990 5 10 DROPPED 20010 30 19980 6 10 DROPPED 20010 30 19980 7 5000 DROPPED 20010 5020 14990 8 10 DROPPED 20010 5020 14990 9 10 DROPPED 20010 5020 14990 남은 메시지 3 · 버린 수 6 · 상태 OPEN # DROP_OLDEST 를 쓰는 writer 가 TERMINATE 결정을 받을 수 있는가 decide 를 156282 조합으로 불렀을 때 TERMINATE 가 나온 횟수 : 0 그 정책 값으로 낼 수 있는 결정 : [DROP_OLDEST, PAUSE, PROCEED]