Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/a14-f011-maxarrayelements-parser.svg
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
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>
2026-09-04 22:51:59 +09:00

134 lines
12 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1492" viewBox="0 0 1200 1492" 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="1490" 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">$ set -e
set -o pipefail
D=$(mktemp -d); trap &#x27;rm -rf &quot;$D&quot;&#x27; EXIT
C=/shared/codebase/clean-architecture-backend-template
</tspan><tspan x="24" dy="22">M=$C/src/adapter/inbound/web
CACHE=/root/.gradle/caches/modules-2/files-2.1
java -version 2&gt;&amp;1 | grep version
cp=&quot;&quot;
whil</tspan><tspan x="24" dy="22">e IFS= read -r line; do
coord=${line%%=*}; confs=${line#*=}
case &quot;,$confs,&quot; in *&quot;,testRuntimeClasspath,&quot;*) ;; *) con</tspan><tspan x="24" dy="22">tinue ;; esac
g=${coord%%:*}; rest=${coord#*:}; n=${rest%%:*}; v=${rest##*:}
jar=$(find &quot;$CACHE/$g/$n/$v&quot; -name &#x27;*.j</tspan><tspan x="24" dy="22">ar&#x27; ! -name &#x27;*sources*&#x27; ! -name &#x27;*javadoc*&#x27; 2&gt;/dev/null | head -1) || true
[ -n &quot;$jar&quot; ] &amp;&amp; cp=&quot;$cp:$jar&quot;
done &lt; &lt;(gre</tspan><tspan x="24" dy="22">p -E &#x27;^[a-zA-Z0-9._-]+:[^=]+=&#x27; &quot;$M/gradle.lockfile&quot;)
SIB=$(find &quot;$C/src&quot; -path &#x27;*/build/libs/*+21234e38cdb9*.jar&#x27; ! -nam</tspan><tspan x="24" dy="22">e &#x27;*-testkit.jar&#x27; | sort | tr &#x27;\n&#x27; &#x27;:&#x27;)
cat &gt; &quot;$D/ArrayBudgetProbe.java&quot; &lt;&lt;&#x27;JAVA&#x27;
import dev.caskeleton.adapter.inbound.</tspan><tspan x="24" dy="22">web.json.BoundedJsonFactory;
import dev.caskeleton.adapter.inbound.web.json.WebJsonProfile;
import tools.jackson.core.Js</tspan><tspan x="24" dy="22">onParser;
import tools.jackson.core.JsonToken;
import tools.jackson.core.StreamReadConstraints;
import tools.jackson.cor</tspan><tspan x="24" dy="22">e.json.JsonFactory;
/** 이 저장소가 세운 파서가 넓은 배열을 어디까지 받는지, 파서가 줄 수 있는 상한이 무엇인지 본다. */</tspan><tspan x="24" dy="22">
public final class ArrayBudgetProbe {
/** 원소 n 개짜리 평평한 정수 배열. 깊이는 1 이고 문자열은 없다. */
private </tspan><tspan x="24" dy="22">static String flatArray(int n) {
StringBuilder out = new StringBuilder(n * 2 + 2).append(&#x27;[&#x27;);
for (int i = 0; i</tspan><tspan x="24" dy="22"> &lt; n; i++) {
if (i &gt; 0) {
out.append(&#x27;,&#x27;);
}
out.append(&#x27;0&#x27;);
}
return out.append(&#x27;]&#x27;)</tspan><tspan x="24" dy="22">.toString();
}
private static long readAll(JsonFactory factory, String json) {
long tokens = 0;
try (JsonPa</tspan><tspan x="24" dy="22">rser parser = factory.createParser(tools.jackson.core.ObjectReadContext.empty(), json)) {
while (parser.nextToken(</tspan><tspan x="24" dy="22">) != null) {
tokens++;
}
}
return tokens;
}
public static void main(String[] args) {
WebJ</tspan><tspan x="24" dy="22">sonProfile strict = WebJsonProfile.strict();
System.out.println();
System.out.println(&quot;[엄격 프로파일이 선언한 </tspan><tspan x="24" dy="22">세 상한]&quot;);
System.out.printf(&quot; %-22s %d%n&quot;, &quot;maxDepth&quot;, strict.maxDepth());
System.out.printf(&quot; %-22s %d%n&quot;, </tspan><tspan x="24" dy="22">&quot;maxArrayElements&quot;, strict.maxArrayElements());
System.out.printf(&quot; %-22s %d%n&quot;, &quot;maxStringBytes&quot;, strict.maxString</tspan><tspan x="24" dy="22">Bytes());
JsonFactory factory = BoundedJsonFactory.create(strict);
StreamReadConstraints set = factory.streamRe</tspan><tspan x="24" dy="22">adConstraints();
System.out.println();
System.out.println(&quot;[유계 팩토리가 파서에 실제로 건 값]&quot;);
System.out</tspan><tspan x="24" dy="22">.printf(&quot; %-22s %d%n&quot;, &quot;maxNestingDepth&quot;, set.getMaxNestingDepth());
System.out.printf(&quot; %-22s %d%n&quot;, &quot;maxStringLe</tspan><tspan x="24" dy="22">ngth&quot;, set.getMaxStringLength());
System.out.printf(&quot; %-22s %d%n&quot;, &quot;maxNameLength&quot;, set.getMaxNameLength());
Sy</tspan><tspan x="24" dy="22">stem.out.printf(&quot; %-22s %d%n&quot;, &quot;maxNumberLength&quot;, set.getMaxNumberLength());
System.out.printf(&quot; %-22s %s%n&quot;, &quot;max</tspan><tspan x="24" dy="22">DocumentLength&quot;,
set.hasMaxDocumentLength() ? String.valueOf(set.getMaxDocumentLength()) : &quot;걸지 않음&quot;);
Sys</tspan><tspan x="24" dy="22">tem.out.printf(&quot; %-22s %s%n&quot;, &quot;maxTokenCount&quot;,
set.hasMaxTokenCount() ? String.valueOf(set.getMaxTokenCount()) </tspan><tspan x="24" dy="22">: &quot;걸지 않음&quot;);
int elements = 2_000_000;
System.out.println();
System.out.printf(&quot;[원소 %,d 개짜리 평평한 </tspan><tspan x="24" dy="22">배열을 그 팩토리에 넣는다]%n&quot;, elements);
String json = flatArray(elements);
System.out.printf(&quot; %-22s %,d%n&quot;, </tspan><tspan x="24" dy="22">&quot;본문 바이트&quot;, json.length());
System.out.printf(&quot; %-22s %,d%n&quot;, &quot;읽어 낸 토큰 수&quot;, readAll(factory, json));
Sy</tspan><tspan x="24" dy="22">stem.out.println(&quot; 결과 끝까지 읽음&quot;);
System.out.println();
System.out.println(&quot;[같은 본</tspan><tspan x="24" dy="22">문을 토큰 수 상한만 건 파서에 넣는다]&quot;);
JsonFactory bounded = JsonFactory.builder()
.streamReadConstraints(</tspan><tspan x="24" dy="22">StreamReadConstraints.builder().maxTokenCount(1_000_000).build())
.build();
try {
readAll(bounded, jso</tspan><tspan x="24" dy="22">n);
System.out.println(&quot; 결과 끝까지 읽음&quot;);
} catch (RuntimeException failure) {
S</tspan><tspan x="24" dy="22">tring message = failure.getMessage();
int cut = message.indexOf(&#x27;\n&#x27;);
System.out.println(&quot; 결과 </tspan><tspan x="24" dy="22"> &quot; + (cut &lt; 0 ? message : message.substring(0, cut)));
}
}
}
JAVA
javac -encoding UTF-8 -cp &quot;$SIB$cp&quot; -d </tspan><tspan x="24" dy="22">&quot;$D/out&quot; &quot;$D/ArrayBudgetProbe.java&quot; 2&gt;&amp;1 | grep -v &#x27;^$&#x27; || true
java -Xmx1g -Dstdout.encoding=UTF-8 -cp &quot;$D/out:$SIB$cp&quot;</tspan><tspan x="24" dy="22"> ArrayBudgetProbe
</tspan></text>
<text x="24" y="882" 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/src</tspan></text>
<text x="24" y="904" 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-03T01:52:46+00:00 · exit 0</tspan></text>
<line x1="24" y1="918" x2="1176" y2="918" stroke="#30363d"/>
<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">openjdk version &quot;21.0.12&quot; 2026-07-21</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"></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">[엄격 프로파일이 선언한 세 상한]</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"> maxDepth 64</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"> maxArrayElements 100000</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"> maxStringBytes 1048576</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"></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">[유계 팩토리가 파서에 실제로 건 값]</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"> maxNestingDepth 64</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"> maxStringLength 1048576</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"> maxNameLength 50000</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"> maxNumberLength 1000</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"> maxDocumentLength 걸지 않음</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"> maxTokenCount 걸지 않음</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"></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">[원소 2,000,000 개짜리 평평한 배열을 그 팩토리에 넣는다]</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"> 본문 바이트 4,000,001</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"> 읽어 낸 토큰 수 2,000,002</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"> 결과 끝까지 읽음</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"></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">[같은 본문을 토큰 수 상한만 건 파서에 넣는다]</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"> 결과 Token count (1000001) exceeds the maximum allowed (1000000, from `StreamReadConstraints.getMaxTokenCo</tspan><tspan x="24" dy="22">unt()`)</tspan></text>
</svg>