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>
338 lines
21 KiB
Plaintext
338 lines
21 KiB
Plaintext
adapter-outbound-cache-redis — 모듈 인벤토리
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T03:57:11+00:00
|
|
|
|
=== tracked file 총계 ===
|
|
$ git ls-files 'adapter/outbound/cache-redis' | wc -l
|
|
390
|
|
exit=0
|
|
|
|
$ git ls-files 'adapter/outbound/cache-redis' | sed 's|^adapter/outbound/cache-redis/||' | awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn
|
|
316 src/main
|
|
70 src/test
|
|
1 gradle.lockfile/
|
|
1 build.gradle/
|
|
1 README.md/
|
|
1 CLAUDE.md/
|
|
exit=0
|
|
|
|
=== source set 별 ===
|
|
main: 316 files, 33489 LOC
|
|
test: 70 files, 15244 LOC
|
|
|
|
=== main 패키지 배치 ===
|
|
$ git ls-files 'adapter/outbound/cache-redis/src/main' | grep '\.java$' | sed 's|.*/dev/caskeleton/||' | xargs -n1 dirname | sort | uniq -c | sort -rn
|
|
69 adapter/outbound/cache/redis/sdk/api/operations
|
|
67 adapter/outbound/cache/redis/sdk/lettuce/operations
|
|
17 adapter/outbound/cache/redis/sdk/programmability
|
|
17 adapter/outbound/cache/redis/sdk/api/reactive
|
|
17 adapter/outbound/cache/redis/sdk/api/key
|
|
14 adapter/outbound/cache/redis/sdk/lettuce/command
|
|
14 adapter/outbound/cache/redis/sdk/api/error
|
|
13 adapter/outbound/cache/redis/sdk/api/command
|
|
12 adapter/outbound/cache/redis/sdk/config
|
|
9 adapter/outbound/cache/redis/sdk/lettuce/connection
|
|
7 adapter/outbound/cache/redis/sdk/raw
|
|
7 adapter/outbound/cache/redis/sdk/lettuce/codec
|
|
7 adapter/outbound/cache/redis/sdk/extensions/search
|
|
6 adapter/outbound/cache/redis/sdk/api
|
|
5 adapter/outbound/cache/redis/sdk/cluster
|
|
4 adapter/outbound/cache/redis/sdk/extensions/timeseries
|
|
4 adapter/outbound/cache/redis/sdk/admin
|
|
4 adapter/outbound/cache/redis/realtime
|
|
3 adapter/outbound/cache/redis/sdk/extensions/json
|
|
3 adapter/outbound/cache/redis/sdk/api/codec
|
|
3 adapter/outbound/cache/redis/ratelimit
|
|
2 adapter/outbound/cache/redis/sdk/lettuce/observability
|
|
2 adapter/outbound/cache/redis/sdk/extensions/probabilistic
|
|
2 adapter/outbound/cache/redis/lease
|
|
2 adapter/outbound/cache/redis/idempotency
|
|
2 adapter/outbound/cache/redis/cache
|
|
1 adapter/outbound/cache/redis/sdk/extensions
|
|
1 adapter/outbound/cache/redis/keyspace
|
|
exit=0
|
|
|
|
=== main 상위 30 파일 ===
|
|
$ git ls-files 'adapter/outbound/cache-redis/src/main' | grep '\.java$' | xargs wc -l | sort -rn | head -31
|
|
32082 total
|
|
1282 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LettuceRedisCommandGateway.java
|
|
1227 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisCommandGateway.java
|
|
959 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkSettings.java
|
|
600 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisTopologyClientFactory.java
|
|
529 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/RedisIdempotencyStoreAdapter.java
|
|
487 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/StreamOperationRequests.java
|
|
428 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/cache/RedisCacheRegionAdapter.java
|
|
422 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/HashOperationRequests.java
|
|
413 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/SortedSetOperationRequests.java
|
|
405 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/RedisOperationContext.java
|
|
403 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/lease/RedisDistributedLeaseAdapter.java
|
|
397 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/config/RedisSdkAutoConfiguration.java
|
|
361 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/programmability/LettuceRedisTransactionOperations.java
|
|
344 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/ListOperationRequests.java
|
|
337 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/admin/LettuceRedisAdminOperations.java
|
|
333 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/SetOperationRequests.java
|
|
320 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/ValueOperationRequests.java
|
|
316 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/connection/RedisRuntimeOwner.java
|
|
306 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/KeyOperationRequests.java
|
|
306 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/idempotency/IdempotencyScripts.java
|
|
301 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/realtime/RedisConnectionRegistryAdapter.java
|
|
280 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/ratelimit/RateLimitScripts.java
|
|
278 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/CommandPolicyGuard.java
|
|
268 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/BatchExecution.java
|
|
268 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/RedisCommandPolicyLoader.java
|
|
251 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/GeoOperationRequests.java
|
|
247 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/operations/LettuceRedisBatch.java
|
|
234 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/lettuce/command/LettuceExceptionTranslator.java
|
|
230 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/extensions/timeseries/LettuceRedisTimeSeriesOperations.java
|
|
220 adapter/outbound/cache-redis/src/main/java/dev/caskeleton/adapter/outbound/cache/redis/sdk/extensions/probabilistic/LettuceRedisProbabilisticOperations.java
|
|
exit=0
|
|
|
|
=== governance / 루트 파일 ===
|
|
$ git ls-files 'adapter/outbound/cache-redis' | grep -v '^adapter/outbound/cache-redis/src/'
|
|
adapter/outbound/cache-redis/CLAUDE.md
|
|
adapter/outbound/cache-redis/README.md
|
|
adapter/outbound/cache-redis/build.gradle
|
|
adapter/outbound/cache-redis/gradle.lockfile
|
|
exit=0
|
|
|
|
=== 레지스트리 엔트리 ===
|
|
$ python3 -c "
|
|
import json;d=json.load(open('config/architecture/modules.json'))
|
|
for m in d['modules']:
|
|
if m['id']=='adapter-outbound-cache-redis': print(json.dumps(m,ensure_ascii=False,indent=2))
|
|
"
|
|
{
|
|
"id": "adapter-outbound-cache-redis",
|
|
"gradle_path": ":adapter:outbound:cache-redis",
|
|
"source_path": "src/adapter/outbound/cache-redis",
|
|
"allowed_dependencies": [
|
|
"domain-core",
|
|
"application-core",
|
|
"shared-contract",
|
|
"adapter-outbound-support"
|
|
],
|
|
"runtime_memberships": [
|
|
"app-bootstrap"
|
|
]
|
|
}
|
|
exit=0
|
|
|
|
=== build.gradle ===
|
|
$ cat -n 'adapter/outbound/cache-redis/build.gradle'
|
|
1 // Redis SDK leaf — see docs/superpowers/specs/2026-08-07-redis-wrapper-typed-api-design.md.
|
|
2 //
|
|
3 // The design models the SDK as separate Gradle modules. This repository's fail-closed module
|
|
4 // registry outranks that layout, so the module boundaries are packages under
|
|
5 // dev.caskeleton.adapter.outbound.cache.redis.sdk and RedisSdkModuleBoundaryTest enforces them.
|
|
6 dependencies {
|
|
7 // Registered edges the semantic port adapters need. The SDK itself imports nothing from them
|
|
8 // today (0 imports across main source) — the semantic cache/session/idempotency/rate-limit
|
|
9 // adapters that did were removed and are restored by Phase E of
|
|
10 // docs/superpowers/plans/2026-08-10-redis-optionality-and-composition.md. They stay declared
|
|
11 // because that restoration is the module's stated responsibility, not because anything here
|
|
12 // compiles against them.
|
|
13 implementation project(':application-core')
|
|
14 implementation project(':shared-contract')
|
|
15 implementation project(':adapter:outbound:support')
|
|
16
|
|
17 implementation 'org.springframework.boot:spring-boot-autoconfigure'
|
|
18 // The role-aware health contributors are HealthIndicators; the readiness probe is the only
|
|
19 // place the required/optional Redis taxonomy can actually be enforced.
|
|
20 implementation 'org.springframework.boot:spring-boot-health'
|
|
21 // Boot's Health type carries Jackson annotations. Without the annotations on the compile
|
|
22 // classpath javac emits an 'unknown enum constant' warning, and this build is -Werror. Runtime
|
|
23 // does not need it from here — the app already has Jackson — so compileOnly is the honest scope.
|
|
24 compileOnly 'com.fasterxml.jackson.core:jackson-annotations'
|
|
25 implementation 'io.lettuce:lettuce-core'
|
|
26 // Reactor is in the public signature of sdk.api.reactive, and it was reaching this module only
|
|
27 // transitively through lettuce-core. A driver upgrade that stopped exposing it would have
|
|
28 // broken compilation of the SDK's own published API, so it is declared directly.
|
|
29 implementation 'io.projectreactor:reactor-core'
|
|
30 implementation 'org.slf4j:slf4j-api'
|
|
31 annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
|
|
32
|
|
33 // Deliberately absent:
|
|
34 // org.springframework.data:spring-data-redis — the SDK owns its own typed API and command
|
|
35 // policy on purpose; routing through Spring Data would reintroduce the untyped, unguarded
|
|
36 // command surface the catalog exists to prevent. Zero imports.
|
|
37 // io.micrometer:micrometer-core — observation leaves this leaf as RedisObservation through a
|
|
38 // Consumer sink; binding it to a meter registry belongs to the composition root, not here.
|
|
39 // Zero imports.
|
|
40 }
|
|
41
|
|
42 tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' }
|
|
43
|
|
44 // The topology lane is opt-in and fail-closed. The default unit task excludes it, and selecting it
|
|
45 // without an endpoint is an error rather than a skip: a topology test that silently passes because
|
|
46 // it never connected is worse than not having one.
|
|
47 tasks.named('test') {
|
|
48 useJUnitPlatform {
|
|
49 excludeTags 'redis-topology'
|
|
50 }
|
|
51 }
|
|
52
|
|
53 // Lane selection is derived from the declared mode rather than chosen by hand. A promotion test is
|
|
54 // meaningless without sentinels and a cross-slot test is meaningless without a cluster, but writing
|
|
55 // that as a runtime assumption would turn "the lane was never started" into a green skip. Selecting
|
|
56 // by tag keeps the lane fail-closed: what a mode cannot prove is not selected, and what is selected
|
|
57 // must pass.
|
|
58 //
|
|
59 // The mode is an allowlist, not free text. Deriving the tag from an arbitrary property produced the
|
|
60 // worst possible result for a qualification lane: `-Predis.topology.mode=TYPO` built the tag
|
|
61 // `lane-typo`, matched nothing, ran zero tests and exited 0. A release gate that reports success
|
|
62 // for a lane it never ran is worse than no gate, so an unknown mode is an error and a run that
|
|
63 // executed no test is a failure.
|
|
64 // `tls` is a lane, not a deployment mode. Its shape is standalone; what it qualifies is the
|
|
65 // transport, which no other lane carries a single command over. It was reachable only by hand —
|
|
66 // point LiveRedisCompositionTest at the TLS compose with an ad-hoc init script — which is another
|
|
67 // way of saying the release gate did not cover TLS at all.
|
|
68 def REDIS_TOPOLOGY_MODES = ['standalone', 'sentinel', 'cluster', 'tls'] as Set
|
|
69 def REDIS_TOPOLOGY_DEPLOYMENT_MODE = ['standalone': 'standalone', 'sentinel': 'sentinel',
|
|
70 'cluster': 'cluster', 'tls': 'standalone']
|
|
71 // The classes each lane exists to run, and the floor below which its coverage has shrunk. Both are
|
|
72 // declarations rather than observations: a lane that lost a class to a rename, or lost half its
|
|
73 // cases to a filter, otherwise still reports success.
|
|
74 def REDIS_TOPOLOGY_REQUIRED_CLASSES = [
|
|
75 'standalone': ['LiveRedisCompositionTest', 'LiveRedisSemanticPortsTest',
|
|
76 'RedisTopologyContractTest', 'LiveRedisGuardrailTest'],
|
|
77 'sentinel' : ['LiveRedisCompositionTest', 'LiveRedisSentinelPromotionTest',
|
|
78 'RedisTopologyContractTest'],
|
|
79 'cluster' : ['LiveRedisCompositionTest', 'LiveRedisClusterTest',
|
|
80 'LiveRedisClusterTransactionTest', 'LiveRedisSemanticPortsTest'],
|
|
81 'tls' : ['LiveRedisTlsTest'],
|
|
82 ]
|
|
83 def REDIS_TOPOLOGY_MINIMUM_TESTS = ['standalone': 20, 'sentinel': 20, 'cluster': 24, 'tls': 4]
|
|
84
|
|
85 tasks.register('redisTopologyTest', Test) {
|
|
86 description = 'Runs the Redis SDK contracts against a real topology declared in infra/redis-sdk.'
|
|
87 group = 'verification'
|
|
88 testClassesDirs = sourceSets.test.output.classesDirs
|
|
89 classpath = sourceSets.test.runtimeClasspath
|
|
90 // Never up to date. This task's result depends on a server outside the build, so Gradle's
|
|
91 // inputs say nothing about whether it would still pass: re-running it against a lane that was
|
|
92 // restarted, reconfigured, or promoted reports the previous run's verdict as the current one.
|
|
93 // That is the same silent-pass failure mode the fail-closed endpoint check exists to prevent.
|
|
94 outputs.upToDateWhen { false }
|
|
95 def declaredMode = (project.findProperty('redis.topology.mode') ?: 'unset').toString().toLowerCase()
|
|
96 useJUnitPlatform {
|
|
97 includeTags "redis-topology & lane-${declaredMode}".toString()
|
|
98 }
|
|
99 // A filter that matches nothing is a configuration mistake, never a pass.
|
|
100 failOnNoDiscoveredTests = true
|
|
101 ['redis.topology.host', 'redis.topology.port',
|
|
102 'redis.topology.master', 'redis.topology.username', 'redis.topology.password',
|
|
103 'redis.topology.trust-material']
|
|
104 .each { key ->
|
|
105 if (project.hasProperty(key)) {
|
|
106 systemProperty key, project.property(key)
|
|
107 }
|
|
108 }
|
|
109 // The lane name and the deployment mode are different things, and only the TLS lane makes that
|
|
110 // visible: its shape is standalone, so the tests must see `standalone` while the tag filter and
|
|
111 // the required properties come from the lane. Passing the lane name through as the mode would
|
|
112 // fail RedisDeploymentMode.valueOf on a value that is not a topology.
|
|
113 systemProperty 'redis.topology.mode', REDIS_TOPOLOGY_DEPLOYMENT_MODE.getOrDefault(declaredMode, declaredMode)
|
|
114 systemProperty 'redis.topology.tls', (declaredMode == 'tls').toString()
|
|
115
|
|
116 // Executed, not merely reported. `afterTest` fires for a skipped test too, so counting every
|
|
117 // callback meant a lane whose tests all skipped could still satisfy the "ran something" check —
|
|
118 // the exact green-for-nothing this gate exists to prevent, one level further in.
|
|
119 def executed = new java.util.concurrent.atomic.AtomicInteger()
|
|
120 def skipped = new java.util.concurrent.atomic.AtomicInteger()
|
|
121 def classes = java.util.Collections.synchronizedSet(new java.util.LinkedHashSet<String>())
|
|
122 afterTest { descriptor, result ->
|
|
123 if (result.resultType == org.gradle.api.tasks.testing.TestResult.ResultType.SKIPPED) {
|
|
124 skipped.incrementAndGet()
|
|
125 } else {
|
|
126 executed.incrementAndGet()
|
|
127 classes.add(descriptor.className.tokenize('.').last())
|
|
128 }
|
|
129 }
|
|
130
|
|
131 doFirst {
|
|
132 if (!REDIS_TOPOLOGY_MODES.contains(declaredMode)) {
|
|
133 throw new GradleException(
|
|
134 "redisTopologyTest was selected with redis.topology.mode='${declaredMode}'; " +
|
|
135 'the supported modes are ' + REDIS_TOPOLOGY_MODES.sort().join(', ') +
|
|
136 '. An unrecognised mode selects no test and would otherwise report success.')
|
|
137 }
|
|
138 def required = ['redis.topology.host', 'redis.topology.port']
|
|
139 if (declaredMode == 'sentinel') {
|
|
140 required += 'redis.topology.master'
|
|
141 }
|
|
142 if (declaredMode == 'tls') {
|
|
143 // Without the trust material the client would have to disable verification to connect,
|
|
144 // and a TLS lane that trusts anything qualifies nothing.
|
|
145 required += 'redis.topology.trust-material'
|
|
146 }
|
|
147 def missing = required.findAll { !project.hasProperty(it) }
|
|
148 if (!missing.isEmpty()) {
|
|
149 throw new GradleException(
|
|
150 'redisTopologyTest was selected without ' + missing.join(', ') +
|
|
151 '; start a lane from infra/redis-sdk and pass -P<key>=<value>.')
|
|
152 }
|
|
153 // The lane's tag must actually exist in the compiled suite. failOnNoDiscoveredTests catches
|
|
154 // an empty run, but this names the cause — a renamed or deleted lane class — instead of
|
|
155 // leaving an operator to guess whether the filter or the server is at fault.
|
|
156 def laneTag = "lane-${declaredMode}"
|
|
157 def tagged = sourceSets.test.allJava.matching { include '**/*.java' }.files.any { file ->
|
|
158 def text = file.text
|
|
159 text.contains('@Tag("redis-topology")') && text.contains("@Tag(\"${laneTag}\")")
|
|
160 }
|
|
161 if (!tagged) {
|
|
162 throw new GradleException(
|
|
163 "redisTopologyTest found no test class tagged 'redis-topology' and " +
|
|
164 "'${laneTag}'. The ${declaredMode} lane has no coverage to run, so a green " +
|
|
165 'result would prove nothing.')
|
|
166 }
|
|
167 }
|
|
168
|
|
169 doLast {
|
|
170 if (executed.get() < 1) {
|
|
171 throw new GradleException(
|
|
172 "redisTopologyTest completed without executing a single test for the " +
|
|
173 "${declaredMode} lane. A qualification lane that runs nothing must not report " +
|
|
174 'success.')
|
|
175 }
|
|
176 // What a lane must cover, named rather than counted by accident. A tag filter matching one
|
|
177 // trivial class satisfied "ran something" while the class the lane exists for had been
|
|
178 // renamed out of the filter, and nothing said so.
|
|
179 def required = REDIS_TOPOLOGY_REQUIRED_CLASSES[declaredMode]
|
|
180 def absent = required.findAll { !classes.contains(it) }
|
|
181 if (!absent.isEmpty()) {
|
|
182 throw new GradleException(
|
|
183 "redisTopologyTest ran the ${declaredMode} lane without ${absent.join(', ')}. " +
|
|
184 'These classes are what the lane qualifies; a run that skipped them proves ' +
|
|
185 'less than the lane claims.')
|
|
186 }
|
|
187 def floor = REDIS_TOPOLOGY_MINIMUM_TESTS[declaredMode]
|
|
188 if (executed.get() < floor) {
|
|
189 throw new GradleException(
|
|
190 "redisTopologyTest executed ${executed.get()} tests for the ${declaredMode} " +
|
|
191 "lane, below the declared floor of ${floor}. Coverage that silently shrank is " +
|
|
192 'a gate that silently weakened.')
|
|
193 }
|
|
194 if (skipped.get() > 0) {
|
|
195 throw new GradleException(
|
|
196 "redisTopologyTest skipped ${skipped.get()} test(s) on the ${declaredMode} " +
|
|
197 'lane. A qualification lane has no conditional coverage: what it cannot prove ' +
|
|
198 'must not be selected, and what is selected must run.')
|
|
199 }
|
|
200 logger.lifecycle("redisTopologyTest: ${declaredMode} lane executed ${executed.get()} tests.")
|
|
201 }
|
|
202 }
|
|
exit=0
|
|
|
|
=== leaf 밖 소비자 ===
|
|
$ git grep -l 'dev.caskeleton.adapter.outbound.cache' -- . | grep -v '^adapter/outbound/cache-redis/' | sed 's|/src/.*||' | sort | uniq -c
|
|
7 app-bootstrap
|
|
exit=0
|
|
|
|
=== test resources ===
|
|
$ git ls-files 'adapter/outbound/cache-redis/src' | grep -v '\.java$' | grep -v '\.groovy$'
|
|
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
|
|
adapter/outbound/cache-redis/src/test/resources/redis-sdk/golden/order-summary-v1.json
|
|
exit=0
|
|
|