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>
113 lines
8.2 KiB
Plaintext
113 lines
8.2 KiB
Plaintext
Sub-scope 07 (mongo) schema + migration - execution probes
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T00:08:22+00:00
|
|
|
|
Two temporary probe classes were added, run, and removed:
|
|
src/test/.../migration/Ss07FenceProbe.java (@Tag mongodb-migration, real server)
|
|
src/test/.../schema/Ss07SchemaProbe.java (@Tag mongodb-contract, hermetic)
|
|
No production source was modified. git status after removal is shown at the end.
|
|
|
|
=== PROBE A: recordApplied under a stale fence (real MongoDB 8.0 replica set) ===
|
|
Scenario: the live runner holds fence 5 and has stored checkpoint o-900.
|
|
the superseded runner still holds fence 1.
|
|
|
|
PROBE saveCheckpoint(fence=1 over stored 5) -> REFUSED MongoOperationRejectedException
|
|
PROBE recordApplied(fence=1 over stored 5) -> ACCEPTED
|
|
PROBE ledger entry now = Document{{_id=6a937447a11d79601374fd73, migrationId=20260829-001,
|
|
checksum=superseded, operator=stale-runner, appliedAt=Thu Jan 01 00:00:00 UTC 1970, fence=1}}
|
|
PROBE live checkpoint still = o-900
|
|
PROBE recordApplied(live fence=5, after stale wrote) -> REFUSED MongoWriteException:
|
|
E11000 duplicate key error collection: ss07probe.mongoMigrationLedger
|
|
index: migrationId_1 dup key: { migrationId: "20260829-001" }
|
|
|
|
Reading: saveCheckpoint honours the documented fence contract; recordApplied does not.
|
|
The superseded runner wins the ledger and the live runner receives a raw driver
|
|
duplicate-key error rather than the platform's lease sentence. This is the same shape
|
|
the codebase already fixed once inside saveCheckpoint (see its inline comment).
|
|
|
|
=== PROBE B: what the index diff cannot see (hermetic) ===
|
|
Declared: ix_ttl (expireAfter=30d), ix_active (sparse, partialFilter, collation, visible)
|
|
Observed: same names, same keys, same uniqueness; ix_active hidden on the server
|
|
|
|
PROBE diff.isClean() -> true
|
|
PROBE diff.render() -> [] (empty)
|
|
PROBE MongoIndexManifest.expireAfter(1s) built -> PT1S isTtlIndex=true
|
|
|
|
Reading: a TTL retention change, a sparse/partialFilter/collation change and an index
|
|
hidden on the server but declared visible all render as no drift at all.
|
|
|
|
=== PROBE C: what the Flamingock lease can run (hermetic) ===
|
|
PROBE FlamingockLockAdapter.fence() = -1
|
|
PROBE runner.apply(NON-resumable migration, Flamingock lease) -> REFUSED
|
|
MongoOperationRejectedException: this migration lease exposes no fencing token,
|
|
so a stalled runner cannot be excluded; use a fenced lease or run the migration
|
|
inside a maintenance window with the application stopped
|
|
[operation=migration.fence ... category=OPERATION_REJECTED outcome=NOT_SENT]
|
|
|
|
Reading: the migration in this probe returns completed(0) and stores no checkpoint, so it
|
|
is not resumable. FlamingockLockAdapter's javadoc says the runner 'refuses resumable
|
|
migrations under an unfenced lease'; the runner refuses every migration.
|
|
|
|
=== 8.1c is the one production consumer of the manifest registry itself wired? ===
|
|
$ grep -n 'Geospatial\|geo\.' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java
|
|
exit=1
|
|
|
|
$ git grep -n 'new SpringMongoGeospatialOperations' -- 'adapter/outbound/persistence-mongo/src/main'
|
|
exit=1
|
|
|
|
$ git grep -n 'MongoManifestRegistry\|MongoCollectionManifest' -- 'adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:10:import dev.caskeleton.adapter.outbound.mongo.schema.manifest.MongoManifestRegistry;
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:29: private final MongoManifestRegistry manifests;
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java:32: DefaultMongoImperativeExecutor executor, MongoManifestRegistry manifests) {
|
|
exit=0
|
|
|
|
=== 8.1d the migration package's cross-package production consumers ===
|
|
$ git grep -n 'import dev.caskeleton.adapter.outbound.mongo.migration' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/migration/'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java:3:import dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationCheckpoint;
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:6:import dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationCheckpoint;
|
|
exit=0
|
|
|
|
$ grep -n 'MongoMigrationCheckpoint\|MongoMigrationLedger\|MongoMigrationLock\|MongoMigrationRunner' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:6:import dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationCheckpoint;
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:73: public void recordCheckpoint(String tenantKey, MongoMigrationCheckpoint checkpoint) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:85: public Optional<MongoMigrationCheckpoint> checkpointFor(String tenantKey) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:98: private final Map<String, MongoMigrationCheckpoint> byTenant =
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:105: public Optional<MongoMigrationCheckpoint> find(String tenantKey) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:110: public void save(String tenantKey, MongoMigrationCheckpoint checkpoint) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java:3:import dev.caskeleton.adapter.outbound.mongo.migration.MongoMigrationCheckpoint;
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java:21: Optional<MongoMigrationCheckpoint> find(String tenantKey);
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCheckpointStore.java:24: void save(String tenantKey, MongoMigrationCheckpoint checkpoint);
|
|
exit=0
|
|
|
|
=== 8.4b migration lane test count at HEAD ===
|
|
$ ls adapter/outbound/persistence-mongo/build/test-results/mongoMigrationTest/*.xml | head -20
|
|
adapter/outbound/persistence-mongo/build/test-results/mongoMigrationTest/TEST-dev.caskeleton.adapter.outbound.mongo.migration.Ss07FenceProbe.xml
|
|
exit=0
|
|
|
|
$ grep -ho 'tests="[0-9]*" skipped="[0-9]*" failures="[0-9]*" errors="[0-9]*"' adapter/outbound/persistence-mongo/build/test-results/mongoMigrationTest/*.xml
|
|
tests="1" skipped="0" failures="0" errors="0"
|
|
exit=0
|
|
|
|
=== source tree after probe removal ===
|
|
$ git status --short | wc -l
|
|
0
|
|
exit=0
|
|
|
|
$ git status --short
|
|
exit=0
|
|
|
|
|
|
=== 8.4b RE-RUN: full mongoMigrationTest lane at HEAD (after probe removal) ===
|
|
$ ./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --rerun-tasks
|
|
> Task :adapter:outbound:persistence-mongo:testClasses
|
|
> Task :adapter:outbound:persistence-mongo:mongoMigrationTest
|
|
|
|
BUILD SUCCESSFUL in 30s
|
|
13 actionable tasks: 13 executed
|
|
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.0.0/userguide/configuration_cache_enabling.html
|
|
|
|
classes: 1
|
|
tests="8" skipped="0" failures="0" errors="0"
|
|
|
|
git status --short: 0 entries
|