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>
65 lines
3.0 KiB
Plaintext
65 lines
3.0 KiB
Plaintext
# 인터페이스가 적는 계약
|
|
/**
|
|
* Records a completed migration, only if the fence is still the current one.
|
|
*
|
|
* <p>Conditioned on the fence because the runner that wrote the batches may no longer be the
|
|
* runner that owns the lease. A ledger entry from a superseded runner says a migration completed
|
|
* when the work it describes was overwritten by the runner that replaced it.
|
|
*
|
|
* @param fence the acquisition token from {@link MongoMigrationLock#fence()}
|
|
* @throws dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException when a
|
|
* newer acquisition exists
|
|
*/
|
|
void recordApplied(
|
|
|
|
# 구현이 하는 일
|
|
requireCurrentFence(fence, "ledger entry for " + migrationId.value());
|
|
ledger.insertOne(
|
|
new Document(MIGRATION_ID, migrationId.value())
|
|
.append("checksum", checksum.value())
|
|
.append("operator", operator)
|
|
.append("appliedAt", Date.from(appliedAt))
|
|
.append("fence", fence));
|
|
|
|
# 그 구현이 부르는 검사와 그 검사의 javadoc
|
|
/**
|
|
* Refuses a write from an unfenced lease.
|
|
*
|
|
* <p>An unfenced lease cannot prove the writer is still the owner, and the whole point of the
|
|
* ledger is that it says what actually happened.
|
|
*/
|
|
private static void requireCurrentFence(long fence, String what) {
|
|
if (fence == MongoMigrationLock.UNFENCED) {
|
|
throw MongoOperationRejectedException.of(
|
|
"migration.fence",
|
|
"refusing to write the "
|
|
+ what
|
|
|
|
# 같은 파일의 saveCheckpoint 는 저장된 펜스를 조건으로 건다
|
|
long replaced =
|
|
checkpoints
|
|
.replaceOne(
|
|
Filters.and(
|
|
Filters.eq(MIGRATION_ID, checkpoint.migrationId().value()),
|
|
Filters.or(Filters.exists("fence", false), Filters.lte("fence", fence))),
|
|
document,
|
|
new ReplaceOptions().upsert(false))
|
|
.getMatchedCount();
|
|
|
|
# 그리고 중복 키를 잡아 번역한다. 그렇게 된 이유가 주석에 있다
|
|
// Nor was the refusal itself reachable. With `upsert(true)`, a superseded write matches nothing
|
|
// and MongoDB attempts an insert, which the unique index on migrationId rejects — so a
|
|
// superseded runner got a driver-level duplicate-key error instead of the sentence written for
|
|
// it, and the branch meant to produce that sentence was dead.
|
|
try {
|
|
checkpoints.insertOne(document);
|
|
} catch (MongoWriteException duplicate) {
|
|
if (duplicate.getError().getCategory() != ErrorCategory.DUPLICATE_KEY) {
|
|
throw duplicate;
|
|
}
|
|
// Either the checkpoint was already there and the fence filter excluded it, or another runner
|
|
// inserted between the replace and this insert. Both are the same fact about this runner.
|
|
throw MongoOperationRejectedException.of(
|
|
"migration.fence",
|
|
"a newer migration runner owns the lease; this runner's checkpoint write was refused");
|