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>
180 lines
14 KiB
Plaintext
180 lines
14 KiB
Plaintext
Sub-scope 07 (cache-redis) 의미 포트 어댑터 + live/topology test 기반 — manifest and probes
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T04:53:45+00:00
|
|
|
|
=== OWNED FILES ===
|
|
7767ce032226a698227c32afc9919a2f1505a6c9 main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/CacheEnvelope.java 213
|
|
ba2e04b43e830b1e0e51a6d3f692b14453e48737 main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapter.java 428
|
|
f3e259be5879a2e9779e8bdcdd7e3b8b26ffad66 main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/IdempotencyScripts.java 306
|
|
7cfb0cd5d9627e6fbbcc8afdb8ff4fd860b2e0b5 main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java 529
|
|
564bc94df42f434302237319ff5c5ebc251772ba main/java/dev/caskeleton/adapter/outbound/cache/redis/keyspace/CapabilityKeyspace.java 106
|
|
e95efcd322b8d0856c917fcbf5e8a87962b24bad main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/LeaseScripts.java 203
|
|
e31bc74da675ccf4167175beb2130bc76d4686bc main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java 403
|
|
7ffbe5e790bf632073e1e74ba925947eca306d63 main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitKeys.java 49
|
|
dd5d94054767e33f9cf567f12c2c03f4827acd67 main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitScripts.java 280
|
|
b0721e10cd394ad39325d5240935e836859ffa19 main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RedisEdgeRateLimitAdapter.java 187
|
|
a0975e74004850c1b6a27999230ad3eacee92bb6 main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RealtimeKeys.java 81
|
|
dfac770123e102a33caa742a6a8a44fe5e67c9b9 main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapter.java 301
|
|
ba584a26db38abe135f28b6b34d87ffc9095c489 main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisEphemeralFanoutAdapter.java 133
|
|
81e64fe800007dd0a5065a5a4952ecde8db8214d main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RegistrationCodec.java 76
|
|
b76c71e2c9eff216c24a9ba87c90c8433928f5c0 test/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapterTest.java 333
|
|
3d6f81aee563152b098ecf74119178a381b37cea test/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapterTest.java 337
|
|
ad8ef22e2b87d0afcdad7f0c654e7f5d9d7f911d test/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapterTest.java 292
|
|
04fc419bee78a203dee1a6a203a6baf3fd70b07e test/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RedisEdgeRateLimitAdapterTest.java 321
|
|
41e39a597eb22e44d44f1ab626cb7ba4e3ca925c test/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapterTest.java 262
|
|
65fe3f6335d61e97fa4b145bdb4b549f0c26af8e test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/RedisSdkModuleBoundaryTest.java 237
|
|
073ebb2de3602c48907e06678529c156c7941fb0 test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/RedisSupportMatrixTest.java 193
|
|
5bb26b538cacf05a74c6dfe409f84d63b399b729 test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/RedisTopologyContractTest.java 306
|
|
1a04d38388906e8be77963dc6f719428dd90a043 test/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/RedisTopologyEndpoint.java 244
|
|
2a4c054061162af30a8f4073d61759adde98126d test/java/dev/caskeleton/adapter/outbound/cache/redis/LiveRedisSemanticPortsTest.java 364
|
|
|
|
main: 14
|
|
=== 8.1 의미 어댑터가 guard/executor/타입 API를 지나는가 ===
|
|
$ git grep -ln 'SyncRedisCommandExecutor\|ReactiveRedisCommandExecutor\|CommandPolicyGuard\|CommandRequest' -- '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'
|
|
exit=1
|
|
|
|
$ git grep -ln 'RedisValueOperations\|RedisHashOperations\|RedisOperations\|RedisKeyOperations' -- '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'
|
|
exit=1
|
|
|
|
--- 대신 무엇을 부르는가 ---
|
|
$ git grep -c 'gateway()' -- '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/cache/RedisCacheRegionAdapter.java:6
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java:6
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java:4
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RedisEdgeRateLimitAdapter.java:1
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapter.java:13
|
|
exit=0
|
|
|
|
$ git grep -h '^import dev.caskeleton.adapter.outbound.cache.redis.sdk' -- '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' | sed 's/.*sdk\.//' | sort | uniq -c | sort -rn
|
|
6 api.key.RedisNamespace;
|
|
5 lettuce.connection.RedisRuntimeOwner;
|
|
5 lettuce.connection.RedisLease;
|
|
5 lettuce.connection.RedisConnectionKind;
|
|
3 lettuce.operations.RedisCommandGateway;
|
|
1 lettuce.operations.ScoredMemberPage;
|
|
1 lettuce.operations.RedisPubSubGateway;
|
|
1 lettuce.operations.MemberScanPage;
|
|
1 lettuce.operations.HashScanPage;
|
|
1 api.operations.SortedSetAddOptions;
|
|
1 api.operations.ScoreRange;
|
|
1 api.operations.PageRequest;
|
|
1 api.operations.ExpirationCondition;
|
|
1 api.key.RedisKeyRules;
|
|
1 api.error.RedisOperationException;
|
|
exit=0
|
|
|
|
=== 8.1b gateway가 주장하는 전제 ===
|
|
$ sed -n '36,40p' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisCommandGateway.java
|
|
* <p>The seam exists so the typed operations can be proven against a deterministic in-memory server
|
|
* without Docker, and so the Lettuce types stay inside {@link LettuceRedisCommandGateway}. Policy,
|
|
* permits, budgets, timeouts, and observability are not this interface's concern: everything routed
|
|
* through it has already passed {@code CommandPolicyGuard}.
|
|
*/
|
|
exit=0
|
|
|
|
$ sed -n '24,26p' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuard.java
|
|
/**
|
|
* The single admission point every command passes through.
|
|
*
|
|
exit=0
|
|
|
|
=== 8.2 두 번째 키 렌더 경로 ===
|
|
$ grep -n 'requireRenderedSize\|MAX_KEY_BYTES' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/keyspace/CapabilityKeyspace.java
|
|
exit=1
|
|
|
|
$ grep -n 'requireRenderedSize' adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/api/key/RedisKeyRenderer.java
|
|
45: return RedisKeyRules.requireRenderedSize(rendered.toString(), maxKeyBytes);
|
|
exit=0
|
|
|
|
$ git grep -ln 'CapabilityKeyspace' -- 'adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis'
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapter.java
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/keyspace/CapabilityKeyspace.java
|
|
adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java
|
|
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/realtime/RealtimeKeys.java
|
|
exit=0
|
|
|
|
=== 8.3 README 대조: 이 어댑터들이 구현하는 application/shared port ===
|
|
$ git grep -h 'implements [A-Za-z<>, ]*Port' -- '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' | sed 's/^ *//'
|
|
public final class RedisCacheRegionAdapter<K, V> implements CacheRegionPort<K, V> {
|
|
public final class RedisIdempotencyStoreAdapter implements IdempotencyStorePortV2 {
|
|
public final class RedisDistributedLeaseAdapter implements DistributedLeasePort {
|
|
public final class RedisEdgeRateLimitAdapter implements EdgeRateLimitPort {
|
|
public final class RedisConnectionRegistryAdapter implements ConnectionRegistryPort {
|
|
public final class RedisEphemeralFanoutAdapter implements EphemeralFanoutPort {
|
|
exit=0
|
|
|
|
$ sed -n '25,28p;35,37p' adapter/outbound/cache-redis/README.md
|
|
| 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 | 없음 | 없음 | 없음 |
|
|
readiness registry에도 `selected` card가 없으므로 Redis R2 release claim은 없다. 아래 절들은
|
|
이전 세대 semantic adapter 세트의 설계 결정을 기록한 것이며, 그 코드는 현재 이 leaf에 없다.
|
|
복구 범위는 위 plan의 Phase E가 소유한다.
|
|
exit=0
|
|
|
|
=== 8.4 이 어댑터들의 test ===
|
|
$ git ls-files 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/cache' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/lease' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/realtime' | xargs -n1 basename
|
|
RedisCacheRegionAdapterTest.java
|
|
RedisIdempotencyStoreAdapterTest.java
|
|
RedisDistributedLeaseAdapterTest.java
|
|
RedisEdgeRateLimitAdapterTest.java
|
|
RedisConnectionRegistryAdapterTest.java
|
|
exit=0
|
|
|
|
$ git grep -h ' void [a-z]' -- 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/cache' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/lease' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit' 'adapter/outbound/cache-redis/src/test/java/dev/caskeleton/adapter/outbound/cache/redis/realtime' | sed 's/^ *//' | head -50
|
|
void recordThenLookup() {
|
|
void staleButUsable() {
|
|
void expiredIsAMiss() {
|
|
void anAuthoritativeAbsenceIsNotAMiss() {
|
|
void aFutureSchemaIsQuarantined() {
|
|
void aForeignEntryIsReported() {
|
|
void regionInvalidationBumpsTheGeneration() {
|
|
void invalidateOneKey() {
|
|
void onlyIfAbsentDoesNotOverwrite() {
|
|
void onlyIfObservedRefusesAStaleWrite() {
|
|
void onlyIfObservedAppliesWhenUnchanged() {
|
|
void anUnreachableRedisDegrades() {
|
|
void negativeEntriesExpireSooner() {
|
|
void aFirstClaimExcludesTheSecond() {
|
|
void theSameAttemptReplays() {
|
|
void aDifferentRequestIsAMismatch() {
|
|
void theHappyPathCompletesAndReplays() {
|
|
void aStaleHandleCannotComplete() {
|
|
void completingTwiceIsIdempotentOnlyForTheSameResult() {
|
|
void aRetryableFailureAllowsAnotherAttempt() {
|
|
void anAbandonedOperationRequiresRecovery() {
|
|
void releasingBeforeExecutionFreesTheScope() {
|
|
void releasingAfterStartIsRefused() {
|
|
void anExpiredLeaseIsTakenOver() {
|
|
void inspectionRecoversALostReply() {
|
|
void anUnreachableRedisIsIndeterminate() {
|
|
void anUncontendedAcquireSucceeds() {
|
|
void aSecondHolderIsContended() {
|
|
void theSameClaimReplays() {
|
|
void theSameOwnerDifferentOperationConflicts() {
|
|
void aNonHolderCannotRelease() {
|
|
void aRenewExtendsOnlyOnConfirmation() {
|
|
void anExpiredBudgetIsLost() {
|
|
void releasingFreesTheLease() {
|
|
void releasingTwiceIsAbsent() {
|
|
void inspectingAnAbsentLease() {
|
|
void anUnreachableRedisIsIndeterminate() {
|
|
void ownerTokensAreUnguessable() {
|
|
void aFixedWindowBoundsItsWindow() {
|
|
void anewWindowRestoresTheBudget() {
|
|
void aSlidingCounterIsHonestAboutBeingApproximate() {
|
|
void aTokenBucketRefillsByPeriod() {
|
|
void anUnreachableRedisFailsClosed() {
|
|
void anUnknownPolicyIsIncompatible() {
|
|
void anOversizedCostIsRefused() {
|
|
void anExpiredCallerDeadlineIsRejectedImmediately() {
|
|
void subjectsAreIsolated() {
|
|
void announcedActorIsFound() {
|
|
void multipleNodesAreReportedNewestFirst() {
|
|
void reAnnounceReplaces() {
|
|
exit=0
|
|
|