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>
55 lines
3.2 KiB
Plaintext
55 lines
3.2 KiB
Plaintext
# 능력 성분의 아홉째 자리
|
|
33: boolean brokerTransaction,
|
|
# Kafka 가 넘기는 열두 값 (아홉째가 참)
|
|
new MessagingCapabilities(
|
|
true, true, true, true, true, true, true, false, true, false, false, true);
|
|
^^^^ brokerTransaction
|
|
# 그 상수 선언이 프로파일을 참조하는가: 0
|
|
# 이 플래그를 읽는 main 코드: 0
|
|
# 바로 옆 열째 플래그를 읽는 main 코드: DefaultMessagePublisher.java:250: && !transport.capabilities(profile.name()).capabilities().deduplicatedPublish()) {
|
|
# 그 열째가 거짓인 이유
|
|
* <p>Declaring it true means {@code PublishDeduplication} is accepted and silently does nothing —
|
|
* the caller believes the broker is deduplicating and skips the idempotency it would otherwise
|
|
* build. False makes that request a startup failure instead, which is the whole reason the flag
|
|
* exists.
|
|
|
|
# 트랜잭션 검증기가 요구하는 다섯
|
|
30: if (transactionalIdPrefix == null || transactionalIdPrefix.isBlank()) {
|
|
34: if (!brokerProfile.enableIdempotence()) {
|
|
38: if (!"all".equals(brokerProfile.acks())) {
|
|
42: if (brokerProfile.enableAutoCommit()) {
|
|
46: if (profile.externalSideEffectGuarantee() == ExternalSideEffectGuarantee.INBOX_TRANSACTIONAL) {
|
|
if (profile.externalSideEffectGuarantee() == ExternalSideEffectGuarantee.INBOX_TRANSACTIONAL) {
|
|
throw new IllegalArgumentException(
|
|
"a Kafka transaction does not cover a database side effect; use the Inbox alone: "
|
|
+ profile.name().value());
|
|
}
|
|
}
|
|
}
|
|
|
|
# 기동 검증으로 감싸이는 검증기와 직접 불리는 검증기
|
|
KafkaMessagingAutoConfiguration.java:54: public StartupProfileValidation<dev.caskeleton.messaging.kafka.KafkaBrokerProfile>
|
|
MessagingCoreAutoConfiguration.java:216: public StartupProfileValidation<dev.caskeleton.messaging.security.BrokerSecurityProfile>
|
|
RabbitMessagingAutoConfiguration.java:48: public StartupProfileValidation<dev.caskeleton.messaging.rabbit.RabbitBrokerProfile>
|
|
134: new DestinationProfileValidator().validateAll(registered);
|
|
# 그런데 트랜잭션 검증기는 그냥 빈이다
|
|
@Bean
|
|
@ConditionalOnMissingBean
|
|
public KafkaTransactionProfileValidator kafkaTransactionProfileValidator() {
|
|
return new KafkaTransactionProfileValidator();
|
|
}
|
|
|
|
# 자동설정 밖에서 그것을 언급하는 main 코드: 0
|
|
# 그것을 만드는 test 코드: 0
|
|
|
|
# 감쌀 수 없는 이유는 인자 수가 아니다
|
|
private final Consumer<T> validator;
|
|
|
|
StartupProfileValidation(Supplier<List<T>> profiles, Consumer<T> validator) {
|
|
this.profiles = Objects.requireNonNull(profiles, "profiles must not be null");
|
|
this.validator = Objects.requireNonNull(validator, "validator must not be null");
|
|
}
|
|
# 두 번째 인자의 출처 — transactionalIdPrefix 가 나오는 곳: KafkaTransactionProfileValidator.java:21: * @param transactionalIdPrefix the configured transactional id prefix
|
|
KafkaTransactionProfileValidator.java:26: DestinationProfile profile, String transactionalIdPrefix, KafkaBrokerProfile brokerProfile) {
|
|
KafkaTransactionProfileValidator.java:30: if (transactionalIdPrefix == null || transactionalIdPrefix.isBlank()) {
|