{ "assetKey": "a08-f005-transferbufferpool-maxborrowedbytes-peak", "kind": "terminal", "command": "set -e\nD=$(mktemp -d)\ntrap 'rm -rf \"$D\"' EXIT\nmkdir -p \"$D/src/dev/caskeleton/adapter/outbound/fileserver/platform/local\"\n\ncat > \"$D/src/dev/caskeleton/adapter/outbound/fileserver/platform/local/BorrowPeakProbe.java\" <<'JAVA'\npackage dev.caskeleton.adapter.outbound.fileserver.platform.local;\n\nimport dev.caskeleton.application.fileserver.api.StorageNamespace;\nimport dev.caskeleton.application.fileserver.api.UploadId;\nimport dev.caskeleton.application.fileserver.api.content.AppendResult;\nimport dev.caskeleton.application.fileserver.api.content.WriteFence;\nimport java.io.IOException;\nimport java.nio.ByteBuffer;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.List;\nimport java.util.UUID;\n\n/** Runs the same append the bounded-memory regression runs, at several payload sizes. */\npublic final class BorrowPeakProbe {\n\n private static final int BUFFER_BYTES = 128 * 1024;\n\n public static void main(String[] args) throws IOException {\n Path root = Files.createTempDirectory(\"borrow-peak\");\n System.out.println(\"BUFFER_BYTES = \" + BUFFER_BYTES);\n System.out.println();\n System.out.println(\"payload maxBorrowedBytes BUFFER_BYTES 이하 버퍼 크기의 배수\");\n for (long payload : List.of(\n 1L * 1024 * 1024, 16L * 1024 * 1024, 256L * 1024 * 1024)) {\n long peak = peakFor(root, payload);\n System.out.printf(\n \"%-18s %-18d %-18s %d%n\",\n (payload / (1024 * 1024)) + \"MiB\", peak,\n peak <= BUFFER_BYTES ? \"예\" : \"아니오\", peak / BUFFER_BYTES);\n }\n System.out.println();\n System.out.println(\"[대조] 반납 없이 두 번 빌리면 그 단언이 깨진다\");\n TransferBufferPool leaking = new TransferBufferPool(BUFFER_BYTES, 4);\n leaking.borrow();\n leaking.borrow();\n System.out.println(\" maxBorrowedBytes = \" + leaking.maxBorrowedBytes()\n + \" BUFFER_BYTES 이하 = \" + (leaking.maxBorrowedBytes() <= BUFFER_BYTES));\n }\n\n private static long peakFor(Path root, long payload) throws IOException {\n TransferBufferPool pool = new TransferBufferPool(BUFFER_BYTES, 4);\n runAppend(root, pool, payload);\n return pool.maxBorrowedBytes();\n }\n\n private static void runAppend(Path root, TransferBufferPool pool, long payload)\n throws IOException {\n SafeFileChannelFactory channels = new SafeFileChannelFactory(true);\n LocalAppendEngine engine = new LocalAppendEngine(channels, pool);\n UploadId uploadId = UploadId.of(UUID.randomUUID());\n Path staging = new DefaultPhysicalPathResolver(root).stagingPath(uploadId);\n Files.createDirectories(staging.getParent());\n Files.createFile(staging);\n LocalUploadHandle handle =\n new LocalUploadHandle(uploadId, StorageNamespace.of(\"tenant-a\"), staging, Long.MAX_VALUE);\n AppendResult result =\n engine.append(root, handle, 0, new Generated(payload), payload, WriteFence.unfenced());\n if (result.committedOffset() != payload) {\n throw new IllegalStateException(\"unexpected committed offset\");\n }\n }\n\n private static final class Generated implements ReadableByteChannel {\n private long remaining;\n private byte next;\n\n private Generated(long total) {\n this.remaining = total;\n }\n\n @Override\n public int read(ByteBuffer destination) {\n if (remaining <= 0) {\n return -1;\n }\n int count = (int) Math.min(destination.remaining(), remaining);\n for (int index = 0; index < count; index++) {\n destination.put(next++);\n }\n remaining -= count;\n return count;\n }\n\n @Override\n public boolean isOpen() {\n return true;\n }\n\n @Override\n public void close() {}\n }\n}\nJAVA\n\nCACHE=/root/.gradle/caches/modules-2/files-2.1\nLOCK=src/adapter/outbound/fileserver/gradle.lockfile\nCP=src/adapter/outbound/fileserver/build/classes/java/main\nCP=$CP:src/application-core/build/classes/java/main:src/domain-core/build/classes/java/main\nwhile IFS= read -r line; do\n coord=${line%%=*}; confs=${line#*=}\n case \",$confs,\" in *\",testRuntimeClasspath,\"*) ;; *) continue ;; esac\n g=${coord%%:*}; r=${coord#*:}; n=${r%%:*}; v=${r##*:}\n jar=$(find \"$CACHE/$g/$n/$v\" -name '*.jar' ! -name '*sources*' ! -name '*javadoc*' 2>/dev/null | head -1)\n [ -n \"$jar\" ] && CP=\"$CP:$jar\"\ndone < <(grep -E '^[a-zA-Z0-9._-]+:[^=]+=' \"$LOCK\")\n\njavac -encoding UTF-8 -cp \"$CP\" -d \"$D/out\" \\\n \"$D/src/dev/caskeleton/adapter/outbound/fileserver/platform/local/BorrowPeakProbe.java\"\njava -Dstdout.encoding=UTF-8 -cp \"$D/out:$CP\" \\\n dev.caskeleton.adapter.outbound.fileserver.platform.local.BorrowPeakProbe", "cwd": "/shared/codebase/clean-architecture-backend-template", "exitCode": 0, "executedAt": "2026-09-02T10:10:59+00:00", "sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916", "raw": "evidence/raw/a08-f005-transferbufferpool-maxborrowedbytes-peak.txt", "svg": "evidence/rendered/a08-f005-transferbufferpool-maxborrowedbytes-peak.svg", "rawSha256": "e460836ec24222bfa403400469d1c1fbd5e5be9ab00d9035910ce998798443bd", "lines": 9, "redaction": "none — 밀폐 탐침 실행 출력, 자격증명 없음" }