The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
255 lines
49 KiB
XML
255 lines
49 KiB
XML
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="4836" viewBox="0 0 1200 4836" role="img">
|
|
<title>terminal evidence</title>
|
|
<desc>Terminal-style rendering generated from retained command output. Sensitive-looking values are redacted in the visual asset.</desc>
|
|
<rect x="1" y="1" width="1198" height="4834" rx="14" fill="#0d1117" stroke="#30363d"/>
|
|
<rect x="1" y="1" width="1198" height="44" rx="14" fill="#161b22"/>
|
|
<rect x="1" y="30" width="1198" height="14" fill="#161b22"/>
|
|
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
|
|
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
|
|
<circle cx="64" cy="22" r="6" fill="#28c840"/>
|
|
<text x="92" y="27" fill="#8b949e" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">terminal evidence</text>
|
|
<text x="24" y="68" fill="#c9d1d9" font-size="15" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">$ #!/bin/bash
|
|
set -euo pipefail
|
|
cd /shared/codebase/clean-architecture-backend-template
|
|
R=src/grpc/grpc-policy/src/main/</tspan><tspan x="24" dy="22">java/dev/caskeleton/grpc/idempotency/GrpcOutcomeReplay.java
|
|
D=src/grpc-advanced/grpc-advanced-streaming/src/main/java/de</tspan><tspan x="24" dy="22">v/caskeleton/grpc/advanced/streaming/GrpcClientMessageDeduplicator.java
|
|
test -f "$R"; test -f "$D"
|
|
CPP=$(cat /tmp/cp-grp</tspan><tspan x="24" dy="22">c-policy.txt)
|
|
|
|
# 검색이 헛돌면 0 을 제조하지 않고 죽는다. rc 0=매치, 1=매치없음, 그 밖=오류.
|
|
refs() {
|
|
local name=$1 se</tspan><tspan x="24" dy="22">lf=$2 out rc
|
|
out=$(git grep -l "$name" -- 'src/**/*.java'); rc=$?
|
|
[ $rc -le 1 ] || { echo "SEARCH FAILED rc=$rc for </tspan><tspan x="24" dy="22">$name" >&2; exit 2; }
|
|
printf '%s\n' "$out" | { grep '/main/' || true; } | { grep -v "/$self" || true; } | grep -c . ||</tspan><tspan x="24" dy="22"> true
|
|
}
|
|
|
|
echo "# 저장소 전체 ($(wc -l < $R) 줄)"
|
|
awk '{printf " GrpcOutcomeReplay.java:%d %s\n", NR, $0}' $R
|
|
|
|
echo
|
|
echo</tspan><tspan x="24" dy="22"> "# 그 맵에 가해지는 연산 전부"
|
|
grep -n 'storedOutcomes' $R | sed -E 's#^([0-9]+): *# GrpcOutcomeReplay.java:\1 #'
|
|
e</tspan><tspan x="24" dy="22">cho " 클래스와 필드의 접근 한정 :"
|
|
grep -nE 'public final class|private final ConcurrentMap' $R | sed -E 's#^([0-9]+)</tspan><tspan x="24" dy="22">: *# GrpcOutcomeReplay.java:\1 #'
|
|
N='\.remove|\.clear|removeIf|computeIfPresent|\.merge|entrySet|keySet|evict|expire</tspan><tspan x="24" dy="22">|TTL|Duration|Instant|maximumSize|Caffeine|LinkedHashMap|WeakReference|SoftReference|Cleaner'
|
|
echo " 줄이는 연산 이름</tspan><tspan x="24" dy="22"> 열여덟을 통틀어 : $({ grep -cE "$N" $R || true; }) 건"
|
|
echo " 같은 검색을 대조 파일에 걸면 : $({ grep -cE "$N" $D</tspan><tspan x="24" dy="22"> || true; }) 건 (검색이 헛돌지 않음을 보인다)"
|
|
|
|
echo
|
|
echo "# 크기 상한이 실제로 거부하는가"
|
|
cd /tmp/probe5
|
|
javac -nowar</tspan><tspan x="24" dy="22">n -encoding UTF-8 -cp "$CPP" -d . OutcomeReplayProbe.java
|
|
java -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -cp ".:</tspan><tspan x="24" dy="22">$CPP" OutcomeReplayProbe
|
|
cd /shared/codebase/clean-architecture-backend-template
|
|
|
|
echo
|
|
echo "# 이 타입이 나오는 파일 전</tspan><tspan x="24" dy="22">부 — 경로 제한 없이, 절단 없이"
|
|
git grep -l 'GrpcOutcomeReplay' | sed 's/^/ /'
|
|
echo " 자기 파일 밖 main 참조 : $(refs</tspan><tspan x="24" dy="22"> GrpcOutcomeReplay GrpcOutcomeReplay.java) 개"
|
|
echo " [대조] 같은 패키지의 GrpcIdempotencyDecision : $(refs GrpcIdempot</tspan><tspan x="24" dy="22">encyDecision GrpcIdempotencyDecision.java) 개 (0 이 아니어야 정상)"
|
|
echo " git 이 추적하지 않는 변경 : $(git status --</tspan><tspan x="24" dy="22">porcelain | grep -c . || true) 건"
|
|
echo " .java 밖에서 이 이름을 쓰는 파일 : $({ git grep -l 'GrpcOutcomeReplay' -- ':!</tspan><tspan x="24" dy="22">*.java' || true; } | grep -c . || true) 개"
|
|
|
|
echo
|
|
echo "# 같은 패키지의 인터셉터는 재생을 어떻게 판정하는가"
|
|
I=src/grpc/</tspan><tspan x="24" dy="22">grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcIdempotencyInterceptor.java
|
|
awk 'NR>=95 && NR<=110 {printf</tspan><tspan x="24" dy="22"> " GrpcIdempotencyInterceptor.java:%d %s\n", NR, $0}' $I
|
|
echo " 그 파일이 GrpcOutcomeReplay 를 부르는가 : $({ grep -c </tspan><tspan x="24" dy="22">'GrpcOutcomeReplay' $I || true; }) 건"
|
|
|
|
echo
|
|
echo "# 자바독이 이 타입을 무엇이라고 적는가"
|
|
awk 'NR>=7 && NR<=16 {printf </tspan><tspan x="24" dy="22">" GrpcOutcomeReplay.java:%d %s\n", NR, $0}' $R
|
|
|
|
echo
|
|
echo "# 대조 클래스에 같은 잣대를 댄다 ($(wc -l < $D) 줄)"
|
|
echo " </tspan><tspan x="24" dy="22"> 이 파일 : $R"
|
|
echo " 대조 파일 : $D"
|
|
awk 'NR>=11 && NR<=14 {printf " GrpcClientMessageDeduplicator.java:%d %s\n", N</tspan><tspan x="24" dy="22">R, $0}' $D
|
|
awk 'NR>=16 && NR<=23 {printf " GrpcClientMessageDeduplicator.java:%d %s\n", NR, $0}' $D
|
|
echo " 적용된 메시</tspan><tspan x="24" dy="22">지마다 무엇을 넣는가 :"
|
|
awk 'NR>=70 && NR<=77 {printf " GrpcClientMessageDeduplicator.java:%d %s\n", NR, $0}' $D
|
|
echo "</tspan><tspan x="24" dy="22"> 정리 경로와 그 호출자 :"
|
|
git grep -n 'endSession' -- 'src/**/*.java' \
|
|
| sed -E 's#^src/[^:]*/src/(main|test)/java/d</tspan><tspan x="24" dy="22">ev/caskeleton/[^:]*/([^:/]+):([0-9]+):# \1 · \2:\3 #'
|
|
echo " 그 대조 클래스의 자기 파일 밖 main 참조 : $(refs Grp</tspan><tspan x="24" dy="22">cClientMessageDeduplicator GrpcClientMessageDeduplicator.java) 개"
|
|
|
|
echo
|
|
echo "# 두 클래스가 같은 family 인가"
|
|
echo " m</tspan><tspan x="24" dy="22">odules.json 의 family 키 : $({ grep -c 'family' src/config/architecture/modules.json || true; }) 건"
|
|
awk 'NR>=1 && NR<=3</tspan><tspan x="24" dy="22"> {printf " src/grpc/CLAUDE.md:%d %s\n", NR, $0}' src/grpc/CLAUDE.md
|
|
awk 'NR>=3 && NR<=9 {printf " src/grpc-advanced/CL</tspan><tspan x="24" dy="22">AUDE.md:%d %s\n", NR, $0}' src/grpc-advanced/CLAUDE.md
|
|
grep -nE 'grpc:\*.*→|Stable leaf' src/grpc-advanced/CLAUDE.md \
|
|
</tspan><tspan x="24" dy="22"> | sed -E 's#^([0-9]+): *# src/grpc-advanced/CLAUDE.md:\1 #'
|
|
</tspan></text>
|
|
<text x="24" y="816" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">cwd: /shared/codebase/clean-architecture-backend-template</tspan></text>
|
|
<text x="24" y="838" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">time: 2026-09-04T03:20:56+00:00 · exit 0</tspan></text>
|
|
<line x1="24" y1="852" x2="1176" y2="852" stroke="#30363d"/>
|
|
<text x="24" y="892" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 저장소 전체 (62 줄)</tspan></text>
|
|
<text x="24" y="914" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:1 package dev.caskeleton.grpc.idempotency;</tspan></text>
|
|
<text x="24" y="936" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:2 </tspan></text>
|
|
<text x="24" y="958" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:3 import java.util.Optional;</tspan></text>
|
|
<text x="24" y="980" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:4 import java.util.concurrent.ConcurrentHashMap;</tspan></text>
|
|
<text x="24" y="1002" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:5 import java.util.concurrent.ConcurrentMap;</tspan></text>
|
|
<text x="24" y="1024" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:6 </tspan></text>
|
|
<text x="24" y="1046" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:7 /**</tspan></text>
|
|
<text x="24" y="1068" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:8 * Returns the stored answer for an operation that already committed.</tspan></text>
|
|
<text x="24" y="1090" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:9 *</tspan></text>
|
|
<text x="24" y="1112" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:10 * <p>Separated from the ledger because the two have different size problems. The ledger row must</tspan></text>
|
|
<text x="24" y="1134" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:11 * stay small and is written inside the business transaction; a response may be large and is only</tspan></text>
|
|
<text x="24" y="1156" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:12 * needed if somebody actually retries. So the ledger stores a reference and this resolves it,</tspan></text>
|
|
<text x="24" y="1178" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:13 * against whatever the deployment chose — a small inline store, an object store, or a re-read of</tspan></text>
|
|
<text x="24" y="1200" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:14 * the committed resource.</tspan></text>
|
|
<text x="24" y="1222" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:15 */</tspan></text>
|
|
<text x="24" y="1244" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:16 public final class GrpcOutcomeReplay {</tspan></text>
|
|
<text x="24" y="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:17 </tspan></text>
|
|
<text x="24" y="1288" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:18 private final ConcurrentMap<String, byte[]> storedOutcomes = new ConcurrentHashMap<>();</tspan></text>
|
|
<text x="24" y="1310" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:19 private final int maxInlineBytes;</tspan></text>
|
|
<text x="24" y="1332" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:20 </tspan></text>
|
|
<text x="24" y="1354" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:21 /**</tspan></text>
|
|
<text x="24" y="1376" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:22 * @param maxInlineBytes the largest response stored inline; anything larger must be kept behind a</tspan></text>
|
|
<text x="24" y="1398" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:23 * reference in an object store instead, which is why {@link #store} refuses it rather than</tspan></text>
|
|
<text x="24" y="1420" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:24 * silently truncating</tspan></text>
|
|
<text x="24" y="1442" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:25 */</tspan></text>
|
|
<text x="24" y="1464" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:26 public GrpcOutcomeReplay(int maxInlineBytes) {</tspan></text>
|
|
<text x="24" y="1486" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:27 if (maxInlineBytes < 1) {</tspan></text>
|
|
<text x="24" y="1508" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:28 throw new IllegalArgumentException("an inline outcome store needs a positive size limit");</tspan></text>
|
|
<text x="24" y="1530" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:29 }</tspan></text>
|
|
<text x="24" y="1552" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:30 this.maxInlineBytes = maxInlineBytes;</tspan></text>
|
|
<text x="24" y="1574" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:31 }</tspan></text>
|
|
<text x="24" y="1596" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:32 </tspan></text>
|
|
<text x="24" y="1618" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:33 /** Stores a response under {@code outcomeReference}. */</tspan></text>
|
|
<text x="24" y="1640" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:34 public void store(String outcomeReference, byte[] serializedResponse) {</tspan></text>
|
|
<text x="24" y="1662" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:35 if (outcomeReference == null || outcomeReference.isBlank()) {</tspan></text>
|
|
<text x="24" y="1684" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:36 throw new IllegalArgumentException("an outcome needs a reference to be stored under");</tspan></text>
|
|
<text x="24" y="1706" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:37 }</tspan></text>
|
|
<text x="24" y="1728" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:38 if (serializedResponse == null) {</tspan></text>
|
|
<text x="24" y="1750" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:39 throw new IllegalArgumentException("an outcome needs a serialized response");</tspan></text>
|
|
<text x="24" y="1772" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:40 }</tspan></text>
|
|
<text x="24" y="1794" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:41 if (serializedResponse.length > maxInlineBytes) {</tspan></text>
|
|
<text x="24" y="1816" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:42 throw new IllegalArgumentException(</tspan></text>
|
|
<text x="24" y="1838" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:43 "response of "</tspan></text>
|
|
<text x="24" y="1860" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:44 + serializedResponse.length</tspan></text>
|
|
<text x="24" y="1882" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:45 + " bytes exceeds the inline outcome limit of "</tspan></text>
|
|
<text x="24" y="1904" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:46 + maxInlineBytes</tspan></text>
|
|
<text x="24" y="1926" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:47 + "; store it behind an object reference instead");</tspan></text>
|
|
<text x="24" y="1948" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:48 }</tspan></text>
|
|
<text x="24" y="1970" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:49 storedOutcomes.put(outcomeReference, serializedResponse.clone());</tspan></text>
|
|
<text x="24" y="1992" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:50 }</tspan></text>
|
|
<text x="24" y="2014" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:51 </tspan></text>
|
|
<text x="24" y="2036" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:52 /** The stored response, if there is one. */</tspan></text>
|
|
<text x="24" y="2058" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:53 public Optional<byte[]> replay(String outcomeReference) {</tspan></text>
|
|
<text x="24" y="2080" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:54 byte[] stored = storedOutcomes.get(outcomeReference);</tspan></text>
|
|
<text x="24" y="2102" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:55 return Optional.ofNullable(stored).map(byte[]::clone);</tspan></text>
|
|
<text x="24" y="2124" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:56 }</tspan></text>
|
|
<text x="24" y="2146" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:57 </tspan></text>
|
|
<text x="24" y="2168" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:58 /** How many outcomes are held. */</tspan></text>
|
|
<text x="24" y="2190" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:59 public int size() {</tspan></text>
|
|
<text x="24" y="2212" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:60 return storedOutcomes.size();</tspan></text>
|
|
<text x="24" y="2234" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:61 }</tspan></text>
|
|
<text x="24" y="2256" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:62 }</tspan></text>
|
|
<text x="24" y="2278" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="2300" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 맵에 가해지는 연산 전부</tspan></text>
|
|
<text x="24" y="2322" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:18 private final ConcurrentMap<String, byte[]> storedOutcomes = new ConcurrentHashMap<>();</tspan></text>
|
|
<text x="24" y="2344" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:49 storedOutcomes.put(outcomeReference, serializedResponse.clone());</tspan></text>
|
|
<text x="24" y="2366" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:54 byte[] stored = storedOutcomes.get(outcomeReference);</tspan></text>
|
|
<text x="24" y="2388" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:60 return storedOutcomes.size();</tspan></text>
|
|
<text x="24" y="2410" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 클래스와 필드의 접근 한정 :</tspan></text>
|
|
<text x="24" y="2432" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:16 public final class GrpcOutcomeReplay {</tspan></text>
|
|
<text x="24" y="2454" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:18 private final ConcurrentMap<String, byte[]> storedOutcomes = new ConcurrentHashMap<>();</tspan></text>
|
|
<text x="24" y="2476" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 줄이는 연산 이름 열여덟을 통틀어 : 0 건</tspan></text>
|
|
<text x="24" y="2498" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 같은 검색을 대조 파일에 걸면 : 5 건 (검색이 헛돌지 않음을 보인다)</tspan></text>
|
|
<text x="24" y="2520" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="2542" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 크기 상한이 실제로 거부하는가</tspan></text>
|
|
<text x="24" y="2564" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 16 바이트 저장 후 size : 1</tspan></text>
|
|
<text x="24" y="2586" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 17 바이트 저장 : response of 17 bytes exceeds the inline outcome limit of 16; store it behind an object reference instead</tspan></text>
|
|
<text x="24" y="2608" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 거부 후 size : 1</tspan></text>
|
|
<text x="24" y="2630" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 서로 다른 키 1000 개 저장 후 size : 1001</tspan></text>
|
|
<text x="24" y="2652" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 1000 개를 모두 replay 한 뒤 : 1001</tspan></text>
|
|
<text x="24" y="2674" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 같은 키로 1000 번 덮어쓴 뒤 : 1001</tspan></text>
|
|
<text x="24" y="2696" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="2718" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 이 타입이 나오는 파일 전부 — 경로 제한 없이, 절단 없이</tspan></text>
|
|
<text x="24" y="2740" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> docs/2026-08-13-grpc-type-safe-rpc-platform-implementation-plan.md</tspan></text>
|
|
<text x="24" y="2762" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcOutcomeReplay.java</tspan></text>
|
|
<text x="24" y="2784" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc/grpc-policy/src/test/java/dev/caskeleton/grpc/idempotency/GrpcIdempotencyInterceptorTest.java</tspan></text>
|
|
<text x="24" y="2806" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 자기 파일 밖 main 참조 : 0 개</tspan></text>
|
|
<text x="24" y="2828" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> [대조] 같은 패키지의 GrpcIdempotencyDecision : 1 개 (0 이 아니어야 정상)</tspan></text>
|
|
<text x="24" y="2850" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> git 이 추적하지 않는 변경 : 0 건</tspan></text>
|
|
<text x="24" y="2872" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> .java 밖에서 이 이름을 쓰는 파일 : 1 개</tspan></text>
|
|
<text x="24" y="2894" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="2916" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 같은 패키지의 인터셉터는 재생을 어떻게 판정하는가</tspan></text>
|
|
<text x="24" y="2938" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:95 if (!record.sameRequestAs(requestFingerprint)) {</tspan></text>
|
|
<text x="24" y="2960" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:96 return GrpcIdempotencyDecision.fingerprintMismatch();</tspan></text>
|
|
<text x="24" y="2982" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:97 }</tspan></text>
|
|
<text x="24" y="3004" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:98 if (record.state() == GrpcOperationLedgerState.COMMITTED) {</tspan></text>
|
|
<text x="24" y="3026" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:99 return GrpcIdempotencyDecision.replay(</tspan></text>
|
|
<text x="24" y="3048" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:100 record</tspan></text>
|
|
<text x="24" y="3070" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:101 .outcomeReference()</tspan></text>
|
|
<text x="24" y="3092" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:102 .orElseThrow(</tspan></text>
|
|
<text x="24" y="3114" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:103 () -></tspan></text>
|
|
<text x="24" y="3136" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:104 new IllegalStateException(</tspan></text>
|
|
<text x="24" y="3158" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:105 "COMMITTED ledger record for '"</tspan></text>
|
|
<text x="24" y="3180" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:106 + identity.storageKey()</tspan></text>
|
|
<text x="24" y="3202" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:107 + "' has no outcome reference to replay")));</tspan></text>
|
|
<text x="24" y="3224" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:108 }</tspan></text>
|
|
<text x="24" y="3246" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:109 if (record.state() == GrpcOperationLedgerState.FAILED_TERMINAL) {</tspan></text>
|
|
<text x="24" y="3268" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcIdempotencyInterceptor.java:110 // A terminal failure is not replayed: the caller asked for the operation, it did no</tspan><tspan x="24" dy="22">t happen,</tspan></text>
|
|
<text x="24" y="3312" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 파일이 GrpcOutcomeReplay 를 부르는가 : 0 건</tspan></text>
|
|
<text x="24" y="3334" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="3356" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 자바독이 이 타입을 무엇이라고 적는가</tspan></text>
|
|
<text x="24" y="3378" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:7 /**</tspan></text>
|
|
<text x="24" y="3400" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:8 * Returns the stored answer for an operation that already committed.</tspan></text>
|
|
<text x="24" y="3422" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:9 *</tspan></text>
|
|
<text x="24" y="3444" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:10 * <p>Separated from the ledger because the two have different size problems. The ledger row must</tspan></text>
|
|
<text x="24" y="3466" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:11 * stay small and is written inside the business transaction; a response may be large and is only</tspan></text>
|
|
<text x="24" y="3488" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:12 * needed if somebody actually retries. So the ledger stores a reference and this resolves it,</tspan></text>
|
|
<text x="24" y="3510" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:13 * against whatever the deployment chose — a small inline store, an object store, or a re-read of</tspan></text>
|
|
<text x="24" y="3532" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:14 * the committed resource.</tspan></text>
|
|
<text x="24" y="3554" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:15 */</tspan></text>
|
|
<text x="24" y="3576" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcOutcomeReplay.java:16 public final class GrpcOutcomeReplay {</tspan></text>
|
|
<text x="24" y="3598" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="3620" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 대조 클래스에 같은 잣대를 댄다 (123 줄)</tspan></text>
|
|
<text x="24" y="3642" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 이 파일 : src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/idempotency/GrpcOutcomeReplay.java</tspan></text>
|
|
<text x="24" y="3664" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 대조 파일 : src/grpc-advanced/grpc-advanced-streaming/src/main/java/dev/caskeleton/grpc/advanced/streaming/GrpcClientMessageDe</tspan><tspan x="24" dy="22">duplicator.java</tspan></text>
|
|
<text x="24" y="3708" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:11 * <p>Checkpoint-based rather than a set of seen keys. A set grows without bound for the</tspan><tspan x="24" dy="22"> life of a</tspan></text>
|
|
<text x="24" y="3752" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:12 * session and answers "have I seen this" — which is not quite the question. The questi</tspan><tspan x="24" dy="22">on is "has</tspan></text>
|
|
<text x="24" y="3796" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:13 * this been applied", and a monotonic applied-sequence answers it in constant space and</tspan><tspan x="24" dy="22"> survives</tspan></text>
|
|
<text x="24" y="3840" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:14 * the process restart that a set does not.</tspan></text>
|
|
<text x="24" y="3862" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:16 * <p>Replayed outcomes are kept for the small window after the checkpoint, so a duplica</tspan><tspan x="24" dy="22">te that</tspan></text>
|
|
<text x="24" y="3906" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:17 * arrives before the checkpoint advances gets the original answer rather than being rea</tspan><tspan x="24" dy="22">pplied.</tspan></text>
|
|
<text x="24" y="3950" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:18 */</tspan></text>
|
|
<text x="24" y="3972" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:19 public final class GrpcClientMessageDeduplicator {</tspan></text>
|
|
<text x="24" y="3994" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:20 </tspan></text>
|
|
<text x="24" y="4016" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:21 private final ConcurrentMap<String, GrpcClientStreamCheckpoint> checkpoints =</tspan></text>
|
|
<text x="24" y="4038" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:22 new ConcurrentHashMap<>();</tspan></text>
|
|
<text x="24" y="4060" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:23 private final ConcurrentMap<String, String> replayableOutcomes = new ConcurrentHashMap</tspan><tspan x="24" dy="22"><>();</tspan></text>
|
|
<text x="24" y="4104" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 적용된 메시지마다 무엇을 넣는가 :</tspan></text>
|
|
<text x="24" y="4126" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:70 public void recordApplied(</tspan></text>
|
|
<text x="24" y="4148" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:71 GrpcClientStreamMessage<?> message, String outcomeReference, Instant at) {</tspan></text>
|
|
<text x="24" y="4170" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:72 GrpcClientStreamCheckpoint checkpoint = requireCheckpoint(message.sessionId());</tspan></text>
|
|
<text x="24" y="4192" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:73 checkpoints.put(message.sessionId().value(), checkpoint.advancedTo(message.sequence(</tspan><tspan x="24" dy="22">), at));</tspan></text>
|
|
<text x="24" y="4236" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:74 if (outcomeReference != null && !outcomeReference.isBlank()) {</tspan></text>
|
|
<text x="24" y="4258" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:75 replayableOutcomes.put(message.dedupKey(), outcomeReference);</tspan></text>
|
|
<text x="24" y="4280" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:76 }</tspan></text>
|
|
<text x="24" y="4302" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> GrpcClientMessageDeduplicator.java:77 }</tspan></text>
|
|
<text x="24" y="4324" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 정리 경로와 그 호출자 :</tspan></text>
|
|
<text x="24" y="4346" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> main · GrpcClientMessageDeduplicator.java:110 public void endSession(GrpcClientStreamSessionId sessionId) {</tspan></text>
|
|
<text x="24" y="4368" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> test · GrpcClientMessageDeduplicatorTest.java:117 deduplicator.endSession(session);</tspan></text>
|
|
<text x="24" y="4390" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 대조 클래스의 자기 파일 밖 main 참조 : 0 개</tspan></text>
|
|
<text x="24" y="4412" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
|
|
<text x="24" y="4434" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 두 클래스가 같은 family 인가</tspan></text>
|
|
<text x="24" y="4456" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> modules.json 의 family 키 : 0 건</tspan></text>
|
|
<text x="24" y="4478" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc/CLAUDE.md:1 # grpc — local authority for the gRPC platform family</tspan></text>
|
|
<text x="24" y="4500" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc/CLAUDE.md:2 </tspan></text>
|
|
<text x="24" y="4522" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc/CLAUDE.md:3 이 문서는 `grpc:*` family의 **local authority**다. leaf 목록·gradle path·허용 의존성은</tspan></text>
|
|
<text x="24" y="4544" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:3 이 문서는 `grpc-advanced:*` family의 **local authority**다. leaf 목록·gradle path·허용 의존성</tspan><tspan x="24" dy="22">은</tspan></text>
|
|
<text x="24" y="4588" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:4 `src/config/architecture/modules.json`이 SSOT다. Root 정책(`CLAUDE.md` / `AGENTS.md`)과 충돌하면</tspan></text>
|
|
<text x="24" y="4610" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:5 root가 이긴다.</tspan></text>
|
|
<text x="24" y="4632" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:6 </tspan></text>
|
|
<text x="24" y="4654" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:7 이 family는 Stable gRPC 플랫폼(`grpc:*`)이 **의도적으로 제외한** 능력들을 담는다. 별도 디렉터리</tspan><tspan x="24" dy="22">와</tspan></text>
|
|
<text x="24" y="4698" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:8 별도 Gradle prefix인 이유는 하나다: "Stable starter가 advanced module을 참조하면 build가 실패한</tspan><tspan x="24" dy="22">다"는</tspan></text>
|
|
<text x="24" y="4742" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:9 불변 조건을 registry의 `allowed_dependencies`만으로 기계 검증할 수 있게 하기 위해서다.</tspan></text>
|
|
<text x="24" y="4764" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:15 grpc:* → grpc-advanced:* (금지 — registry가 거부한다)</tspan></text>
|
|
<text x="24" y="4786" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> src/grpc-advanced/CLAUDE.md:74 - Stable leaf(`grpc:*`)가 이 family를 참조하는 것.</tspan></text>
|
|
</svg>
|