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>
95 lines
6.6 KiB
Plaintext
95 lines
6.6 KiB
Plaintext
# 주제: messaging-spring-boot-starter 가 실제로 만드는 빈 전수와, 만들지 않는 것
|
|
# revision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
|
|
# ---- 진입점 ----
|
|
# src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
|
|
# dev.caskeleton.messaging.autoconfigure.MessagingPlatformRootAutoConfiguration
|
|
# (한 줄. 나머지 설정은 전부 @Import 로 끌려온다.)
|
|
#
|
|
# MessagingPlatformRootAutoConfiguration
|
|
# @ConditionalOnProperty(prefix="app.messaging", name="enabled", havingValue="true")
|
|
# @Import({MessagingCoreAutoConfiguration, MessagingProviderSelection,
|
|
# MessagingReliabilityAutoConfiguration, MessagingAdminAutoConfiguration})
|
|
|
|
# ---- 만드는 @Bean 전수 (38개) ----
|
|
# MessagingCoreAutoConfiguration (23)
|
|
# messagingCredentialRequirementValidator, messagingPrefixMigrationValidator,
|
|
# compiledMessagingConfiguration, messagingConfigurationKeyValidator,
|
|
# validatedDestinationRegistry, destinationProfileValidator, messageSecurityValidator,
|
|
# retryDecisionEngine, deadLetterOrchestrator, messagingRuntimeRegistry,
|
|
# gracefulShutdownCoordinator, brokerSecurityStartupValidation, messagingShutdownLifecycle,
|
|
# messagingRedactor, cardinalityGuard, blockingMessagePublisher, reactiveMessagePublisher,
|
|
# batchMessagePublisher, credentialRuntimeRegistry(@ConditionalOnBean CredentialProvider),
|
|
# brokerTlsPolicy, messagingEndpoint, messagingCodecs, messagingDestinationProfiles,
|
|
# messagingDestinationAccess, messagingAdmission, messagingPublisher, messagingRuntimeInstallation
|
|
# KafkaMessagingAutoConfiguration (7)
|
|
# kafkaProfileValidator, kafkaProfileStartupValidation, kafkaTransactionProfileValidator,
|
|
# kafkaPublishFailureClassifier, kafkaSecurityConfigurer(@ConditionalOnBean),
|
|
# messagingKafkaProducer, messagingKafkaTransport
|
|
# RabbitMessagingAutoConfiguration (4) — 단, 선택 불가 (아래)
|
|
# rabbitProfileValidator, rabbitProfileStartupValidation, rabbitPublishFailureClassifier,
|
|
# rabbitSecurityConfigurer(@ConditionalOnBean)
|
|
# MessagingReliabilityAutoConfiguration (9)
|
|
# outboxProperties, outboxRetryScheduler, outboxRelay(@ConditionalOnBean),
|
|
# outboxRelayWorker(@ConditionalOnBean), outboxRelayLifecycle(@ConditionalOnBean),
|
|
# outboxCleanupJob(@ConditionalOnBean), inboxRetentionPolicy,
|
|
# inboxCleanupJob(@ConditionalOnBean), transactionalInboxHandler(@ConditionalOnBean)
|
|
# MessagingAdminAutoConfiguration (4)
|
|
# destructiveOperationGuard, adminOperationJournal, messagingAdminDurabilityValidator,
|
|
# compositeTopologyValidator(@ConditionalOnBean)
|
|
|
|
# ---- 만들지 않는 것 ----
|
|
# command: git grep -nE "ConsumerRegistrar|DeliveryProcessor|RetryExecutor|DeadLetterPublisher|MessagingConsumer|MessageListener|MessagingObservation|MessagingMetrics" -- src/messaging/messaging-spring-boot-starter/src/main
|
|
# exit: 1 (출력 없음)
|
|
#
|
|
# => 스타터 전체에 컨슈머·배달 처리기·재시도 실행기·DLQ 발행기·관측 구현 빈이 하나도 없다.
|
|
# 확정되는 것:
|
|
# (a) 배포되는 컨텍스트는 발행할 수 있고 소비할 수 없다.
|
|
# (b) MessagingObservation 빈이 없으므로 관측은 no-op 이다.
|
|
# MessagingCoreAutoConfiguration:253 messagingRedactor, :264 cardinalityGuard 는
|
|
# MessagingMetrics 의 두 생성자 협력자인데, MessagingMetrics 자체를 만드는 곳이 없다.
|
|
# messagingPublisher(:439-447)는 DefaultMessagePublisher 의 6-arg 생성자를 쓰고,
|
|
# 그 체인은 NO_OBSERVATION 으로 끝난다(DefaultMessagePublisher.java:126-135).
|
|
# (c) retryDecisionEngine(:167)·deadLetterOrchestrator(:179) 빈은 만들어지되
|
|
# 그 유일한 소비자(KafkaRetryExecutor, Kafka/RabbitDeadLetterPublisher)가 생성되지 않는다.
|
|
|
|
# ---- 브로커 선택: rabbit 은 선택 불가 ----
|
|
# MessagingProviderSelection.java:63-68
|
|
# static final Map<String, String> BROKERS_WITHOUT_A_TRANSPORT =
|
|
# Map.of("rabbit",
|
|
# "the Rabbit adapter ships its validators and security configuration but no "
|
|
# + "MessagingTransport: its native channel publisher is not implemented, so a publish "
|
|
# + "has nothing to travel on");
|
|
# selectedBroker(...):121-131 이 rabbit 을 IllegalStateException 으로 거절한다.
|
|
# => 오늘 선택 가능한 브로커는 kafka 하나다. assemblableBrokerIds() == ["kafka"].
|
|
# RabbitMessagingAutoConfiguration 의 4개 빈은 도달 불가 경로에 있다.
|
|
|
|
# ---- 프로바이더 설정 javadoc 자체 모순 ----
|
|
# KafkaMessagingAutoConfiguration.java:16-17 (첫 문단)
|
|
# "Conditional on the Kafka client being present, so a service that depends on the starter for
|
|
# RabbitMQ alone does not fail to start because a Kafka class is missing."
|
|
# KafkaMessagingAutoConfiguration.java:26-29 (셋째 문단)
|
|
# "Reached only when app.messaging.broker=kafka selected it. The @ConditionalOnClass this used to
|
|
# carry could not select ..."
|
|
# 클래스에 @ConditionalOnClass 가 없다(@Configuration(proxyBeanMethods=false) 뿐).
|
|
# RabbitMessagingAutoConfiguration.java:15-16 / 22-23 도 같은 형태다.
|
|
# => 같은 javadoc 안에서 첫 문단과 셋째 문단이 어긋난다.
|
|
|
|
# ---- 선언된 의존 중 스타터가 import 하지 않는 것 ----
|
|
# command: git grep -n "import dev.caskeleton.messaging.cloudevents|import dev.caskeleton.messaging.claimcheck" -- src/messaging/messaging-spring-boot-starter
|
|
# exit: 1 (출력 없음)
|
|
# build.gradle 은 둘 다 implementation 으로 선언하고 modules.json 의 allowed_dependencies 에도 있다.
|
|
# => messaging-cloudevents 와 messaging-claim-check 이 runtime_memberships:["app-bootstrap"] 을
|
|
# 갖는 이유가 이 선언이며, 스타터는 그 둘의 타입을 하나도 쓰지 않는다.
|
|
# (두 리프의 외부 참조 0건은 각 리프 문서에 기록되어 있다.)
|
|
|
|
# ---- 정리 잡은 빈이지만 스케줄되지 않는다 ----
|
|
# MessagingReliabilityAutoConfiguration.java:32-34 (클래스 javadoc)
|
|
# "The cleanup jobs are beans but no scheduler is registered for them. Scheduling is the
|
|
# application's decision: a service running several replicas usually wants one of them to run
|
|
# cleanup, and auto-registering a fixed-rate task would have every replica delete the same rows."
|
|
# => OutboxCleanupJob / InboxCleanupJob 의 runOnce 를 부르는 프로덕션 코드가 없다.
|
|
# EVD-311 의 무제한 DELETE 는 애플리케이션이 이 잡을 스케줄하는 순간 발동한다.
|
|
# (기본 배포에서는 아무것도 실행되지 않으므로, EVD-311 의 "프로덕션 기본 경로" 표현은
|
|
# "애플리케이션이 문서 지시대로 스케줄하면 발동하는 경로" 로 읽어야 정확하다.)
|