Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/a08-f005-transferbufferpool-maxborrowedbytes-peak.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

142 lines
9.4 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1228" viewBox="0 0 1200 1228" 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="1226" 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
D=$(mktemp -d)
trap &#x27;rm -rf &quot;$D&quot;&#x27; EXIT
mkdir -p &quot;$D/src/dev/caskeleton/adapter/outbound/fileserver/platform/loc</tspan><tspan x="24" dy="22">al&quot;
cat &gt; &quot;$D/src/dev/caskeleton/adapter/outbound/fileserver/platform/local/BorrowPeakProbe.java&quot; &lt;&lt;&#x27;JAVA&#x27;
package dev.</tspan><tspan x="24" dy="22">caskeleton.adapter.outbound.fileserver.platform.local;
import dev.caskeleton.application.fileserver.api.StorageNamespac</tspan><tspan x="24" dy="22">e;
import dev.caskeleton.application.fileserver.api.UploadId;
import dev.caskeleton.application.fileserver.api.content.A</tspan><tspan x="24" dy="22">ppendResult;
import dev.caskeleton.application.fileserver.api.content.WriteFence;
import java.io.IOException;
import jav</tspan><tspan x="24" dy="22">a.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
i</tspan><tspan x="24" dy="22">mport java.util.List;
import java.util.UUID;
/** Runs the same append the bounded-memory regression runs, at several pa</tspan><tspan x="24" dy="22">yload sizes. */
public final class BorrowPeakProbe {
private static final int BUFFER_BYTES = 128 * 1024;
public st</tspan><tspan x="24" dy="22">atic void main(String[] args) throws IOException {
Path root = Files.createTempDirectory(&quot;borrow-peak&quot;);
System.</tspan><tspan x="24" dy="22">out.println(&quot;BUFFER_BYTES = &quot; + BUFFER_BYTES);
System.out.println();
System.out.println(&quot;payload maxB</tspan><tspan x="24" dy="22">orrowedBytes BUFFER_BYTES 이하 버퍼 크기의 배수&quot;);
for (long payload : List.of(
1L * 1024 * 1024, 16L * </tspan><tspan x="24" dy="22">1024 * 1024, 256L * 1024 * 1024)) {
long peak = peakFor(root, payload);
System.out.printf(
&quot;%-18s </tspan><tspan x="24" dy="22">%-18d %-18s %d%n&quot;,
(payload / (1024 * 1024)) + &quot;MiB&quot;, peak,
peak &lt;= BUFFER_BYTES ? &quot;&quot; : &quot;아니오&quot;, </tspan><tspan x="24" dy="22">peak / BUFFER_BYTES);
}
System.out.println();
System.out.println(&quot;[대조] 반납 없이 두 번 빌리면 그 단언이 깨</tspan><tspan x="24" dy="22">진다&quot;);
TransferBufferPool leaking = new TransferBufferPool(BUFFER_BYTES, 4);
leaking.borrow();
leaking.borr</tspan><tspan x="24" dy="22">ow();
System.out.println(&quot; maxBorrowedBytes = &quot; + leaking.maxBorrowedBytes()
+ &quot; BUFFER_BYTES 이하 = &quot; + (</tspan><tspan x="24" dy="22">leaking.maxBorrowedBytes() &lt;= BUFFER_BYTES));
}
private static long peakFor(Path root, long payload) throws IOExcep</tspan><tspan x="24" dy="22">tion {
TransferBufferPool pool = new TransferBufferPool(BUFFER_BYTES, 4);
runAppend(root, pool, payload);
re</tspan><tspan x="24" dy="22">turn pool.maxBorrowedBytes();
}
private static void runAppend(Path root, TransferBufferPool pool, long payload)
</tspan><tspan x="24" dy="22"> throws IOException {
SafeFileChannelFactory channels = new SafeFileChannelFactory(true);
LocalAppendEngine en</tspan><tspan x="24" dy="22">gine = new LocalAppendEngine(channels, pool);
UploadId uploadId = UploadId.of(UUID.randomUUID());
Path staging =</tspan><tspan x="24" dy="22"> new DefaultPhysicalPathResolver(root).stagingPath(uploadId);
Files.createDirectories(staging.getParent());
File</tspan><tspan x="24" dy="22">s.createFile(staging);
LocalUploadHandle handle =
new LocalUploadHandle(uploadId, StorageNamespace.of(&quot;tenan</tspan><tspan x="24" dy="22">t-a&quot;), staging, Long.MAX_VALUE);
AppendResult result =
engine.append(root, handle, 0, new Generated(payload)</tspan><tspan x="24" dy="22">, payload, WriteFence.unfenced());
if (result.committedOffset() != payload) {
throw new IllegalStateException(</tspan><tspan x="24" dy="22">&quot;unexpected committed offset&quot;);
}
}
private static final class Generated implements ReadableByteChannel {
p</tspan><tspan x="24" dy="22">rivate long remaining;
private byte next;
private Generated(long total) {
this.remaining = total;
}
</tspan><tspan x="24" dy="22"> @Override
public int read(ByteBuffer destination) {
if (remaining &lt;= 0) {
return -1;
}
</tspan><tspan x="24" dy="22"> int count = (int) Math.min(destination.remaining(), remaining);
for (int index = 0; index &lt; count; index++) {
</tspan><tspan x="24" dy="22"> destination.put(next++);
}
remaining -= count;
return count;
}
@Override
public bool</tspan><tspan x="24" dy="22">ean isOpen() {
return true;
}
@Override
public void close() {}
}
}
JAVA
CACHE=/root/.gradle/caches</tspan><tspan x="24" dy="22">/modules-2/files-2.1
LOCK=src/adapter/outbound/fileserver/gradle.lockfile
CP=src/adapter/outbound/fileserver/build/class</tspan><tspan x="24" dy="22">es/java/main
CP=$CP:src/application-core/build/classes/java/main:src/domain-core/build/classes/java/main
while IFS= read</tspan><tspan x="24" dy="22"> -r line; do
coord=${line%%=*}; confs=${line#*=}
case &quot;,$confs,&quot; in *&quot;,testRuntimeClasspath,&quot;*) ;; *) continue ;; es</tspan><tspan x="24" dy="22">ac
g=${coord%%:*}; r=${coord#*:}; n=${r%%:*}; v=${r##*:}
jar=$(find &quot;$CACHE/$g/$n/$v&quot; -name &#x27;*.jar&#x27; ! -name &#x27;*source</tspan><tspan x="24" dy="22">s*&#x27; ! -name &#x27;*javadoc*&#x27; 2&gt;/dev/null | head -1)
[ -n &quot;$jar&quot; ] &amp;&amp; CP=&quot;$CP:$jar&quot;
done &lt; &lt;(grep -E &#x27;^[a-zA-Z0-9._-]+:[^=]+</tspan><tspan x="24" dy="22">=&#x27; &quot;$LOCK&quot;)
javac -encoding UTF-8 -cp &quot;$CP&quot; -d &quot;$D/out&quot; \
&quot;$D/src/dev/caskeleton/adapter/outbound/fileserver/platform</tspan><tspan x="24" dy="22">/local/BorrowPeakProbe.java&quot;
java -Dstdout.encoding=UTF-8 -cp &quot;$D/out:$CP&quot; \
dev.caskeleton.adapter.outbound.fileserve</tspan><tspan x="24" dy="22">r.platform.local.BorrowPeakProbe</tspan></text>
<text x="24" y="926" 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="948" 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-02T10:10:59+00:00 · exit 0</tspan></text>
<line x1="24" y1="962" x2="1176" y2="962" stroke="#30363d"/>
<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">BUFFER_BYTES = 131072</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"></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">payload maxBorrowedBytes BUFFER_BYTES 이하 버퍼 크기의 배수</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">1MiB 131072 예 1</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">16MiB 131072 예 1</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">256MiB 131072 예 1</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"></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">[대조] 반납 없이 두 번 빌리면 그 단언이 깨진다</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"> maxBorrowedBytes = 262144 BUFFER_BYTES 이하 = false</tspan></text>
</svg>