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>
47 lines
2.9 KiB
Plaintext
47 lines
2.9 KiB
Plaintext
# 일곱 어댑터의 submit 이 돌려주는 줄 — 비동기로 감싸는 것은 하나뿐이다
|
|
SesNotificationProviderAdapter.java-107- return CompletableFuture.completedFuture(send(submission));
|
|
WebPushNotificationProviderAdapter.java-82- return CompletableFuture.completedFuture(send(submission));
|
|
SmtpNotificationProviderAdapter.java-82- return CompletableFuture.supplyAsync(() -> send(submission), executor);
|
|
TwilioSmsProviderAdapter.java-78- return CompletableFuture.completedFuture(send(submission));
|
|
FcmNotificationProviderAdapter.java-65- return coordinator.submit(List.of(submission)).thenApply(results -> results.get(0));
|
|
ApnsNotificationProviderAdapter.java-76- return CompletableFuture.completedFuture(send(submission));
|
|
WebhookNotificationProviderAdapter.java-109- return CompletableFuture.completedFuture(send(submission));
|
|
|
|
# 게이트웨이가 던지는 자리와 잡는 자리
|
|
47: runtime.adapter().submit(submission).toCompletableFuture().join();
|
|
48: applyHealth(runtime, result);
|
|
50: } catch (CompletionException failure) {
|
|
|
|
# 배차 서비스가 잡는 세 종류 — 인자 예외는 마지막에만 걸린다
|
|
221: dev.caskeleton.application.notification.platform.provider.ProviderCallNotStartedException
|
|
227: dev.caskeleton.application.notification.platform.api.error.NotificationException
|
|
243: } catch (RuntimeException transportFailure) {
|
|
|
|
# 그 마지막 가지가 만드는 값
|
|
241: ProviderExecutionEvidence.responseLost(),
|
|
248: NotificationFailureCode.PROVIDER_RESPONSE_LOST,
|
|
249: FailureCategory.AMBIGUOUS_SUBMISSION,
|
|
251: ProviderExecutionEvidence.responseLost(),
|
|
|
|
# dispatch 한 회차의 순서 — 라우팅은 제출보다 앞이다
|
|
121: RoutingDecision decision = routing.next(routingContext(work, now));
|
|
124: releaseLease(lease);
|
|
141: releaseLease(lease);
|
|
169: submitOutsideTransaction(attempt, profile, contactPoint, content, work);
|
|
175: RetryDecision next = retryPolicy.decide(retryContext(work, recorded, result, profile));
|
|
176: applyNextAction(work, recorded, next, clock.instant(), lease);
|
|
|
|
# 중지가 남기는 상태
|
|
435: case RetryDecision.Stop ignored ->
|
|
436- recipients.transitionHeldBy(
|
|
437- work.recipient().id(),
|
|
438- attempt.submissionOutcome() == SubmissionOutcome.CONFIRMED_ACCEPTED
|
|
439- ? RecipientDeliveryState.COMPLETED
|
|
440- : RecipientDeliveryState.FAILED,
|
|
441- Optional.empty(),
|
|
|
|
# 다음 회차가 청구하는 상태
|
|
JdbcNotificationServingState.java:37: WHERE delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')
|
|
RecipientClaimSql.java:30: + " AND delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')"
|
|
RecipientClaimSql.java:60: + " WHERE delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')"
|