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>
148 lines
12 KiB
Plaintext
148 lines
12 KiB
Plaintext
Sub-scope 03 (httpclient) resilience/** — manifest and probes
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T05:29:08+00:00
|
|
|
|
=== OWNED FILES ===
|
|
11a188e9944de7f9a9176c1cd0b9462b0f5292d4 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AmbiguousFailure.java 21
|
|
2c7865dc11efe442369926498ddaaa807dfd35fa main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptBudget.java 19
|
|
464198c200d6cd5bcd06f92808889fe8e232ebdd main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptBudgetCalculator.java 40
|
|
a93d860a126338067419b976f632df11c356d8da main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptCall.java 13
|
|
26b571da2a14976174819ab073e59c8adf01d376 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptCircuitBreaker.java 69
|
|
cd0790c034022816d975323ee5e6bd7bd011caeb main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptOutcome.java 73
|
|
183de4db6c5fb7e4124d7f1d7f993c510efcf7ef main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptProgress.java 31
|
|
00ca67c6f78446bc50d74eb8cf755177f615eb70 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptProgressTracker.java 75
|
|
617fcae4c19b55e390517e83b6b5af9da8eb7d08 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptRateLimiter.java 29
|
|
d306c441d5ad12c6f78c648e1932d5f8b8e5587a main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptResiliencePipeline.java 135
|
|
b7d3fb2d075b16c28d65a43a2e82804b3253d8d0 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/BackoffStrategy.java 11
|
|
358dfa09be4194c60b49fdc7635c998c880018f9 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/BlockingAttemptBulkhead.java 57
|
|
bc90242c8844b849ea2f2b4c1114511d09a1458a main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/BlockingLogicalCall.java 28
|
|
46848fcd8808370bf0e62a2c550c252bf3d4ed00 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/BlockingRetryCoordinator.java 64
|
|
3ee96a734846f38ba828694397f568cac8d9ab19 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/Deadline.java 33
|
|
04e7d007c360ab3494c5c95d46962fd869d9b990 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DeadlineCalculator.java 22
|
|
ae18bf166bdebc1fe5958a2d461291f01c945b3c main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DeadlineGuard.java 36
|
|
ed8fa2a4c7cefcad29b6f03189117596ad97e4e1 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultExecutionEvidenceClassifier.java 32
|
|
2ad13a74289c71848cfcea369cf94f7e106c493f main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java 133
|
|
3aa9ac4ee7d8631871d40481b8e74d4690d36e74 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ExecutionEvidenceClassifier.java 10
|
|
bd908670ae736f3a3b3d21db5627fd271e5474dd main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ExponentialFullJitterBackoff.java 93
|
|
e94078d67cefa9ec656c3d458385404208bdf49f main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/Http2EvidenceMapper.java 24
|
|
97144eb2b67b5af9715c2786250e2e7fbd9c1666 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/Http2ProtocolEvidence.java 40
|
|
48a3876a7c05766e623bee39d404e6dce91753e1 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/LogicalAdmissionLimiter.java 44
|
|
5d51cdaf82a109c658316aac08e587cbaf26f6e3 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ProtocolEvidence.java 26
|
|
8b0986a8a69a34192f4d1c5535561c0afd56a3b7 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ReactiveLogicalCall.java 28
|
|
93c4ac3e856f73bce071421471518ca7d4dce064 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ReactiveRetryCoordinator.java 59
|
|
a0957ebb899074b2e745601d512a5bf3074f9364 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ResilienceRegistry.java 62
|
|
a7738acb9523d32e8fbf7bb67eded4b97fbb35a5 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ResilienceRejectionRecorder.java 93
|
|
3839bb0fa0c6207550e3f49d3ddc2f8812520791 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryAllowed.java 22
|
|
e63ee2e7151ed63710c3320da88d3db6dbbb6539 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryBudget.java 36
|
|
d55e0406ec42fb7e98141570d7be17a0bb0467d0 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryBudgetSnapshot.java 28
|
|
de6512387689e3308ef6e5cf914c9e16a0e232ff main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryContext.java 77
|
|
82f51add27b2f754d11eabbaeaed601adac91167 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryCoordinator.java 13
|
|
f868279a15690cfbf94a05fb5484110009f9d7cc main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryDecision.java 7
|
|
7b26fd2bd6808da0a5b2e69e24fd87df45f8e086 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryDenied.java 47
|
|
1580a68f10234e8c9dadcdf5d280934b67d6105f main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryEligibilityEngine.java 13
|
|
0f4bb4ee5ab1e2e29b22bafdeea078f5e6f68791 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/Sleeper.java 29
|
|
64959264dae8b30f8010d2952e7b25bd4f4576f2 main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/TokenBucketRetryBudget.java 74
|
|
683ff1f0ff4e6cbae182906cf87083fa86d273ad test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptResiliencePipelineTest.java 87
|
|
3faa418425ef981ffe0cdcce5bd2c833cccfa1ee test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/BlockingRetryCoordinatorTest.java 224
|
|
143e9f13f728815df52b90d067dd9955105ddc64 test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DeadlineCalculatorTest.java 70
|
|
7b2806757d20fe7a95b3c8c9c23dc72cee1b7691 test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ExecutionEvidenceClassifierTest.java 69
|
|
00d62297d10374fe7774a84d83c62356b46c30ab test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/Http2EvidenceMapperTest.java 51
|
|
ca47780a5ec194bc8e287baffc7791345254fe4c test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/ReactiveRetryCoordinatorTest.java 180
|
|
835ebb43f5daf2737c95c4cb2aff0d6f0166b450 test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryBudgetTest.java 59
|
|
0906157f9b630ce1ded435bc6a441b33caedaba8 test/java/dev/caskeleton/adapter/outbound/httpclient/resilience/RetryEligibilityEngineTest.java 205
|
|
|
|
main: 39 test: 8 main LOC: 1746 total
|
|
=== 8.1 회로 permission 반환 경로 ===
|
|
$ grep -n 'releasePermission' -r adapter/outbound/httpclient/src app-bootstrap/src
|
|
exit=1
|
|
|
|
$ grep -n 'tryAcquirePermission\|onCompleted\|release()\|onError\|onSuccess' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptResiliencePipeline.java
|
|
83: if (!circuitBreaker.tryAcquirePermission()) {
|
|
88: if (!rateLimiter.tryAcquirePermission()) {
|
|
94: rateLimiter.onCompleted();
|
|
106: circuitBreaker.onError(System.nanoTime() - started, upstreamFailure.get());
|
|
108: circuitBreaker.onSuccess(System.nanoTime() - started);
|
|
113: circuitBreaker.onError(System.nanoTime() - started, failure);
|
|
119: bulkhead.release();
|
|
120: rateLimiter.onCompleted();
|
|
exit=0
|
|
|
|
$ grep -n 'boolean tryAcquirePermission\|void onSuccess\|void onError\|String state' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/AttemptCircuitBreaker.java
|
|
14: boolean tryAcquirePermission();
|
|
16: void onSuccess(long durationNanos);
|
|
18: void onError(long durationNanos, Throwable failure);
|
|
20: String state();
|
|
25: public boolean tryAcquirePermission() {
|
|
30: public void onSuccess(long durationNanos) {
|
|
35: public void onError(long durationNanos, Throwable failure) {
|
|
40: public String state() {
|
|
49: public boolean tryAcquirePermission() {
|
|
54: public void onSuccess(long durationNanos) {
|
|
59: public void onError(long durationNanos, Throwable failure) {
|
|
64: public String state() {
|
|
exit=0
|
|
|
|
=== 8.2 PARTIAL_RESPONSE 분기 도달성 (후보 추적) ===
|
|
$ grep -rn 'ExecutionEvidence.PARTIAL_RESPONSE' adapter/outbound/httpclient/src/main
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java:40: if (context.evidence() == ExecutionEvidence.PARTIAL_RESPONSE) {
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultExecutionEvidenceClassifier.java:20: return ExecutionEvidence.PARTIAL_RESPONSE;
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/jdk/JdkFailureClassifier.java:83: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/jdk/JdkFailureClassifier.java:103: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/http3/JettyHttp3FailureClassifier.java:41: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/reactor/ReactorFailureClassifier.java:94: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/reactor/ReactorFailureClassifier.java:114: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/apache/ApacheFailureClassifier.java:121: ExecutionEvidence.PARTIAL_RESPONSE,
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/apache/ApacheFailureClassifier.java:136: lastObservedStage, ExecutionEvidence.PARTIAL_RESPONSE, category, reason);
|
|
exit=0
|
|
|
|
$ grep -n 'firstByteDelivered' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultExecutionEvidenceClassifier.java
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java:28: if (context.firstByteDelivered()) {
|
|
adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultExecutionEvidenceClassifier.java:19: if (progress.responseBytesDelivered() > 0 || progress.firstByteDelivered()) {
|
|
exit=0
|
|
|
|
=== 8.3 재시도 결정표의 순서 ===
|
|
$ grep -n 'return RetryDenied\|return AmbiguousFailure\|return RetryAllowed\|case ' adapter/outbound/httpclient/src/main/java/dev/caskeleton/adapter/outbound/httpclient/resilience/DefaultRetryEligibilityEngine.java
|
|
20: return RetryDenied.maxAttempts();
|
|
23: return RetryDenied.budgetExhausted();
|
|
26: return RetryDenied.bodyNotReplayable();
|
|
29: return RetryDenied.responseAlreadyDelivered();
|
|
32: return RetryDenied.runtimeDraining();
|
|
35: return RetryDenied.deadline();
|
|
38: return RetryDenied.permanentFailure(context.failureCategory().name());
|
|
48: return AmbiguousFailure.remoteOutcomeUnknown();
|
|
68: case 408 ->
|
|
73: case 425 -> {
|
|
81: case 429 ->
|
|
85: case 401 ->
|
|
91: case 500 ->
|
|
95: case 502, 503, 504 ->
|
|
105: case POOL_ACQUIRE_TIMEOUT -> RetryAllowed.of("POOL_ACQUIRE_TIMEOUT");
|
|
106: case DNS -> RetryAllowed.of("DNS");
|
|
107: case CONNECT -> RetryAllowed.of("CONNECT");
|
|
108: case PROXY -> RetryAllowed.of("PROXY");
|
|
109: case TLS_TRANSIENT -> RetryAllowed.of("TLS_TRANSIENT");
|
|
110: case REQUEST_WRITE, RESPONSE_TIMEOUT, RESPONSE_TRUNCATED ->
|
|
114: case NONE -> RetryDenied.success();
|
|
115: case CIRCUIT_OPEN, RATE_LIMIT_REJECTED, BULKHEAD_REJECTED ->
|
|
125: return RetryAllowed.of(reason);
|
|
129: return RetryDenied.deadline();
|
|
131: return RetryAllowed.after(reason, retryAfter.get());
|
|
exit=0
|
|
|
|
=== 8.4 test 밀도 ===
|
|
$ git ls-files 'adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/resilience' | xargs -n1 basename
|
|
AttemptResiliencePipelineTest.java
|
|
BlockingRetryCoordinatorTest.java
|
|
DeadlineCalculatorTest.java
|
|
ExecutionEvidenceClassifierTest.java
|
|
Http2EvidenceMapperTest.java
|
|
ReactiveRetryCoordinatorTest.java
|
|
RetryBudgetTest.java
|
|
RetryEligibilityEngineTest.java
|
|
exit=0
|
|
|
|
$ git grep -hc ' void [a-z]' -- 'adapter/outbound/httpclient/src/test/java/dev/caskeleton/adapter/outbound/httpclient/resilience' | paste -sd+ | bc
|
|
main: line 8: bc: command not found
|
|
exit=127
|
|
|