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>
78 lines
4.0 KiB
Plaintext
78 lines
4.0 KiB
Plaintext
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
=== stable scan includes experimental ===
|
|
7- "dev.caskeleton.adapter.outbound.persistence.cache",
|
|
8- "dev.caskeleton.adapter.outbound.persistence.envers",
|
|
9: "dev.caskeleton.adapter.outbound.persistence.experimental",
|
|
10- "dev.caskeleton.adapter.outbound.persistence.failure",
|
|
11- "dev.caskeleton.adapter.outbound.persistence.hibernate",
|
|
--
|
|
30- "dev.caskeleton.adapter.outbound.persistence.cache",
|
|
31- "dev.caskeleton.adapter.outbound.persistence.envers",
|
|
32: "dev.caskeleton.adapter.outbound.persistence.experimental",
|
|
33- "dev.caskeleton.adapter.outbound.persistence.failure",
|
|
34- "dev.caskeleton.adapter.outbound.persistence.hibernate",
|
|
=== opt-in exemptions known by scan coverage test ===
|
|
/** Each opt-in capability: its package, its own configuration, and the switch that gates it. */
|
|
private record OptInCapability(String packageName, Class<?> configuration, String switchPrefix) {}
|
|
|
|
private static final List<OptInCapability> OPT_IN =
|
|
List.of(
|
|
new OptInCapability(
|
|
PersistenceJpaConfig.OPT_IN_NOTIFICATION_PACKAGE,
|
|
NotificationJpaPersistenceConfig.class,
|
|
"ca-skeleton.notification.platform"),
|
|
new OptInCapability(
|
|
PersistenceJpaConfig.OPT_IN_FILESERVER_PACKAGE,
|
|
FileserverJpaPersistenceConfig.class,
|
|
"app.fileserver-platform"));
|
|
|
|
/** Sub-packages that hold no JPA entity or repository and therefore need no scan. */
|
|
private static final Set<String> NOT_SCANNED =
|
|
Set.of(
|
|
// Vendor-selection configuration only.
|
|
"config",
|
|
"h2",
|
|
// The opt-in capabilities, each scanned by its own gated configuration instead.
|
|
"notification",
|
|
"fileserver");
|
|
|
|
=== current experimental JPA stereotypes ===
|
|
=== architecture rule checks type dependency, not scan strings ===
|
|
"the testkit carries ArchUnit and Testcontainers; a production edge would put both on"
|
|
+ " every deployment's runtime classpath")
|
|
.check(PRODUCTION_CLASSES);
|
|
}
|
|
|
|
@Test
|
|
@DisplayName("the Stable platform does not depend on experimental packages")
|
|
void stablePlatformDoesNotDependOnExperimentalPackages() {
|
|
noClasses()
|
|
.that()
|
|
.resideOutsideOfPackage(ROOT + ".experimental..")
|
|
.should()
|
|
.dependOnClassesThat()
|
|
.resideInAPackage(ROOT + ".experimental..")
|
|
.as("experimental capabilities are never reachable from Stable code (plan §3)")
|
|
.because(
|
|
"an experimental capability may change or be withdrawn; a Stable type referencing one"
|
|
+ " gives it a Stable contract nobody agreed to")
|
|
.check(PRODUCTION_CLASSES);
|
|
}
|
|
|
|
@Test
|
|
=== plan constraint ===
|
|
**Architecture:** Experimental module은 Stable `jpa-core-api` 계약만 소비하며 Stable starter에 자동 포함되지 않는다. 각 기능은 명시적 feature flag와 별도 compatibility/failure suite를 요구한다. 실험 결과가 Stable 의미론과 충돌하면 Core를 왜곡하지 않고 capability 또는 별도 profile로 유지한다.
|
|
|
|
**Tech Stack:** Stable 계획의 Java 21·Spring Boot 4.1·PostgreSQL Testcontainers 기반, PostgreSQL RLS, AbstractRoutingDataSource, tenant-specific DataSource registry, Jakarta Persistence 4.0 preview/final compatibility lane, Hibernate ORM 8 compatibility lane, PostgreSQL 19 compatibility lane.
|
|
|
|
## Global Constraints
|
|
|
|
- Stable 계획 Task 1~53이 완료되고 Release Gate가 통과한 뒤 시작한다.
|
|
- 모듈 루트는 `modules/jpa-experimental`이다.
|
|
- Experimental module은 `jpa-spring-boot-starter`의 기본 dependency가 아니다.
|
|
- 모든 기능은 `backend.jpa.experimental.*` feature flag를 요구한다.
|
|
- Tenant ID와 consistency token은 metric label에 기록하지 않는다.
|
|
- Tenant context 누락은 fail-closed다.
|
|
- `readOnly=true`만으로 replica routing하지 않는다.
|
|
- Lock query, write transaction, read-after-write pin은 primary를 사용한다.
|