Sub-scope 01 (cache-redis) governance + sdk/config — manifest and negative-space probes revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5 generatedAt=2026-08-30T03:59:48+00:00 cwd=/shared/codebase/clean-architecture-backend-template/src === OWNED FILES === d7d9299d8b8d61386d14f09d8c50da1dad758efa CLAUDE.md 143 8598f863777600267efdac00e5624d96d6ceec8f README.md 381 99c8f5bede105ee16ac9312c9aee055c16900076 build.gradle 202 062d16a37189b5de4bae45f3a05424504efda198 gradle.lockfile 164 e4b23adfdf4fe19b64c9d29c58bdd4d721fceba1 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/ConfiguredRedisPermitVerifier.java 94 08397249ba133ab758419f95af9c52ce9ccd17a9 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/ConfiguredRedisPolicyAuthority.java 106 c114b289e2da0b2afd60104af8c50f1561d070b6 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedAdvancedOperationPermit.java 24 d474a8d29be05c0e6e7eb836736ebd3625936012 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedMultiKeyPermit.java 21 c4c3b611267c35a0721413acba22cc773914bc06 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedPersistentKeyPermit.java 21 0bb89c573b6c80d71f0f9122374e7639acd9b57b src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbe.java 176 5c008151853a73ff417482f803602ba6f6418b61 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCorrectnessRoles.java 62 929a59fa7035c717447b72eac36cdeb57c2e0513 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCredentialResolver.java 125 5e1ec779b058125188a7e4b86150935f0df37a52 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisHealthContributor.java 88 a699afb72b41717d121dcff3778555da2ab18ea1 src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfiguration.java 397 2f577f0c64e7d108b9e7d477ccc61010e3da852b src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java 959 9ac874da2ffd3f901e168aaa3c20e019ae9922bb src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbe.java 142 47656d4826960bf5b4717d44569527a3ead037cf src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/LiveRedisCompositionTest.java 176 10a77ad0f8096326079ae25358863840616084b8 src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/LiveRedisTlsTest.java 141 3a266c797630d7cf8c5e917f759c8edbbf4ba675 src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java 158 e3ff2763675b302239deee9fbb6cb31d0b526a94 src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java 87 6e97086762eb93705c72b009d970002dbd882fd8 src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfigurationTest.java 447 6b576bbf04cee7791e7c688c9fb910e39935d24d src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettingsTest.java 201 8bea0f7148c054e816fe876ff4731b3961933a32 src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java 145 ebb0ed7ab8900c0007582e96351ff8bbfd7bb0ad src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RequiredWhenIsEnforcedTest.java 152 223a63b5cefb8be4169e068f5f6404e16a7c2a7f src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 1 1f51d1352a6a3f515b3d1cf4d40aed4f120d42de src/main/resources/redis-sdk/redis-command-policy.yml 1406 === 8.4 README readiness 표 vs 실제 소스 === $ sed -n '23,41p' adapter/outbound/cache-redis/README.md | Capability | API 구현 | Spring composition 구현 | 실서버 qualification | | --- | --- | --- | --- | | Redis SDK typed API (`…cache.redis.sdk`) | 있음 | settings bind + validate 까지만 | 없음 | | Topology client / connection lifecycle | 없음 | 없음 | 없음 | | cache / session / idempotency / rate limit / lease semantic port | 없음 | 없음 | 없음 | | role-aware health·readiness contributor | 없음 | 없음 | 없음 | 즉 현재 `APP_REDIS_ENABLED=true`가 하는 일은 `RedisSdkSettings`를 bind하고 cross-field 규칙을 fail-fast로 검증하는 것까지다. client, connection, gateway, semantic adapter, health contributor는 아직 조립되지 않는다. 남은 단계와 순서는 `docs/superpowers/plans/2026-08-10-redis-optionality-and-composition.md`에 있다. readiness registry에도 `selected` card가 없으므로 Redis R2 release claim은 없다. 아래 절들은 이전 세대 semantic adapter 세트의 설계 결정을 기록한 것이며, 그 코드는 현재 이 leaf에 없다. 복구 범위는 위 plan의 Phase E가 소유한다. 모듈은 Lettuce connection lifecycle, finite command timeout, reconnect replay 차단, finite request queue/admission, positive/negative TTL, absolute soft/hard expiry, deterministic bounded TTL jitter, digest-protected v2 binary exit=0 --- 'Topology client / connection lifecycle: 없음' 대조 --- $ git ls-files 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection' | xargs wc -l 75 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionKind.java 28 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionLease.java 142 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisConnectionRegistry.java 32 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisCredentialRole.java 44 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisLease.java 83 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeClient.java 316 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeOwner.java 600 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisTopologyClientFactory.java 153 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/SentinelFailoverObserver.java 1473 total exit=0 --- 'semantic port: 없음' / '그 코드는 현재 이 leaf에 없다' 대조 --- $ git ls-files 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache' 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency' 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease' 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit' 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime' 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/keyspace' | xargs wc -l 213 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/CacheEnvelope.java 428 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapter.java 306 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/IdempotencyScripts.java 529 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java 106 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/keyspace/CapabilityKeyspace.java 203 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/LeaseScripts.java 403 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java 49 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitKeys.java 280 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitScripts.java 187 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RedisEdgeRateLimitAdapter.java 81 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RealtimeKeys.java 301 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapter.java 133 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisEphemeralFanoutAdapter.java 76 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RegistrationCodec.java 3295 total exit=0 --- 'role-aware health contributor: 없음' 대조 --- $ git ls-files 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config' | grep -i 'health\|role' | xargs wc -l 62 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCorrectnessRoles.java 88 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisHealthContributor.java 150 total exit=0 === 8.4b build.gradle의 'Zero imports' 주장 3건 대조 === $ sed -n '6,16p;33,39p' adapter/outbound/cache-redis/build.gradle dependencies { // Registered edges the semantic port adapters need. The SDK itself imports nothing from them // today (0 imports across main source) — the semantic cache/session/idempotency/rate-limit // adapters that did were removed and are restored by Phase E of // docs/superpowers/plans/2026-08-10-redis-optionality-and-composition.md. They stay declared // because that restoration is the module's stated responsibility, not because anything here // compiles against them. implementation project(':application-core') implementation project(':shared-contract') implementation project(':adapter:outbound:support') // Deliberately absent: // org.springframework.data:spring-data-redis — the SDK owns its own typed API and command // policy on purpose; routing through Spring Data would reintroduce the untyped, unguarded // command surface the catalog exists to prevent. Zero imports. // io.micrometer:micrometer-core — observation leaves this leaf as RedisObservation through a // Consumer sink; binding it to a meter registry belongs to the composition root, not here. // Zero imports. exit=0 --- application-core / shared-contract / support import 실측 --- $ git grep -c 'import dev.caskeleton.application\.' -- 'adapter/outbound/cache-redis/src/main' | head -20 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapter.java:9 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java:18 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java:10 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RealtimeKeys.java:2 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapter.java:5 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisEphemeralFanoutAdapter.java:3 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RegistrationCodec.java:4 exit=0 $ git grep -l 'import dev.caskeleton.shared\.' -- 'adapter/outbound/cache-redis/src/main' | head -20 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitKeys.java adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitScripts.java adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RedisEdgeRateLimitAdapter.java exit=0 $ git grep -l 'import dev.caskeleton.adapter.outbound.support' -- 'adapter/outbound/cache-redis/src/main' | head exit=0 --- spring-data-redis / micrometer import 실측 --- $ git grep -c 'org.springframework.data.redis\|io.micrometer' -- 'adapter/outbound/cache-redis/src/main' exit=1 === 8.1 조립: auto-configuration이 실제로 만드는 bean === $ cat adapter/outbound/cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports dev.caskeleton.adapter.outbound.cache.redis.sdk.config.RedisSdkAutoConfiguration exit=0 $ grep -n '@Bean\|@Conditional\|class \|record ' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfiguration.java 35: *

{@code app.redis.enabled} is the whole switch. While it is false this class contributes 54:@ConditionalOnProperty(prefix = "app.redis", name = "enabled", havingValue = "true") 55:// Registers the binding post-processor that populates the @ConfigurationProperties @Bean below. 59:public class RedisSdkAutoConfiguration { 83: @Bean 101: @Bean 156: @Bean 226: @Bean 273: @Bean(destroyMethod = "close") 297: @Bean(RedisCorrectnessRoles.OPTIONAL_HEALTH_CONTRIBUTOR) 324: @Bean(RedisCorrectnessRoles.REQUIRED_HEALTH_CONTRIBUTOR) 325: @Conditional(RedisCorrectnessRoleBound.class) 344: static final class RedisCorrectnessRoleBound implements Condition { 364: public record RedisResolvedCredentials( 391: public record RedisSdkSettingsValidation(List warnings) { exit=0 === 8.2 활성화 게이트 === $ grep -rn 'APP_REDIS_ENABLED\|app.cache.redis\|matchIfMissing\|ConditionalOnProperty' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/*.java | head -30 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfiguration.java:18:import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfiguration.java:54:@ConditionalOnProperty(prefix = "app.redis", name = "enabled", havingValue = "true") exit=0 === 8.3 permit 타입 3종 === $ cat adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedAdvancedOperationPermit.java adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedPersistentKeyPermit.java adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/GrantedMultiKeyPermit.java package dev.caskeleton.adapter.outbound.cache.redis.sdk.config; import dev.caskeleton.adapter.outbound.cache.redis.sdk.api.command.AdvancedOperationPermit; import java.util.Objects; /** * The only advanced permit implementation the verifier accepts. * *

Package private on purpose: application code can implement the public interface, but it cannot * construct this type, and the verifier checks the concrete type as well as the signature. * * @param policyName the approved policy name * @param issuerId identity of the issuing authority instance * @param signature signature the issuing authority computed over the policy name */ record GrantedAdvancedOperationPermit(String policyName, String issuerId, String signature) implements AdvancedOperationPermit { GrantedAdvancedOperationPermit { Objects.requireNonNull(policyName, "policyName must be non-null"); Objects.requireNonNull(issuerId, "issuerId must be non-null"); Objects.requireNonNull(signature, "signature must be non-null"); } } package dev.caskeleton.adapter.outbound.cache.redis.sdk.config; import dev.caskeleton.adapter.outbound.cache.redis.sdk.api.command.PersistentKeyPermit; import java.util.Objects; /** * The only persistent-key permit implementation the verifier accepts. * * @param policyName the approved policy name * @param issuerId identity of the issuing authority instance * @param signature signature the issuing authority computed over the policy name */ record GrantedPersistentKeyPermit(String policyName, String issuerId, String signature) implements PersistentKeyPermit { GrantedPersistentKeyPermit { Objects.requireNonNull(policyName, "policyName must be non-null"); Objects.requireNonNull(issuerId, "issuerId must be non-null"); Objects.requireNonNull(signature, "signature must be non-null"); } } package dev.caskeleton.adapter.outbound.cache.redis.sdk.config; import dev.caskeleton.adapter.outbound.cache.redis.sdk.api.command.MultiKeyPermit; import java.util.Objects; /** * The only multi-key permit implementation the verifier accepts. * * @param policyName the approved policy name * @param issuerId identity of the issuing authority instance * @param signature signature the issuing authority computed over the policy name */ record GrantedMultiKeyPermit(String policyName, String issuerId, String signature) implements MultiKeyPermit { GrantedMultiKeyPermit { Objects.requireNonNull(policyName, "policyName must be non-null"); Objects.requireNonNull(issuerId, "issuerId must be non-null"); Objects.requireNonNull(signature, "signature must be non-null"); } } exit=0 === 8.4c config test 목록 === $ git ls-files 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config' | xargs -n1 basename LiveRedisCompositionTest.java LiveRedisTlsTest.java RedisCapabilityProbeTest.java RedisPermitProvenanceTest.java RedisSdkAutoConfigurationTest.java RedisSdkSettingsTest.java RedisStartupProbeTest.java RequiredWhenIsEnforcedTest.java exit=0 $ git grep -h ' void [a-z]' -- 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config' | sed 's/^ *//' | head -60 void aWrongPasswordIsRefused() throws Exception { void theModeProducesOneMatchingClient() { void aLeaseReachesTheServer() throws Exception { void theOptionalContributorReportsUp() { void closingTheContextTearsEverythingDown() { void aFilesystemTrustReferenceWorks() { void aClasspathTrustReferenceWorks() throws IOException { void unreadableTrustMaterialFailsAtStartup() { void plaintextAgainstATlsOnlyServerFails() { void redisSevenTwoHasNoHashFieldExpiration() { void redisSevenFourGainsHashFieldExpiration() { void aNewEnoughVersionWithoutTheCommandIsNotACapability() { void anExplicitlyEnabledCapabilityThatIsAbsentFailsStartup() { void aServerBelowTheBaselineFailsStartup() { void clusterWithANonZeroDatabaseFailsStartup() { void refusesAReplicatedDeploymentThatCannotKeepTheWritesItAcknowledges() { void acceptsAReplicatedDeploymentWithAWriteGuarantee() { void rejectsAReplicaCountWithNoLagBound() { void allowsTheLossOnlyWhenTheDeploymentDeclaredIt() { void doesNotRequireAWriteGuaranteeFromStandalone() { void acceptsAPermitTheAuthorityIssued() { void rejectsAPermitTheCallerImplementedItself() { void rejectsAPermitIssuedForADifferentPolicy() { void rejectsAPermitIssuedByAnotherAuthorityInstance() { void refusesToIssueAPolicyConfigurationDidNotEnable() { void aPermitCarriesNoAuthorityMaterialInItsPolicyName() { void absentSwitchRegistersNothing() { void disabledRegistersNothing() { void disabledIgnoresMalformedRedisConfiguration() { void disabledNeverAsksForASecretOrAConnection() { void enabledResolvesEachConfiguredRoleOnce() { void enabledBindsSettings() { void enabledRejectsClusterWithANonZeroDatabase() { void enabledRejectsAnUnboundedBlock() { void enabledRejectsARawGatewayWithoutAnAllowlist() { void enabledRejectsAMissingRawAllowlistResource() { void enabledAcceptsAReadableRawAllowlistResource() { void standaloneWithSeveralNodesIsRefused() { void sentinelWithoutAMasterNameIsRefused() { void enabledWithoutAnApplicationCredentialIsRefused() { void anonymousAccessCanBeDeclared() { void configuredAccountsAreResolvedPerRole() { void anUnresolvableCredentialReferenceFailsAtStartup() { void aLiteralCredentialIsRefused() { void clusterBuildsAClusterClient() { void theRuntimeOwnerFollowsTheContext() { void enabledRejectsAnAdminPlaneWithoutItsOwnCredential() { void clusterRejectsDatabaseOtherThanZero() { void standaloneAcceptsANonZeroDatabase() { void namespaceTokensAreValidatedAtStartup() { void aFastTimeoutAboveTheGuardrailWarnsAndFarAboveItFails() { void blockingMustNeverBeUnbounded() { void theRawGatewayRequiresAnAllowlistAndItsOwnCredential() { void theAdminPlaneRequiresItsOwnCredential() { void aClientCertificateWithoutItsKeyIsRefusedAtStartup() { void oneWayTlsNeedsNoClientCertificateAndNoTrustMaterial() { void advancedPoliciesCannotBeConfiguredWhileAdvancedOperationsAreDisabled() { void defaultsMatchTheDesignGuardrails() { void authenticationIsRequiredUnlessAnonymousIsDeclared() { void aSingleAccountDeploymentIsToldThatScriptingRunsAsTheApplicationAccount() { exit=0 === 8.1d reachability: startup probe / capability probe / permit authority === $ git grep -n 'RedisStartupProbe\|RedisCapabilityProbe' -- . | grep -v 'sdk/config/RedisStartupProbe.java' | grep -v 'sdk/config/RedisCapabilityProbe.java' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java:888: * signal exists for it; see {@link RedisCapabilityProbe#requireWriteDurability}. adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:17:class RedisCapabilityProbeTest { adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:19: private final RedisCapabilityProbe probe = new RedisCapabilityProbe(); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:28: RedisCapabilityProbeTest::everyCommand, adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:42: RedisCapabilityProbeTest::everyCommand, adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:86: RedisCapabilityProbeTest::everyCommand, adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:100: RedisCapabilityProbeTest::everyCommand, adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:16:class RedisStartupProbeTest { adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:18: private final RedisStartupProbe probe = new RedisStartupProbe(new RedisCapabilityProbe()); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:53: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:71: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:85: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:99: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:113: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:124: RedisStartupProbe.ServerFacts.from( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:139: RedisStartupProbe.ServerFacts.from( exit=0 $ git grep -n 'ConfiguredRedisPolicyAuthority\|ConfiguredRedisPermitVerifier' -- . | grep -v 'sdk/config/ConfiguredRedis' adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:17: private final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:18: new ConfiguredRedisPolicyAuthority( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:21: private final ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:22: new ConfiguredRedisPermitVerifier(authority, RedisDeploymentMode.STANDALONE); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:64: ConfiguredRedisPolicyAuthority other = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisPermitProvenanceTest.java:65: new ConfiguredRedisPolicyAuthority(List.of("collection-full-read")); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:23:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:24:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:39: private final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:40: new ConfiguredRedisPolicyAuthority( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuardTest.java:266: new ConfiguredRedisPermitVerifier(authority, mode), adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java:18:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java:19:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java:43: private final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java:44: new ConfiguredRedisPolicyAuthority(List.of()); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/ObservationIsolationTest.java:49: new ConfiguredRedisPermitVerifier(authority, RedisDeploymentMode.STANDALONE), adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:12:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:13:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:67: final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:68: new ConfiguredRedisPolicyAuthority(ENABLED_POLICIES); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:123: ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LiveRedisOperationsFixture.java:124: new ConfiguredRedisPermitVerifier(authority, endpoint.mode()); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:12:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:13:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:67: final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:68: new ConfiguredRedisPolicyAuthority(ENABLED_POLICIES); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:149: ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationsFixture.java:150: new ConfiguredRedisPermitVerifier(authority, RedisDeploymentMode.STANDALONE); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:17:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:18:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:58: private final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:59: new ConfiguredRedisPolicyAuthority( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:84: ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionContractTest.java:85: new ConfiguredRedisPermitVerifier(authority, RedisDeploymentMode.STANDALONE); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:18:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:19:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:63: private final ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:64: new ConfiguredRedisPolicyAuthority( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:78: ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisTransactionSlotContractTest.java:79: new ConfiguredRedisPermitVerifier(authority, RedisDeploymentMode.CLUSTER); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:20:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPermitVerifier; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:21:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.ConfiguredRedisPolicyAuthority; adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:110: ConfiguredRedisPolicyAuthority authority = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:111: new ConfiguredRedisPolicyAuthority( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:116: ConfiguredRedisPermitVerifier verifier = adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LiveRedisClusterTransactionTest.java:117: new ConfiguredRedisPermitVerifier(authority, endpoint.mode()); exit=0 $ git grep -n 'requireWriteDurability\|\.confirm(' -- 'adapter/outbound/cache-redis/src' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbe.java:133: public void requireWriteDurability( adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java:888: * signal exists for it; see {@link RedisCapabilityProbe#requireWriteDurability}. adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbe.java:61: probe.requireWriteDurability( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:113: assertThatThrownBy(() -> probe.requireWriteDurability(mode, 0, 10, false)) adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:122: probe.requireWriteDurability(RedisDeploymentMode.SENTINEL, 1, 10, false); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:123: probe.requireWriteDurability(RedisDeploymentMode.CLUSTER, 2, 5, false); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:133: assertThatThrownBy(() -> probe.requireWriteDurability(mode, 2, 0, false)) adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:142: probe.requireWriteDurability(RedisDeploymentMode.SENTINEL, 0, 0, true); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisCapabilityProbeTest.java:148: probe.requireWriteDurability(RedisDeploymentMode.STANDALONE, 0, 0, false); adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:51: probe.confirm( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:69: probe.confirm( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:83: probe.confirm( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:97: probe.confirm( adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisStartupProbeTest.java:137: probe.confirm( exit=0 === 8.1e app-bootstrap가 RedisCorrectnessRoles를 실제로 읽는가 === $ git grep -rn 'RedisCorrectnessRoles\|redisRequired\|redisOptional' -- app-bootstrap | head -20 app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:3:import dev.caskeleton.adapter.outbound.cache.redis.sdk.config.RedisCorrectnessRoles; app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:20: * contributor 'redisRequired' in group 'readiness' does not exist}. So a static {@code app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:21: * readiness.include} listing {@code redisRequired} is not "tolerated when Redis is off" — it is a app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:30: *

Hence this post-processor. It reads {@link RedisCorrectnessRoles} — the identical predicate app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:45: if (!redisEnabled(environment) || !RedisCorrectnessRoles.anySelected(environment)) { app-bootstrap/src/main/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessor.java:54: if (!members.add(RedisCorrectnessRoles.REQUIRED_HEALTH_CONTRIBUTOR)) { app-bootstrap/src/main/resources/application.yml:272: # unconditional contributors, and the one conditional member — `redisRequired` — is appended app-bootstrap/src/main/resources/application.yml:292: # `redisRequired` is NOT listed here, and must not be: group membership does not tolerate a app-bootstrap/src/main/resources/application.yml:295: # startup with "Included health contributor 'redisRequired' in group 'readiness' does not app-bootstrap/src/main/resources/application.yml:298: # `redisOptional` is never appended: a cache outage is reported as degraded detail and never app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:107: assertThat(readiness.isMember("redisRequired")) app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:110: assertThat(context.getBeansOfType(HealthIndicator.class)).doesNotContainKey("redisRequired"); app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:123: .containsKey("redisOptional") app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:124: .doesNotContainKey("redisRequired"); app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:125: assertThat(readiness.isMember("redisRequired")).isFalse(); app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:126: assertThat(readiness.isMember("redisOptional")) app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:139: assertThat(context.getBeansOfType(HealthIndicator.class)).containsKey("redisRequired"); app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:140: assertThat(readiness.isMember("redisRequired")) app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RedisReadinessGroupPostProcessorTest.java:166: .isMember("redisRequired")) app-bootstrap/src/test/java/dev/caskeleton/bootstrap/runtime/RuntimeHealthLifecycleContractTest.java:98: // The shipped file names only contributors that always exist. Both `redisRequired` and `db` are exit=0 === 8.2b RedisSdkSettings.validate 규칙 목록 === $ grep -n 'private void require\|violations.add\|warnings.add\|throw new IllegalStateException' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java | head -60 61: throw new IllegalStateException("Cluster supports database 0 only"); 64: throw new IllegalStateException("database index must not be negative"); 67: throw new IllegalStateException("at least one Redis node must be configured"); 77: throw new IllegalStateException("blocking commands must not be unbounded"); 80: throw new IllegalStateException("dedicated connection lanes need a positive ceiling"); 84: throw new IllegalStateException( 95: throw new IllegalStateException( 101: warnings.add( 110: throw new IllegalStateException("the raw gateway requires an allowlist resource"); 114: throw new IllegalStateException( 120: throw new IllegalStateException( 125: throw new IllegalStateException( 138: throw new IllegalStateException(name + " timeout must be positive"); 141: throw new IllegalStateException( 145: warnings.add( 255: throw new IllegalStateException( 268: throw new IllegalStateException("every Redis SDK limit must be positive"); 409: throw new IllegalStateException( 418: warnings.add( 426: warnings.add( 570: throw new IllegalStateException("the client name must not be blank"); 578: throw new IllegalStateException("the shutdown quiet period must not be negative"); 581: throw new IllegalStateException( 589: throw new IllegalStateException(name + " timeout must be positive"); 658: throw new IllegalStateException("cluster maximum redirects must be positive"); 663: throw new IllegalStateException("the cluster topology refresh period must be positive"); 694: throw new IllegalStateException("every Redis capacity ceiling must be positive"); 748: throw new IllegalStateException("the pub/sub buffer capacity must be positive"); 751: throw new IllegalStateException( exit=0 $ grep -n 'public List validate' -A 40 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java | head -60 58: public List validate() { 59- List warnings = new ArrayList<>(); 60- if (mode == RedisDeploymentMode.CLUSTER && database != 0) { 61- throw new IllegalStateException("Cluster supports database 0 only"); 62- } 63- if (database < 0) { 64- throw new IllegalStateException("database index must not be negative"); 65- } 66- if (nodes == null || nodes.isEmpty()) { 67- throw new IllegalStateException("at least one Redis node must be configured"); 68- } 69- namespace.validate(); 70- limits.validate(); 71- validateTimeout("fast", timeout.getFast(), warnings); 72- validateTimeout("collection", timeout.getCollection(), warnings); 73- validateTimeout("script", timeout.getScript(), warnings); 74- validateTimeout("batch", timeout.getBatch(), warnings); 75- validateTimeout("admin", timeout.getAdmin(), warnings); 76- if (blocking.getMaxBlock().isZero() || blocking.getMaxBlock().isNegative()) { 77- throw new IllegalStateException("blocking commands must not be unbounded"); 78- } 79- if (blocking.getMaxConnections() < 1 || transaction.getMaxConnections() < 1) { 80- throw new IllegalStateException("dedicated connection lanes need a positive ceiling"); 81- } 82- if (mode == RedisDeploymentMode.SENTINEL 83- && (sentinel.getMasterName() == null || sentinel.getMasterName().isBlank())) { 84- throw new IllegalStateException( 85- "app.redis.sentinel.master-name is required when app.redis.mode=sentinel: a Sentinel" 86- + " deployment must name the monitored primary; without it the client cannot resolve" 87- + " a primary at all, let alone follow a promotion"); 88- } 89- // A certificate without its key cannot build a key manager. Refused here rather than at the 90- // first connection, where it surfaced as a NullPointerException from inside the SSL options. 91- if (tls.isEnabled() 92- && tls.getClientCertificateResource() != null 93- && !tls.getClientCertificateResource().isBlank() 94- && (tls.getClientKeyReference() == null || tls.getClientKeyReference().isBlank())) { 95- throw new IllegalStateException( 96- "app.redis.tls.client-key-reference is required when" 97- + " app.redis.tls.client-certificate-resource is configured: mutual TLS presents a" 98- + " certificate, and a certificate without its private key cannot be presented"); exit=0 === 8.3b raw allowlist 기본 리소스가 실재하는가 === $ grep -n 'raw-command-allowlist\|policyResource' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java | head 800: private String policyResource = "classpath:redis-sdk/raw-command-allowlist.yml"; 812: return policyResource; 815: public void setPolicyResource(String policyResource) { 816: this.policyResource = policyResource; exit=0 $ git ls-files | grep -i 'raw-command-allowlist' | head exit=0 $ git ls-files 'adapter/outbound/cache-redis/src/main/resources' adapter/outbound/cache-redis/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports adapter/outbound/cache-redis/src/main/resources/redis-sdk/redis-command-policy.yml exit=0