{ "assetKey": "a10-f004-pub-sub-bound", "kind": "terminal", "command": "set -e\nD=$(mktemp -d); trap 'rm -rf \"$D\"' EXIT\nJAR=/shared/codebase/clean-architecture-backend-template/src/adapter/outbound/cache-redis/build/libs/cache-redis-0.0.1+21234e38cdb9.jar\ncat > \"$D/PubSubBoundProbe.java\" <<'JAVA'\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.codec.RedisCodec;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.QualifiedRedisKey;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.RedisKeyName;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.RedisKeyRenderer;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.RedisKeyRules;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.RedisNamespace;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.key.RedisSlotTag;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.operations.PubSubChannel;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.operations.PubSubPattern;\nimport dev.caskeleton.adapter.outbound.cache.redis.sdk.api.operations.ShardedPubSubChannel;\nimport java.nio.charset.StandardCharsets;\n\npublic final class PubSubBoundProbe {\n\n private static String token(int n) {\n return \"a\".repeat(n - 1) + \"z\";\n }\n\n private static int bytes(String s) {\n return s.getBytes(StandardCharsets.UTF_8).length;\n }\n\n private static final RedisCodec CODEC =\n new RedisCodec<>() {\n public String id() { return \"probe-utf8\"; }\n public byte[] encode(String v) { return v.getBytes(StandardCharsets.UTF_8); }\n public String decode(byte[] b) { return new String(b, StandardCharsets.UTF_8); }\n };\n\n public static void main(String[] args) {\n RedisNamespace ns = new RedisNamespace(token(64), token(64), token(64));\n RedisKeyName name = new RedisKeyName(token(64), token(128));\n\n System.out.println(\"MAX_KEY_BYTES = \" + RedisKeyRules.MAX_KEY_BYTES);\n System.out.println();\n System.out.println(\"[구성 요소의 상한] 토큰 64, 식별자 128, 슬롯 태그 128\");\n System.out.println(\" namespace.prefix() 길이 : \" + bytes(ns.prefix()));\n System.out.println();\n\n System.out.println(\"[채널] 구성 요소를 최대로 채운 렌더\");\n String ch = new PubSubChannel<>(ns, name, CODEC).render();\n String sh = new ShardedPubSubChannel<>(ns, name, CODEC).render();\n System.out.printf(\" PubSubChannel 렌더 %d 바이트 여유 %d%n\",\n bytes(ch), RedisKeyRules.MAX_KEY_BYTES - bytes(ch));\n System.out.printf(\" ShardedPubSubChannel 렌더 %d 바이트 여유 %d%n\",\n bytes(sh), RedisKeyRules.MAX_KEY_BYTES - bytes(sh));\n System.out.println();\n\n System.out.println(\"[키] 같은 규칙을 받은 조각들, 슬롯 태그 하나가 더 붙는다\");\n RedisKeyRenderer renderer = new RedisKeyRenderer(RedisKeyRules.MAX_KEY_BYTES);\n System.out.println(\" 태그 없음 -> 렌더 \" + bytes(renderer.render(QualifiedRedisKey.of(ns, name))) + \" 바이트\");\n QualifiedRedisKey tagged =\n QualifiedRedisKey.tagged(ns, name, new RedisSlotTag(token(128)));\n try {\n System.out.println(\" 태그 있음 -> 렌더 \" + bytes(renderer.render(tagged)) + \" 바이트\");\n } catch (IllegalArgumentException e) {\n System.out.println(\" 태그 있음 -> \" + e.getMessage());\n }\n System.out.println();\n\n System.out.println(\"[기본 이름공간] 설정 기본값 local:sample-service:shared\");\n RedisNamespace def = new RedisNamespace(\"local\", \"sample-service\", \"shared\");\n System.out.println(\" prefix 길이 : \" + bytes(def.prefix()));\n System.out.println(\" 채널 렌더 : \" + bytes(new PubSubChannel<>(def, name, CODEC).render()) + \" 바이트\");\n QualifiedRedisKey defTagged =\n QualifiedRedisKey.tagged(def, name, new RedisSlotTag(token(128)));\n try {\n System.out.println(\" 태그 붙인 키 : \" + bytes(renderer.render(defTagged)) + \" 바이트\");\n } catch (IllegalArgumentException e) {\n System.out.println(\" 태그 붙인 키 -> \" + e.getMessage());\n }\n System.out.println();\n\n System.out.println(\"[패턴] 접미가 토큰 규칙을 받지 않는다 (이름공간은 위와 같은 최대치)\");\n for (int n : new int[] {64, 317, 318}) {\n try {\n PubSubPattern p = new PubSubPattern<>(ns, token(n), CODEC);\n System.out.printf(\" suffix %-4d -> 생성됨, 렌더 %d 바이트%n\", n, bytes(p.render()));\n } catch (IllegalArgumentException e) {\n System.out.printf(\" suffix %-4d -> %s%n\", n, e.getMessage());\n }\n }\n System.out.println();\n\n System.out.println(\"[상한을 어디서 가져오는가]\");\n System.out.println(\" RedisKeyRenderer : 생성자 인자 (1..512)\");\n System.out.println(\" PubSubPattern : RedisKeyRules.MAX_KEY_BYTES 상수\");\n RedisKeyRenderer tight = new RedisKeyRenderer(256);\n try {\n tight.render(QualifiedRedisKey.of(ns, name));\n System.out.println(\" 상한 256 렌더러에 키 388 바이트 -> 통과\");\n } catch (IllegalArgumentException e) {\n System.out.println(\" 상한 256 렌더러에 키 -> \" + e.getMessage());\n }\n System.out.println(\" 같은 배포의 채널 \" + bytes(ch) + \" 바이트 -> 검사 없음\");\n }\n}\nJAVA\njavac -encoding UTF-8 -cp \"$JAR\" -d \"$D\" \"$D/PubSubBoundProbe.java\"\njava -Dstdout.encoding=UTF-8 -cp \"$JAR:$D\" PubSubBoundProbe\n", "cwd": "/shared/codebase/clean-architecture-backend-template/src/adapter/outbound/cache-redis", "exitCode": 0, "executedAt": "2026-09-02T11:21:34+00:00", "sourceRevision": "21234e38cdb9a926cbc92bb97a2aee2e4a7d2916", "raw": "evidence/raw/a10-f004-pub-sub-bound.txt", "svg": "evidence/rendered/a10-f004-pub-sub-bound.svg", "rawSha256": "f9ca0446a9dbdba46c9eef12f89a1d325b8876df7f04fff16e7be9193849dee1", "lines": 28, "redaction": "none — 밀폐 탐침 실행 출력, 자격증명 없음" }