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>
404 lines
36 KiB
Plaintext
404 lines
36 KiB
Plaintext
Sub-scope 07 (mongo) schema + migration - manifest and negative-space probes
|
|
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
generatedAt=2026-08-30T00:04:41+00:00
|
|
cwd=/shared/codebase/clean-architecture-backend-template/src
|
|
|
|
=== OWNED FILES ===
|
|
0466ca15110d4251cef0759b3c1383b73ee59232 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java 187
|
|
f9170f8288edeab3487c7918852a78f2a6137f65 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLock.java 180
|
|
a3a6d5ecf2ecbd4d05b17e5bf2b03118d60feb8c main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigration.java 30
|
|
769ec5d10816627ba8e887f2f8de8d80826c61c5 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationCheckpoint.java 44
|
|
4682c0cd8dda0b15365ce585b41c65f4067d9125 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationChecksum.java 30
|
|
80296f59836963b7e108804211efec12a42ef373 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationContext.java 81
|
|
47af881f0b4ef203e17c26baff47cd92bd23661f main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationHeartbeat.java 32
|
|
a1f751aefed77a211c0990095170257e9fb54f81 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationId.java 33
|
|
65a2e6cab8f1582372f07b2b042cd38685dde18c main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLedger.java 54
|
|
ca21debac1dd694789d075b0d32508302272145e main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLock.java 49
|
|
5db2ea1cddf4d82df9b0d81893a5487d4c1b53ea main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationPostcondition.java 21
|
|
83f020725259fc9c634d69e2f6c91ccae34b6551 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationPrecondition.java 20
|
|
5718f2152bd32c4aa22fb2edbbbd7941903aa0ac main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationResult.java 58
|
|
b79d234a93e4e9f7755bbfa266f93de6da464f72 main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunner.java 161
|
|
8de0ca45f1d4317726a7b9271810da2e969b4ab5 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockChangeUnitView.java 29
|
|
113c564352c7f7739c1fdc4d731c8cff51d6dcd6 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java 77
|
|
34f128ba5191d0ed026da420e6652cb7699089ee main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java 72
|
|
40127b9342e69c7d7f0f0ad13c690144038bee63 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMigrationConfiguration.java 40
|
|
d9bd3c5db0a9dd35cc4a8412cd7c45135ff9fe96 main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMongoMigrationAdapter.java 33
|
|
b61b21deb96608b84e28577476043652325e90a7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java 49
|
|
3070de52719edfb8b0a22c82feef8557a310de3e main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDescriptorView.java 57
|
|
3a2396275b191162a19c8073a9cb77795374d826 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiff.java 55
|
|
dece468ed334df72d050e849a5982443fc0c8efe main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java 79
|
|
4d8e976dac5191cec7a3024bb4d384bdbc988b72 main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexRetirementPlan.java 60
|
|
be34f2f9c81f314bf7433217da7f4000f0b7238f main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexRetirementState.java 52
|
|
f35defc38fa8521c97413c658cffe9d9b6d75e7f main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoCollectionManifest.java 107
|
|
6e00339723a4bc7d58a00ff90aa39e844b5af38b main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexDirection.java 42
|
|
20ba2c85f7b762c226c1dc067e998694f146e3b6 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexKey.java 20
|
|
3a1533d193354efb63ab7d6e12f57152671dfdde main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java 243
|
|
6ffafda63defa8aa39638518e729030dd44680df main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java 87
|
|
e5000ec35e83b27805e4d3b51ba6c288f2501109 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoMetadataOwnership.java 30
|
|
6c07172745f15eabc446b1a16f119dfa148ff814 main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoSchemaManifest.java 79
|
|
0fde15eab0dc7e0382f9b13afe40296b0c7c9f21 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/EmbeddedCollectionDescriptor.java 60
|
|
b4eb96dda1f12b4b90504998cd08202c4101b631 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoBinaryFieldDescriptor.java 32
|
|
cc6e4463f03567a521a5c36d8c88e1e5375a87f6 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelManifest.java 156
|
|
e186785b79e639218b1157b75d48e8de93f98f76 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelValidator.java 88
|
|
8c91b34f914b3e8c3e2708ba4855d86cf99505f1 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentSizeBudget.java 47
|
|
cf1dd7adbdc171ab8b2cadb190ef74739029017e main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoReferenceDescriptor.java 39
|
|
6c589c888ef8f9f7427ff00b3dafd643c28afea7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoReferenceLifecycle.java 20
|
|
786b5e19d229fc84d2aba1c02a91b5e2615fa633 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoExpirationAccessPolicy.java 38
|
|
2bee1ae545855880eb2165d2551ef57902722b63 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlIndexDescriptor.java 38
|
|
d554d62f7ff110406764d95352bc623dba491bf2 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicy.java 55
|
|
5bf375305d37971eaf43ff3e13d49efab6613138 main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java 94
|
|
9bac323540839409b6b14bac4b649afe39968277 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidationAction.java 26
|
|
8d305f8383dd15bf5245f27a1215c4f57d0be522 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidationLevel.java 21
|
|
7da0133d082e8a1f5d9eca37d0de84fe39fc2a37 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java 102
|
|
56cfc5a4826661d1cdcbe669b171cdd99d8f42c9 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDescriptor.java 36
|
|
03b2f002c9c6dccc0fd91910c7a0b79ea1a13b60 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDiff.java 31
|
|
34ac7d7480bdd093f00188d1a63753f79f1839b7 main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorDiffEngine.java 50
|
|
5dcc5125ed688ba9a84e5950279d766540fe9bae test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationFencingTest.java 238
|
|
8de780e56da2c069cf8ee25f34dc4ecac9d5e525 test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java 357
|
|
be3f250a150cf77231ef1c63bc8a6ebc0a7b4d5f test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunnerTest.java 161
|
|
8aab71567e0f5df28a12445d2ef5cc73ff9bdff5 test/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockMongoMigrationAdapterTest.java 118
|
|
45b9b78653d63943d63432e55ab835b56135d4b4 test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java 112
|
|
e25a040a6f5fa9960e64b8eb484fdf04237cd2a1 test/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistryTest.java 110
|
|
095780bb7e5f55eb5a153c69920d6f6d17bf7024 test/java/dev/caskeleton/adapter/outbound/mongo/schema/model/MongoDocumentModelValidatorTest.java 95
|
|
ee2cd514b9cb28705cd83a09d1e11810fee6171a test/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidatorTest.java 61
|
|
4282df2da997e11d1783655d3915970ffa24bcee test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java 74
|
|
|
|
production: 49
|
|
test: 9
|
|
production LOC: 3124 total
|
|
|
|
=== 8.1 reachability: what the auto-configuration builds from schema/migration ===
|
|
$ grep -n 'schema\.\|migration\.\|Manifest\|Migration\|IndexDiff\|Validator' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java
|
|
4:import dev.caskeleton.adapter.outbound.mongo.api.mapping.MongoTypeRepresentationManifest;
|
|
315: public MongoTypeRepresentationManifest mongoPlatformTypeRepresentationManifest() {
|
|
316: return MongoTypeRepresentationManifest.standard();
|
|
339: ObjectProvider<dev.caskeleton.adapter.outbound.mongo.api.schema.MongoSchemaVersionRange>
|
|
354: new MongoStartupValidator(
|
|
exit=0
|
|
|
|
$ git grep -ln 'schema.manifest\|schema.index\|schema.model\|schema.ttl\|schema.validation' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/schema/'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/encryption/qe/MongoEncryptionMetadataOwnership.java
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/search/MongoSearchIndexDescriptor.java
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/shared/MongoTenantManifestValidator.java
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/api/error/MongoSchemaValidationException.java
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/geo/SpringMongoGeospatialOperations.java
|
|
exit=0
|
|
|
|
$ git grep -ln '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
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java
|
|
exit=0
|
|
|
|
=== 8.1b who consults the apply policies ===
|
|
$ git grep -n 'MongoIndexApplyPolicy\|requireRuntimeApplyAllowed' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java:14:public enum MongoIndexApplyPolicy {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexApplyPolicy.java:38: public void requireRuntimeApplyAllowed(String collection) {
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:77: assertThat(MongoIndexApplyPolicy.REPORT_ONLY.runtimeMayApply()).isFalse();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:78: assertThatThrownBy(() -> MongoIndexApplyPolicy.REPORT_ONLY.requireRuntimeApplyAllowed("orders"))
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:81: assertThat(MongoIndexApplyPolicy.APPLY.runtimeMayApply()).isTrue();
|
|
exit=0
|
|
|
|
$ git grep -n 'MongoValidatorApplyPolicy' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:23:public final class MongoValidatorApplyPolicy {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:37: private MongoValidatorApplyPolicy(String serverVersion) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:42: public static MongoValidatorApplyPolicy forServer(String serverVersion) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicy.java:43: return new MongoValidatorApplyPolicy(Objects.requireNonNull(serverVersion, "serverVersion"));
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:6:import dev.caskeleton.adapter.outbound.mongo.schema.validation.MongoValidatorApplyPolicy;
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:24: assertThat(MongoValidatorApplyPolicy.forServer("8.0.4").certifiedLane())
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:27: assertThat(MongoValidatorApplyPolicy.forServer("7.0.28").certifiedLane()).isTrue();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:33: assertThat(MongoValidatorApplyPolicy.forServer("6.0.19").certifiedLane()).isFalse();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:34: assertThat(MongoValidatorApplyPolicy.forServer("8.1.0").certifiedLane()).isFalse();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/api/capability/MongoServerVersionTest.java:40: assertThat(MongoValidatorApplyPolicy.forServer("unknown").certifiedLane())
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:14:class MongoValidatorApplyPolicyTest {
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:18: MongoValidatorApplyPolicy policy = MongoValidatorApplyPolicy.forServer("8.0");
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:28: MongoValidatorApplyPolicy.forServer("7.0")
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:35: MongoValidatorApplyPolicy policy = MongoValidatorApplyPolicy.forServer("8.0");
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:44: assertThat(MongoValidatorApplyPolicy.forServer("8.0").runtimeMayApply()).isFalse();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:55: MongoValidatorApplyPolicy.forServer("8.0")
|
|
exit=0
|
|
|
|
$ git grep -n 'new MongoIndexDiffEngine\|new MongoValidatorDiffEngine' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngineTest.java:18: private final MongoIndexDiffEngine engine = new MongoIndexDiffEngine();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/schema/validation/MongoValidatorApplyPolicyTest.java:64: new MongoValidatorDiffEngine()
|
|
exit=0
|
|
|
|
$ git grep -n 'MongoManifestRegistry' -- 'adapter/outbound/persistence-mongo/src/main'
|
|
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) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoCollectionManifest.java:93: * <p>Duplicates are accepted here and rejected by {@link MongoManifestRegistry}: a builder that
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:21:public final class MongoManifestRegistry {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:25: private MongoManifestRegistry(Map<String, MongoCollectionManifest> byCollection) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:30: public static MongoManifestRegistry of(MongoCollectionManifest... manifests) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:35: public static MongoManifestRegistry of(Collection<MongoCollectionManifest> manifests) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoManifestRegistry.java:48: return new MongoManifestRegistry(Map.copyOf(byCollection));
|
|
exit=0
|
|
|
|
=== 8.2 recordApplied: the interface contract vs the two implementations ===
|
|
$ grep -n 'only if the fence is still the current one' -A 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLedger.java
|
|
19: * Records a completed migration, only if the fence is still the current one.
|
|
20- *
|
|
21- * <p>Conditioned on the fence because the runner that wrote the batches may no longer be the
|
|
22- * runner that owns the lease. A ledger entry from a superseded runner says a migration completed
|
|
23- * when the work it describes was overwritten by the runner that replaced it.
|
|
24- *
|
|
25- * @param fence the acquisition token from {@link MongoMigrationLock#fence()}
|
|
26- * @throws dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException when a
|
|
27- * newer acquisition exists
|
|
--
|
|
40: * Stores a checkpoint after a completed batch, only if the fence is still the current one.
|
|
41- *
|
|
42- * @param fence the acquisition token from {@link MongoMigrationLock#fence()}
|
|
43- * @throws dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException when a
|
|
44- * newer acquisition exists
|
|
45- */
|
|
46- void saveCheckpoint(MongoMigrationCheckpoint checkpoint, long fence);
|
|
47-
|
|
48- /** One applied migration. */
|
|
exit=0
|
|
|
|
$ grep -n 'public void recordApplied' -A 22 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java
|
|
83: public void recordApplied(
|
|
84- MongoMigrationId migrationId,
|
|
85- MongoMigrationChecksum checksum,
|
|
86- String operator,
|
|
87- Instant appliedAt,
|
|
88- long fence) {
|
|
89- Objects.requireNonNull(migrationId, "migrationId");
|
|
90- Objects.requireNonNull(checksum, "checksum");
|
|
91- Objects.requireNonNull(operator, "operator");
|
|
92- Objects.requireNonNull(appliedAt, "appliedAt");
|
|
93- requireCurrentFence(fence, "ledger entry for " + migrationId.value());
|
|
94- ledger.insertOne(
|
|
95- new Document(MIGRATION_ID, migrationId.value())
|
|
96- .append("checksum", checksum.value())
|
|
97- .append("operator", operator)
|
|
98- .append("appliedAt", Date.from(appliedAt))
|
|
99- .append("fence", fence));
|
|
100- }
|
|
101-
|
|
102- @Override
|
|
103- public Optional<MongoMigrationCheckpoint> findCheckpoint(MongoMigrationId migrationId) {
|
|
104- Objects.requireNonNull(migrationId, "migrationId");
|
|
105- Document found = checkpoints.find(Filters.eq(MIGRATION_ID, migrationId.value())).first();
|
|
exit=0
|
|
|
|
$ grep -n 'public void recordApplied' -A 12 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java
|
|
56: public void recordApplied(
|
|
57- MongoMigrationId migrationId,
|
|
58- MongoMigrationChecksum checksum,
|
|
59- String operator,
|
|
60- Instant appliedAt,
|
|
61- long fence) {
|
|
62- recorded.put(
|
|
63- Objects.requireNonNull(migrationId, "migrationId"),
|
|
64- new AppliedMigration(migrationId, checksum, operator, appliedAt));
|
|
65- }
|
|
66-
|
|
67- @Override
|
|
68- public Optional<MongoMigrationCheckpoint> findCheckpoint(MongoMigrationId migrationId) {
|
|
exit=0
|
|
|
|
$ grep -n 'requireCurrentFence' -A 12 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java
|
|
93: requireCurrentFence(fence, "ledger entry for " + migrationId.value());
|
|
94- ledger.insertOne(
|
|
95- new Document(MIGRATION_ID, migrationId.value())
|
|
96- .append("checksum", checksum.value())
|
|
97- .append("operator", operator)
|
|
98- .append("appliedAt", Date.from(appliedAt))
|
|
99- .append("fence", fence));
|
|
100- }
|
|
101-
|
|
102- @Override
|
|
103- public Optional<MongoMigrationCheckpoint> findCheckpoint(MongoMigrationId migrationId) {
|
|
104- Objects.requireNonNull(migrationId, "migrationId");
|
|
105- Document found = checkpoints.find(Filters.eq(MIGRATION_ID, migrationId.value())).first();
|
|
--
|
|
120: requireCurrentFence(fence, "checkpoint for " + checkpoint.migrationId().value());
|
|
121- // Conditioned on the stored fence as well as matched by id: a superseded runner that wakes up
|
|
122- // mid-batch must not move the checkpoint of the runner that replaced it, which would send the
|
|
123- // live migration back over rows it has already done or forward past rows it has not.
|
|
124- Document document =
|
|
125- new Document(MIGRATION_ID, checkpoint.migrationId().value())
|
|
126- .append("resumePosition", checkpoint.resumePosition())
|
|
127- .append("processedCount", checkpoint.processedCount())
|
|
128- .append("updatedAt", Date.from(checkpoint.updatedAt()))
|
|
129- .append("fence", fence);
|
|
130-
|
|
131- // Replace-then-insert rather than one upsert, and the two cases this separates are the whole
|
|
132- // behaviour of the fence.
|
|
--
|
|
177: private static void requireCurrentFence(long fence, String what) {
|
|
178- if (fence == MongoMigrationLock.UNFENCED) {
|
|
179- throw MongoOperationRejectedException.of(
|
|
180- "migration.fence",
|
|
181- "refusing to write the "
|
|
182- + what
|
|
183- + " under an unfenced lease; a lease with no acquisition token cannot show that this"
|
|
184- + " runner is still the one migrating");
|
|
185- }
|
|
186- }
|
|
187-}
|
|
exit=0
|
|
|
|
=== 8.2b the diff's comparable field set vs what the manifest declares ===
|
|
$ grep -n 'public record MongoIndexManifest' -A 16 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java
|
|
21:public record MongoIndexManifest(
|
|
22- String name,
|
|
23- List<MongoIndexKey> keys,
|
|
24- boolean unique,
|
|
25- boolean sparse,
|
|
26- boolean hidden,
|
|
27- boolean deprecated,
|
|
28- String partialFilterExpression,
|
|
29- String collationProfile,
|
|
30- Duration expireAfter,
|
|
31- String wildcardProjection,
|
|
32- boolean shardKeySupport,
|
|
33- Set<String> expectedUsage,
|
|
34- String owner,
|
|
35- MongoMetadataOwnership metadataOwnership) {
|
|
36-
|
|
37- public MongoIndexManifest {
|
|
exit=0
|
|
|
|
$ grep -n 'public record MongoIndexDescriptorView' -A 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDescriptorView.java
|
|
14:public record MongoIndexDescriptorView(
|
|
15- String collection,
|
|
16- String name,
|
|
17- String keySignature,
|
|
18- boolean unique,
|
|
19- boolean hidden,
|
|
20- MongoMetadataOwnership metadataOwnership) {
|
|
21-
|
|
22- /** MongoDB's own index on {@code _id}. */
|
|
exit=0
|
|
|
|
$ grep -n 'keySignature().equals\|unique() !=\|expireAfter\|sparse\|partialFilter\|collation\|wildcard' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java
|
|
49: if (!actual.keySignature().equals(declared.keySignature())
|
|
50: || actual.unique() != declared.unique()) {
|
|
exit=0
|
|
|
|
=== 8.2c the diff has no case for an index hidden on the server but declared visible ===
|
|
$ grep -n 'hidden' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/index/MongoIndexDiffEngine.java
|
|
55: if (declared.hidden() && !actual.hidden()) {
|
|
exit=0
|
|
|
|
=== 8.3 TTL declared two ways: manifest expireAfter vs MongoTtlPolicy ===
|
|
$ grep -n 'expireAfter' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java
|
|
30: Duration expireAfter,
|
|
54: if (expireAfter != null && expireAfter.isNegative()) {
|
|
73: return Optional.ofNullable(expireAfter);
|
|
78: return expireAfter != null;
|
|
114: private Duration expireAfter;
|
|
189: public Builder expireAfter(Duration retention) {
|
|
190: this.expireAfter = Objects.requireNonNull(retention, "retention");
|
|
235: expireAfter,
|
|
exit=0
|
|
|
|
$ git grep -n 'MongoTtlPolicyValidator\|MongoTtlIndexDescriptor\|MongoTtlPolicy\b' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/schema/ttl/'
|
|
exit=1
|
|
|
|
$ git grep -n 'isTtlIndex\|\.ttl()' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/manifest/MongoIndexManifest.java:77: public boolean isTtlIndex() {
|
|
exit=0
|
|
|
|
$ git grep -n 'MINIMUM_SAFE_RETENTION' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:21: public static final Duration MINIMUM_SAFE_RETENTION = Duration.ofMinutes(1);
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:49: if (!policy.retention().isZero() && policy.retention().compareTo(MINIMUM_SAFE_RETENTION) < 0) {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/schema/ttl/MongoTtlPolicyValidator.java:55: + MINIMUM_SAFE_RETENTION
|
|
exit=0
|
|
|
|
=== 8.3b two ledgers, two locks, and what the unfenced adapter can actually run ===
|
|
$ git grep -n 'implements MongoMigrationLedger\|implements MongoMigrationLock' -- 'adapter/outbound/persistence-mongo/src/main'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java:29:public final class MongoCollectionMigrationLedger implements MongoMigrationLedger {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLock.java:28:public final class MongoCollectionMigrationLock implements MongoMigrationLock {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLedgerAdapter.java:24:public final class FlamingockLedgerAdapter implements MongoMigrationLedger {
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java:17:public final class FlamingockLockAdapter implements MongoMigrationLock {
|
|
exit=0
|
|
|
|
$ grep -n 'refuses resumable migrations under an unfenced lease' -B 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/flamingock/FlamingockLockAdapter.java
|
|
40- return held.get();
|
|
41- }
|
|
42-
|
|
43- /**
|
|
44- * Unfenced: the engine's lock exposes no monotonic token.
|
|
45- *
|
|
46- * <p>Reported honestly rather than synthesised from a local counter, which would look like
|
|
47- * fencing and protect nothing — a per-process counter says nothing about what another process
|
|
48: * acquired. The runner refuses resumable migrations under an unfenced lease for exactly this
|
|
exit=0
|
|
|
|
$ grep -n 'UNFENCED' -A 10 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationRunner.java
|
|
82: if (lock.fence() == MongoMigrationLock.UNFENCED) {
|
|
83- // An unfenced lease cannot tell a live runner from a stalled one that is about to wake up.
|
|
84- // Refused rather than downgraded: the alternative is a certification that says "one runner at
|
|
85- // a time" and a mechanism that cannot show it.
|
|
86- throw MongoOperationRejectedException.of(
|
|
87- "migration.fence",
|
|
88- "this migration lease exposes no fencing token, so a stalled runner cannot be excluded;"
|
|
89- + " use a fenced lease or run the migration inside a maintenance window with the"
|
|
90- + " application stopped");
|
|
91- }
|
|
92-
|
|
exit=0
|
|
|
|
=== 8.3c ensureIndexes: who calls it ===
|
|
$ git grep -n 'ensureIndexes' -- 'adapter/outbound/persistence-mongo/src'
|
|
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoCollectionMigrationLedger.java:62: public void ensureIndexes() {
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:78: ledger.ensureIndexes();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:129: ledger.ensureIndexes();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:152: writer.ensureIndexes();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:178: ledger.ensureIndexes();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:206: ledger.ensureIndexes();
|
|
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/migration/MongoMigrationLaneTest.java:225: ledger.ensureIndexes();
|
|
exit=0
|
|
|
|
=== 8.4 documentation and lane drift ===
|
|
$ grep -rn 'mongoMigrationTest\|mongodb-migration' adapter/outbound/persistence-mongo/build.gradle
|
|
97: 'mongodb-migration',
|
|
119: lane('mongoMigrationTest') {
|
|
120: tag = 'mongodb-migration'
|
|
exit=0
|
|
|
|
$ grep -rn 'mongodb-migration\|mongoMigrationTest' ../docs ../README.md 2>/dev/null | head -30
|
|
../docs/reviews/2026-08-14-mongodb-module-code-review.md:1127:./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --no-daemon --console=plain
|
|
../docs/superpowers/plans/2026-08-15-wave6-final-qualification.md:88: :adapter:outbound:persistence-mongo:mongoMigrationTest \
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave0-baseline.md:163:registered: [mongoReplicaSetTest, mongoFailoverTest, mongoMigrationTest, mongoCompatibilityTest,
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-b5-ghost-release-lanes.md:57:`mongoStableContractTest`, `mongoReplicaSetTest`, `mongoFailoverTest`, `mongoMigrationTest`,
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes.log:22:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:23:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:30:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest FAILED
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:35:Execution failed for task ':adapter:outbound:persistence-mongo:mongoMigrationTest'.
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-lanes-first-run-failed.log:36:> There were failing tests. See the report at: file:///home/donghyeon/workspace/desktop-server-git/clean-architecture-backend-template/src/adapter/outbound/persistence-mongo/build/reports/tests/mongoMigrationTest/index.html
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-migration.log:1:$ ./gradlew :adapter:outbound:persistence-mongo:mongoMigrationTest --console=plain
|
|
../docs/superpowers/plans/evidence/2026-08-15-wave6-final/task2-mongo-migration.log:22:> Task :adapter:outbound:persistence-mongo:mongoMigrationTest
|
|
../docs/superpowers/plans/2026-08-15-wave2-module-on-path.md:594: :adapter:outbound:persistence-mongo:mongoMigrationTest \
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:58:│ ├── mongodb-migration-core/
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:59:│ ├── mongodb-migration-flamingock/
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:109:mongodb-migration-core
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:113:mongodb-migration-flamingock
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:114: → mongodb-migration-core
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:139:- Create: `modules/mongodb/mongodb-migration-core/build.gradle.kts`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:140:- Create: `modules/mongodb/mongodb-migration-flamingock/build.gradle.kts`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:195: "mongodb-geospatial", "mongodb-migration-core",
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:196: "mongodb-migration-flamingock", "mongodb-observability",
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:221:git add 'build-logic/src/main/kotlin/mongodb-library-conventions.gradle.kts' 'modules/mongodb/mongodb-core-api/build.gradle.kts' 'modules/mongodb/mongodb-spring-data/build.gradle.kts' 'modules/mongodb/mongodb-imperative/build.gradle.kts' 'modules/mongodb/mongodb-reactive/build.gradle.kts' 'modules/mongodb/mongodb-query/build.gradle.kts' 'modules/mongodb/mongodb-aggregation/build.gradle.kts' 'modules/mongodb/mongodb-transaction/build.gradle.kts' 'modules/mongodb/mongodb-index-schema/build.gradle.kts' 'modules/mongodb/mongodb-change-stream/build.gradle.kts' 'modules/mongodb/mongodb-geospatial/build.gradle.kts' 'modules/mongodb/mongodb-migration-core/build.gradle.kts' 'modules/mongodb/mongodb-migration-flamingock/build.gradle.kts' 'modules/mongodb/mongodb-observability/build.gradle.kts' 'modules/mongodb/mongodb-security/build.gradle.kts' 'modules/mongodb/mongodb-spring-boot-starter/build.gradle.kts' 'modules/mongodb/mongodb-testkit-core/build.gradle.kts' 'modules/mongodb/mongodb-testkit-replicaset/build.gradle.kts' 'modules/mongodb/mongodb-testkit-failover/build.gradle.kts' 'modules/mongodb/mongodb-testkit-migration/build.gradle.kts' 'settings.gradle.kts' 'build-logic/src/test/java/MongoDbModuleBoundaryTest.java'
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1755:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigration.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1756:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationId.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1757:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationChecksum.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1758:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationLedger.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1759:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationCheckpoint.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1760:- Create: `modules/mongodb/mongodb-migration-core/src/main/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunner.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1761:- Test: `modules/mongodb/mongodb-migration-core/src/test/java/io/backend/skeleton/mongodb/migration/MongoMigrationRunnerTest.java`
|
|
../docs/superpowers/plans/2026-08-11-mongodb-document-persistence-platform-implementation-plan.md:1794:./gradlew :modules:mongodb:mongodb-migration-core:test --tests 'io.backend.skeleton.mongodb.migration.MongoMigrationRunnerTest'
|
|
exit=0
|
|
|
|
$ git grep -n 'schema.manifest\|MongoManifestRegistry\|MongoMigrationRunner' -- 'adapter/outbound/persistence-mongo/README.md' 2>/dev/null
|
|
exit=1
|
|
|
|
$ ls adapter/outbound/persistence-mongo/src/main/resources 2>/dev/null; find adapter/outbound/persistence-mongo/src -name '*.json' -path '*schema*' | head
|
|
META-INF
|
|
exit=0
|
|
|