Sub-scope 05 (cache-redis) sdk/lettuce/{command,connection,codec,observability} — manifest and probes revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 generatedAt=2026-08-30T04:47:40+00:00 === OWNED FILES === ba89d938899ab92cf3c4d45facfea03dee1368c8 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/ByteArrayCodec.java 38 2dc49adbd86e934e3fc66a6c7abd50b63afbf1d5 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/DoubleCodec.java 57 32a4a8acb6f48d36deb7727532f4d4261685094c main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/JsonEnvelopeFraming.java 206 89d996cd317067144f662ac11bd2dc284722ccbf main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/LongCodec.java 56 77f6e2e5cf3ce677e40041c3f9c7547b170d77a1 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/RedisCodecRegistry.java 203 68e4685bae9138b285c1d4d857dc973a4e88f79a main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/Utf8StringCodec.java 39 fa5ffcfc22983ade442f48a47f50fe0ddc43cb9f main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/VersionedJsonCodec.java 106 7a2f709f6deba7a8e22d33edd77887c7c20a353a main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandAdmission.java 32 fa34b73b14534f84b549177ca99f75a26b918700 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandExecutionContext.java 156 70d9570303805749615e76cc4d2c488dd367ae45 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuard.java 278 41098f8de3bf0c10ed8abdce07f9b106372a50be main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandRequest.java 87 a55e4fd3b6a1cc3d140ab37179c777676909759e main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ExecutionCertainty.java 43 e5228fcf891ed04864d190c4b6dc8c0275fa1866 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/LettuceExceptionTranslator.java 234 f3134a239b1b77c674c9f612af9e8bf64d64c4b1 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/QueueingRedisCommandExecutor.java 134 7e51cf05ea83b97191474a44c4652380358e8554 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ReactiveRedisCommandExecutor.java 88 b9e63268abe98778298e62faaeda66c7d8739180 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandCatalog.java 136 a5e60cab2033eadb33a69cfd309568a353c87e42 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiff.java 145 18bb7d816ea6e088d6730b37a0e7ac808197dd2d main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicy.java 95 1a64df2654ff5f22b787c92a9eb8d9934946ef73 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicyLoader.java 268 e54187b76418ed537f77f11c37c34ccf0c2b768e main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisServerCommandMetadata.java 29 977f8e89a52ba9c3b62be4b6f1da677d520bd60e main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/SyncRedisCommandExecutor.java 100 0def0f4aee46a15ec87343cb2b2ba120ee0f1ae6 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionKind.java 75 6fc28d3e46dedab9e8314ed18af6eda412d0ab59 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionLease.java 28 6d74f61bb9be6537da97574027af6e8d7bcc7966 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionRegistry.java 142 ff404b80d826bb2c50fcdc3b4477c6b0dca19a39 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisCredentialRole.java 32 850e71b4410735b9169ba12760c1095979244872 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisLease.java 44 84a9b13cc707d13e0b627a03aa55af9fe61c6d87 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeClient.java 83 bbb465c6bfd766bdcd2bf5c8156b7fd6ba996670 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeOwner.java 316 d4d23ae941e401f1da4ce515210346f335dbb000 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisTopologyClientFactory.java 600 1e3f37c50fc407b67a9f67bab5ae1c3b1f138f0d main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserver.java 153 29749551acb95daae4c8a38f89af5f0cf4490c12 main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/observability/NoThrowObservationSink.java 88 d9fa4a2c8ebe4bd89d2f845cc4c2158671a4e25f main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/observability/RedisObservation.java 203 443eef8d46a40284bf563109d0f61eb40112065d test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/RedisCodecRegistryTest.java 94 5e5d6d76496bd95eab31cc857e80c7da2a12301b test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/codec/VersionedJsonCodecTest.java 216 fbe267c5fa2d90a95e91263bf819211ec68752b4 test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java 368 917f7e0656a54da796c2dc2557a2972d0fd6fa8a test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/LettuceExceptionTranslatorTest.java 138 af77d26f91f6b5bc4abb8cc88dd0019b3b359151 test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java 147 cfb95d32b43012a9630855b2d990c7f1dcf9141a test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java 119 7e23f55e3acde280f5e1ef52e581588b3663b22d test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicyLoaderTest.java 199 8e41ef7b84ef71971e1a90d6a7cfdd91eea38f8b test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionRegistryTest.java 100 cd5253c6f86b78453909dad203560f821094843f test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeOwnerTest.java 238 dcb70191c7df1fc78ea25a95dbf6eb40eddf44de test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java 109 daf77cad4cf7fead3f9a6fd5817000c4893f350c test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/observability/RedisObservationTest.java 108 1e14e4c319fe43d940b2ea66595800df458af277 test/resources/redis-sdk/golden/order-summary-v1.json 0 main: 32 test: 11 resource: 1 === 8.1 reachability: 이 sub-scope 타입들의 production 소비자 === CommandPolicyGuard: main 참조 파일 17 RedisCommandCatalog: main 참조 파일 4 RedisCommandPolicyLoader: main 참조 파일 1 RedisCommandMetadataDiff: main 참조 파일 0 SentinelFailoverObserver: main 참조 파일 0 RedisConnectionRegistry: main 참조 파일 1 QueueingRedisCommandExecutor: main 참조 파일 1 RedisCodecRegistry: main 참조 파일 0 NoThrowObservationSink: main 참조 파일 3 $ git grep -n 'SentinelFailoverObserver' -- 'adapter/outbound/cache-redis/src' | grep -v 'connection/SentinelFailoverObserver.java' adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:20:class SentinelFailoverObserverTest { adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:29: SentinelFailoverObserver observer = new SentinelFailoverObserver(16); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:40: SentinelFailoverObserver observer = new SentinelFailoverObserver(16); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:50: SentinelFailoverObserver observer = new SentinelFailoverObserver(16); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:67: SentinelFailoverObserver observer = new SentinelFailoverObserver(2); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:81: SentinelFailoverObserver observer = new SentinelFailoverObserver(4); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserverTest.java:90: assertThatThrownBy(() -> new SentinelFailoverObserver(0)) adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisSentinelPromotionTest.java:15:import dev.caskeleton.adapter.outbound.cache.redis.sdk.lettuce.connection.SentinelFailoverObserver; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisSentinelPromotionTest.java:40: *

The unit suite can prove that {@link SentinelFailoverObserver} counts what it is told and that adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisSentinelPromotionTest.java:105: private static SentinelFailoverObserver observer; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisSentinelPromotionTest.java:146: observer = new SentinelFailoverObserver(1_000); exit=0 $ git grep -n 'RedisCommandMetadataDiff' -- 'adapter/outbound/cache-redis/src' | grep -v 'command/RedisCommandMetadataDiff.java' adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:12:class RedisCommandMetadataDiffTest { adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:19: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:20: RedisCommandMetadataDiff.compare( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:35: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:36: RedisCommandMetadataDiff.compare( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:51: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:52: RedisCommandMetadataDiff.compare( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:65: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:66: RedisCommandMetadataDiff.compare( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:78: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:79: RedisCommandMetadataDiff.compare( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:91: RedisCommandMetadataDiff diff = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandMetadataDiffTest.java:92: RedisCommandMetadataDiff.compare( exit=0 === 8.2 sync/reactive 실행자가 같은 guard 경로를 지나는가 === $ grep -n 'guard\|admission\|translator\|observationSink' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/SyncRedisCommandExecutor.java | head -20 17: * Synchronous execution of a guarded command. 25: private final CommandPolicyGuard guard; 26: private final LettuceExceptionTranslator translator; 28: private final Consumer observationSink; 33: * @param guard the admission guard 34: * @param translator the driver failure translator 36: * @param observationSink receives one completed observation per execution 39: CommandPolicyGuard guard, 40: LettuceExceptionTranslator translator, 42: Consumer observationSink) { 43: this.guard = Objects.requireNonNull(guard, "guard must be non-null"); 44: this.translator = Objects.requireNonNull(translator, "translator must be non-null"); 47: this.observationSink = NoThrowObservationSink.wrap(observationSink); 51: * Executes a guarded command and waits for its result. 56: * @throws RedisOperationException when the guard refuses the command or the driver fails 60: CommandAdmission admission = guard.validate(request); 63: admission.descriptor(), admission.connectionKind(), deploymentMode, admission.slot()); 66: // Only the Redis call is inside the guarded region. Recording the success used to sit here too, 70: result = await(request.invocation().get(), admission.timeout()); 73: translator.translate(failure, context(admission, startedAt)); exit=0 $ grep -n 'guard\|admission\|translator\|observationSink' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ReactiveRedisCommandExecutor.java | head -20 13: * Reactive execution of a guarded command. 21: private final CommandPolicyGuard guard; 22: private final LettuceExceptionTranslator translator; 24: private final Consumer observationSink; 29: * @param guard the admission guard 30: * @param translator the driver failure translator 32: * @param observationSink receives one completed observation per execution 35: CommandPolicyGuard guard, 36: LettuceExceptionTranslator translator, 38: Consumer observationSink) { 39: this.guard = Objects.requireNonNull(guard, "guard must be non-null"); 40: this.translator = Objects.requireNonNull(translator, "translator must be non-null"); 46: this.observationSink = NoThrowObservationSink.wrap(observationSink); 50: * Executes a guarded command. 60: CommandAdmission admission = guard.validate(request); 63: admission.descriptor(), 64: admission.connectionKind(), 66: admission.slot()); 69: .timeout(admission.timeout()) 70: .doOnSuccess(result -> observationSink.accept(observation.succeeded(0))) exit=0 === 8.3 정책 이름 교차 검사 (sub-scope 04 §37 이월 확정) === $ grep -n 'everyAdvancedCommandDeclaresThePermitPolicyItRequires' -A 10 adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicyLoaderTest.java 114: void everyAdvancedCommandDeclaresThePermitPolicyItRequires() { 115- loader.loadDefault().values().stream() 116- .filter(policy -> policy.support() == CommandSupport.ADVANCED_TYPED) 117- .forEach( 118- policy -> 119- assertThat(policy.requiredPolicyName()) 120- .as("command '%s'", policy.commandId()) 121- .isPresent()); 122- } 123- 124- @Test exit=0 $ git grep -n 'RedisOperationContext' -- 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command' adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:229: // RedisOperationContext.requireReplyWithinBudget, and is covered against real replies by the exit=0 === 8.4 정책 문서의 필드 목록 대 loader가 받는 필드 === $ grep -n 'private static final Set FIELDS' -A 15 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicyLoader.java 39: private static final Set FIELDS = 40- Set.of( 41- "minimum-version", 42- "risk", 43- "support", 44- "access", 45- "blocking", 46- "optional-block", 47- "read-only", 48- "retry-safe", 49- "may-be-ambiguous", 50- "timeout-profile", 51- "key-spec", 52- "required-policy"); 53- 54- /** exit=0 $ sed -n '7,19p' adapter/outbound/cache-redis/src/main/resources/redis-sdk/redis-command-policy.yml # Fields and their defaults: # risk required R1 | R2 | R3 | R4 # support required TYPED | ADVANCED_TYPED | RAW_ONLY | ADMIN_ONLY | VERSION_GATED | BLOCKED # minimum-version "7.2" lowest server version that carries the command # access derived TYPED->APPLICATION, ADVANCED_TYPED/VERSION_GATED->APPLICATION_ADVANCED, # RAW_ONLY->RAW_GATEWAY, ADMIN_ONLY->ADMIN_READONLY, BLOCKED->NONE # blocking false occupies its connection until the server replies or the block expires # read-only false never mutates the dataset # retry-safe read-only may be retried after a failure that could have reached the server # may-be-ambiguous !read-only a failure may leave the outcome unknown # timeout-profile derived R1->FAST, R2->COLLECTION, R3->ADMIN, blocking->BLOCKING # key-spec "1 1 1" " ", "none", or "movable" # required-policy - permit policy name an R2 command demands exit=0 $ grep -n 'optional-block' adapter/outbound/cache-redis/src/main/resources/redis-sdk/redis-command-policy.yml 666: optional-block: true 673: optional-block: true exit=0 === 8.4b 관측: 태그 집합이 닫혀 있는가 === $ grep -n 'Set.of\|TAG\|tags()' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/observability/RedisObservation.java | head -20 exit=0 === 8.4c 제거된 중복 mechanism의 흔적 === $ grep -n 'validateReply' -B 2 -A 10 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuard.java 107- } 108- 109: // There is deliberately no validateReply(...) here. 110- // 111- // This class used to carry one, and nothing called it. Reply budgets were — and are — enforced by 112- // RedisOperationContext.requireReplyWithinBudget, which the typed operations call at the point 113- // where the reply is decoded and its real size is known. Two mechanisms for one rule, with the 114- // more visible one dead, is worse than one: a reader finds the guard's method, assumes replies 115- // are bounded during admission, and writes an operation that never bounds its own. 116- // 117- // Admission cannot do this job anyway. The guard runs before the command is sent, so the only 118- // reply size available to it is the estimate the request declared. The authority has to sit 119- // where the bytes actually arrive. exit=0