Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/a06-f013-recordapplied.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
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>
2026-09-04 22:51:59 +09:00

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");