Files
document-haness/docs/clean-architecture-backend-template/final/evidence/rendered/analysis-finding-a05-f030.svg
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

264 lines
54 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="5408" viewBox="0 0 1200 5408" role="img">
<title>terminal evidence</title>
<desc>Terminal-style rendering generated from retained command output. Sensitive-looking values are redacted in the visual asset.</desc>
<rect x="1" y="1" width="1198" height="5406" rx="14" fill="#0d1117" stroke="#30363d"/>
<rect x="1" y="1" width="1198" height="44" rx="14" fill="#161b22"/>
<rect x="1" y="30" width="1198" height="14" fill="#161b22"/>
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
<circle cx="64" cy="22" r="6" fill="#28c840"/>
<text x="92" y="27" fill="#8b949e" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">terminal evidence</text>
<text x="24" y="68" fill="#c9d1d9" font-size="15" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">$ #!/bin/bash
set -euo pipefail
cd /shared/codebase/clean-architecture-backend-template
. /tmp/lib.sh
J=src/adapter/outb</tspan><tspan x="24" dy="22">ound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/notification/platform/JpaAdminOperationSt</tspan><tspan x="24" dy="22">ore.java
S=src/application-core/src/main/java/dev/caskeleton/application/notification/platform/admin/NotificationAdminAp</tspan><tspan x="24" dy="22">plicationService.java
V=$(git ls-files -- &#x27;**/V8__notification_admin_claim.sql&#x27;); V3=$(git ls-files -- &#x27;**/V3__notificat</tspan><tspan x="24" dy="22">ion_platform_inbox_admin.sql&#x27;)
E=$(git ls-files -- &#x27;**/AdminAuditEntity.java&#x27;); T=$(git ls-files -- &#x27;**/AdminOperationCl</tspan><tspan x="24" dy="22">aimContractTest.java&#x27;)
R=$(git ls-files -- &#x27;**/AdminAuditJpaRepository.java&#x27;)
for f in &quot;$J&quot; &quot;$S&quot; &quot;$V&quot; &quot;$V3&quot; &quot;$E&quot; &quot;$T&quot; &quot;$</tspan><tspan x="24" dy="22">R&quot;; do test -f &quot;$f&quot;; done
echo &quot;# 마이그레이션이 만든 것&quot;
at &quot;$V&quot; 1 29 V8__notification_admin_claim.sql
echo &quot; operati</tspan><tspan x="24" dy="22">on_id 의 유일성 제약 :&quot;
hits &#x27;uk_notification_admin_operation|UNIQUE \(operation_id\)&#x27; &quot;$V3&quot; \
| sed -E &#x27;s#^([0-9]+): </tspan><tspan x="24" dy="22">*# V3__notification_platform_inbox_admin.sql:\1 #&#x27;
echo
echo &quot;# 청구가 넣는 행과 저장이 넣는 행&quot;
at &quot;$J&quot; 30 72 JpaA</tspan><tspan x="24" dy="22">dminOperationStore.java
echo
at &quot;$R&quot; 22 47 AdminAuditJpaRepository.java
echo
at &quot;$J&quot; 91 108 JpaAdminOperationStore.java
</tspan><tspan x="24" dy="22">
echo
echo &quot;# 그 청구를 부르는 자리&quot;
printf &#x27; operations.claim 을 부르는 main 줄 : %s\n&#x27; &quot;$({ grep -c &#x27;operations\.clai</tspan><tspan x="24" dy="22">m&#x27; &quot;$S&quot; || true; })&quot;
printf &#x27; [대조] operations.save 를 부르는 main 줄 : %s\n&#x27; &quot;$({ grep -c &#x27;operations\.save&#x27; &quot;$S&quot; || </tspan><tspan x="24" dy="22">true; })&quot;
echo &quot; AdminOperationClaim 을 참조하는 파일 :&quot;
hits &#x27;AdminOperationClaim&#x27; &#x27;src/**/*.java&#x27; | cut -d: -f1 | sor</tspan><tspan x="24" dy="22">t -u | sed &#x27;s#.*/# #&#x27;
echo
echo &quot;# 청구가 넣은 행을 완료로 바꾸는 코드가 있는가&quot;
printf &#x27; 프로덕션에서 그 컬럼을 갱</tspan><tspan x="24" dy="22">신하는 자리 : %s 개\n&#x27; \
&quot;$({ git grep -niE &#x27;UPDATE notification_admin_audit|SET +phase&#x27; -- &#x27;src/**/src/main/**&#x27; || tr</tspan><tspan x="24" dy="22">ue; } | grep -c . || true)&quot;
echo &quot; 그 문장이 나오는 자리 전부 (시험 포함) :&quot;
{ git grep -niE &#x27;UPDATE notification_admin</tspan><tspan x="24" dy="22">_audit|SET +phase&#x27; -- &#x27;src/**&#x27; || true; } \
| sed -E &#x27;s#^src/(.*)/java/.*/([A-Za-z0-9_]+\.java):([0-9]+): *# \2:\3 </tspan><tspan x="24" dy="22"> #&#x27;
echo &quot; AdminAuditEntity 가 가진 필드 :&quot;
E=$(git ls-files -- &#x27;**/AdminAuditEntity.java&#x27;)
{ grep -nE &#x27;^ private &#x27; &quot;$</tspan><tspan x="24" dy="22">E&quot; || true; } | sed -E &#x27;s#^([0-9]+): *# AdminAuditEntity.java:\1 #&#x27;
echo
echo &quot;# 청구 계약 시험이 무엇을 단언하는가</tspan><tspan x="24" dy="22">&quot;
T=$(git ls-files -- &#x27;**/AdminOperationClaimContractTest.java&#x27;)
{ grep -nB1 -E &#x27; void [a-zA-Z]+\(\)&#x27; &quot;$T&quot; || true; } \</tspan><tspan x="24" dy="22">
| sed -E &#x27;s#^([0-9]+)[:-] *# AdminOperationClaimContractTest.java:\1 #&#x27;
printf &#x27; 그 시험 파일에서 COMPLETED 가 나</tspan><tspan x="24" dy="22">오는 줄 : %s 개\n&#x27; &quot;$({ grep -c &#x27;COMPLETED&#x27; &quot;$T&quot; || true; })&quot;
printf &#x27; phase 를 단언하는 줄 : %s 개\n&#x27; &quot;$({ grep -cE &#x27;a</tspan><tspan x="24" dy="22">ssertThat\(phase&#x27; &quot;$T&quot; || true; })&quot;
echo
echo &quot;# 두 연산을 이어 부르면 무엇이 일어나는가&quot;
echo &quot; postgres:16-alpine 에</tspan><tspan x="24" dy="22"> V3 51~62 와 V8 12~15·27~29 를 그대로 적용하고 두 삽입을 이어 실행한다.&quot;
docker rm -f ca-f030 &gt;/dev/null 2&gt;&amp;1 || true
d</tspan><tspan x="24" dy="22">ocker run -d --name ca-f030 -e POSTGRES_PASSWORD=probe postgres:16-alpine &gt;/dev/null
for _ in $(seq 1 30); do docker exe</tspan><tspan x="24" dy="22">c ca-f030 pg_isready -U postgres &gt;/dev/null 2&gt;&amp;1 &amp;&amp; break; sleep 1; done
pg() { docker exec -i ca-f030 psql -q -v ON_ERR</tspan><tspan x="24" dy="22">OR_STOP=0 -U postgres 2&gt;&amp;1 | sed &#x27;s/^/ /&#x27;; }
{ sed -n &#x27;51,62p&#x27; &quot;$V3&quot;; sed -n &#x27;12,15p&#x27; &quot;$V&quot;; sed -n &#x27;27,29p&#x27; &quot;$V&quot;; } |</tspan><tspan x="24" dy="22"> pg
echo &quot; 1. claimOperation 의 삽입 (AdminAuditJpaRepository:34-39)&quot;
echo &quot;INSERT INTO notification_admin_audit ( id, </tspan><tspan x="24" dy="22">operation_id, action, actor_ref, attributes, dry_run, occurred_at, command_fingerprint, phase, claimed_at) VALUES (&#x27;1111</tspan><tspan x="24" dy="22">1111-1111-1111-1111-111111111111&#x27;, &#x27;op-1&#x27;, &#x27;RETRY&#x27;, &#x27;actor-a&#x27;, &#x27;{}&#x27;::jsonb, false, now(), &#x27;fp-a&#x27;, &#x27;CLAIMED&#x27;, now()) ON C</tspan><tspan x="24" dy="22">ONFLICT (operation_id) DO NOTHING;&quot; | pg
echo &quot; 2. save 의 삽입 (JpaAdminOperationStore:96-106) — ids.nextId() 로 새 i</tspan><tspan x="24" dy="22">d, 같은 operation_id&quot;
echo &quot;INSERT INTO notification_admin_audit (id, operation_id, action, actor_ref, reason_code, tena</tspan><tspan x="24" dy="22">nt_id, attributes, dry_run, occurred_at) VALUES (&#x27;22222222-2222-2222-2222-222222222222&#x27;, &#x27;op-1&#x27;, &#x27;RETRY&#x27;, &#x27;actor-a&#x27;, NUL</tspan><tspan x="24" dy="22">L, NULL, &#x27;{}&#x27;::jsonb, false, now());&quot; | pg
echo &quot; 3. 남은 행&quot;
echo &quot;SELECT operation_id, phase, id FROM notification_ad</tspan><tspan x="24" dy="22">min_audit;&quot; | pg
docker rm -f ca-f030 &gt;/dev/null 2&gt;&amp;1 || true
</tspan></text>
<text x="24" y="838" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">cwd: /shared/codebase/clean-architecture-backend-template</tspan></text>
<text x="24" y="860" fill="#8b949e" font-size="13" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">time: 2026-09-04T06:58:09+00:00 · exit 0</tspan></text>
<line x1="24" y1="874" x2="1176" y2="874" stroke="#30363d"/>
<text x="24" y="914" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 마이그레이션이 만든 것</tspan></text>
<text x="24" y="936" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:1 -- NTF-020 — the admin path was find, then side effect, then save.</tspan></text>
<text x="24" y="958" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:2 --</tspan></text>
<text x="24" y="980" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:3 -- Two callers presenting the same operation id both read &quot;not found&quot;, both ran the redrive</tspan><tspan x="24" dy="22">, and</tspan></text>
<text x="24" y="1024" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:4 -- both saved. The unique constraint on operation_id was already there and made the *second</tspan><tspan x="24" dy="22"> save*</tspan></text>
<text x="24" y="1068" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:5 -- fail — after the second side effect had already happened. An idempotency key that is ch</tspan><tspan x="24" dy="22">ecked but</tspan></text>
<text x="24" y="1112" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:6 -- never held prevents a repeat and does nothing about a race, which is the case that matte</tspan><tspan x="24" dy="22">rs for a</tspan></text>
<text x="24" y="1156" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:7 -- destructive operation.</tspan></text>
<text x="24" y="1178" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:8 --</tspan></text>
<text x="24" y="1200" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:9 -- The claim is now the insert itself: ON CONFLICT DO NOTHING means exactly one caller crea</tspan><tspan x="24" dy="22">tes the</tspan></text>
<text x="24" y="1244" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:10 -- row, and everyone else reads what that caller is doing.</tspan></text>
<text x="24" y="1266" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:11 </tspan></text>
<text x="24" y="1288" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:12 ALTER TABLE notification_admin_audit</tspan></text>
<text x="24" y="1310" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:13 ADD COLUMN IF NOT EXISTS command_fingerprint char(64),</tspan></text>
<text x="24" y="1332" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:14 ADD COLUMN IF NOT EXISTS phase varchar(20) NOT NULL DEFAULT &#x27;COMPLETED&#x27;,</tspan></text>
<text x="24" y="1354" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:15 ADD COLUMN IF NOT EXISTS claimed_at timestamptz;</tspan></text>
<text x="24" y="1376" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:16 </tspan></text>
<text x="24" y="1398" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:17 COMMENT ON COLUMN notification_admin_audit.command_fingerprint IS</tspan></text>
<text x="24" y="1420" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:18 &#x27;A digest of the command this operation id was presented with. The same id with a diff</tspan><tspan x="24" dy="22">erent &#x27;</tspan></text>
<text x="24" y="1464" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:19 &#x27;command is a mistake worth reporting, not an idempotent repeat — returning the earli</tspan><tspan x="24" dy="22">er &#x27;</tspan></text>
<text x="24" y="1508" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:20 &#x27;result would silently execute neither command.&#x27;;</tspan></text>
<text x="24" y="1530" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:21 </tspan></text>
<text x="24" y="1552" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:22 COMMENT ON COLUMN notification_admin_audit.phase IS</tspan></text>
<text x="24" y="1574" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:23 &#x27;CLAIMED while a caller holds the operation, COMPLETED once it finished. A row that ex</tspan><tspan x="24" dy="22">ists in &#x27;</tspan></text>
<text x="24" y="1618" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:24 &#x27;CLAIMED tells a second caller that the work is under way rather than done, which is a</tspan><tspan x="24" dy="22"> &#x27;</tspan></text>
<text x="24" y="1662" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:25 &#x27;different answer and needs to be.&#x27;;</tspan></text>
<text x="24" y="1684" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:26 </tspan></text>
<text x="24" y="1706" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:27 ALTER TABLE notification_admin_audit</tspan></text>
<text x="24" y="1728" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:28 ADD CONSTRAINT ck_notification_admin_phase</tspan></text>
<text x="24" y="1750" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> V8__notification_admin_claim.sql:29 CHECK (phase IN (&#x27;CLAIMED&#x27;, &#x27;COMPLETED&#x27;, &#x27;FAILED&#x27;));</tspan></text>
<text x="24" y="1772" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> operation_id 의 유일성 제약 :</tspan></text>
<text x="24" y="1794" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">src/adapter/outbound/persistence-jpa/src/main/resources/db/migration/jpa/notification-platform/V3__notification_platform_inbox_a</tspan><tspan x="24" dy="22">dmin.sql:61: CONSTRAINT uk_notification_admin_operation UNIQUE (operation_id)</tspan></text>
<text x="24" y="1838" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">src/adapter/outbound/persistence-jpa/src/main/resources/db/migration/jpa/notification-platform/V3__notification_platform_inbox_a</tspan><tspan x="24" dy="22">dmin.sql:64:COMMENT ON CONSTRAINT uk_notification_admin_operation ON notification_admin_audit IS</tspan></text>
<text x="24" y="1882" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="1904" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 청구가 넣는 행과 저장이 넣는 행</tspan></text>
<text x="24" y="1926" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:30 </tspan></text>
<text x="24" y="1948" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:31 @Override</tspan></text>
<text x="24" y="1970" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:32 public dev.caskeleton.application.notification.platform.admin.AdminOperationClaim claim(</tspan></text>
<text x="24" y="1992" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:33 String operationId, String commandFingerprint, AdminActor actor, String action) {</tspan></text>
<text x="24" y="2014" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:34 Objects.requireNonNull(operationId, &quot;operationId&quot;);</tspan></text>
<text x="24" y="2036" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:35 Objects.requireNonNull(commandFingerprint, &quot;commandFingerprint&quot;);</tspan></text>
<text x="24" y="2058" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:36 Objects.requireNonNull(actor, &quot;actor&quot;);</tspan></text>
<text x="24" y="2080" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:37 Objects.requireNonNull(action, &quot;action&quot;);</tspan></text>
<text x="24" y="2102" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:38 </tspan></text>
<text x="24" y="2124" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:39 int claimed =</tspan></text>
<text x="24" y="2146" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:40 audits.claimOperation(</tspan></text>
<text x="24" y="2168" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:41 ids.nextId(),</tspan></text>
<text x="24" y="2190" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:42 operationId,</tspan></text>
<text x="24" y="2212" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:43 action,</tspan></text>
<text x="24" y="2234" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:44 actor.actorRef(),</tspan></text>
<text x="24" y="2256" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:45 commandFingerprint,</tspan></text>
<text x="24" y="2278" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:46 clock.instant());</tspan></text>
<text x="24" y="2300" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:47 if (claimed == 1) {</tspan></text>
<text x="24" y="2322" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:48 return dev.caskeleton.application.notification.platform.admin.AdminOperationClaim.claimed</tspan><tspan x="24" dy="22">();</tspan></text>
<text x="24" y="2366" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:49 }</tspan></text>
<text x="24" y="2388" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:50 // Somebody else has the row. What they are doing decides this caller&#x27;s answer.</tspan></text>
<text x="24" y="2410" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:51 List&lt;Object[]&gt; existing = audits.phaseOf(operationId);</tspan></text>
<text x="24" y="2432" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:52 if (existing.isEmpty()) {</tspan></text>
<text x="24" y="2454" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:53 // The row vanished between the insert and the read, which only a concurrent delete expla</tspan><tspan x="24" dy="22">ins.</tspan></text>
<text x="24" y="2498" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:54 // Refusing is safer than assuming the operation is free.</tspan></text>
<text x="24" y="2520" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:55 return dev.caskeleton.application.notification.platform.admin.AdminOperationClaim</tspan></text>
<text x="24" y="2542" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:56 .inProgress();</tspan></text>
<text x="24" y="2564" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:57 }</tspan></text>
<text x="24" y="2586" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:58 String phase = String.valueOf(existing.get(0)[0]);</tspan></text>
<text x="24" y="2608" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:59 String fingerprint = existing.get(0)[1] == null ? null : String.valueOf(existing.get(0)[1])</tspan><tspan x="24" dy="22">;</tspan></text>
<text x="24" y="2652" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:60 if (fingerprint != null &amp;&amp; !fingerprint.equals(commandFingerprint)) {</tspan></text>
<text x="24" y="2674" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:61 // The same id, a different command. Returning the earlier result would execute neither.</tspan></text>
<text x="24" y="2696" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:62 return dev.caskeleton.application.notification.platform.admin.AdminOperationClaim.conflic</tspan><tspan x="24" dy="22">t();</tspan></text>
<text x="24" y="2740" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:63 }</tspan></text>
<text x="24" y="2762" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:64 if (&quot;CLAIMED&quot;.equals(phase)) {</tspan></text>
<text x="24" y="2784" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:65 return dev.caskeleton.application.notification.platform.admin.AdminOperationClaim</tspan></text>
<text x="24" y="2806" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:66 .inProgress();</tspan></text>
<text x="24" y="2828" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:67 }</tspan></text>
<text x="24" y="2850" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:68 return findByOperationId(operationId)</tspan></text>
<text x="24" y="2872" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:69 .map(dev.caskeleton.application.notification.platform.admin.AdminOperationClaim::replay</tspan><tspan x="24" dy="22">)</tspan></text>
<text x="24" y="2916" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:70 .orElseGet(</tspan></text>
<text x="24" y="2938" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:71 dev.caskeleton.application.notification.platform.admin.AdminOperationClaim::inProgr</tspan><tspan x="24" dy="22">ess);</tspan></text>
<text x="24" y="2982" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:72 }</tspan></text>
<text x="24" y="3004" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="3026" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:22 /**</tspan></text>
<text x="24" y="3048" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:23 * Claims an operation id, or reports who already holds it.</tspan></text>
<text x="24" y="3070" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:24 *</tspan></text>
<text x="24" y="3092" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:25 * &lt;p&gt;{@code ON CONFLICT DO NOTHING} is the claim: exactly one caller inserts the row, and t</tspan><tspan x="24" dy="22">he</tspan></text>
<text x="24" y="3136" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:26 * update count tells that caller it won. Everything the admin path used to do — read, act,</tspan><tspan x="24" dy="22"> write</tspan></text>
<text x="24" y="3180" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:27 * — happened between two callers&#x27; reads, so both acted.</tspan></text>
<text x="24" y="3202" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:28 *</tspan></text>
<text x="24" y="3224" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:29 * @return 1 when this caller claimed it, 0 when somebody else already had</tspan></text>
<text x="24" y="3246" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:30 */</tspan></text>
<text x="24" y="3268" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:31 @org.springframework.data.jpa.repository.Modifying</tspan></text>
<text x="24" y="3290" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:32 @org.springframework.data.jpa.repository.Query(</tspan></text>
<text x="24" y="3312" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:33 value =</tspan></text>
<text x="24" y="3334" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:34 &quot;INSERT INTO notification_admin_audit (&quot;</tspan></text>
<text x="24" y="3356" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:35 + &quot; id, operation_id, action, actor_ref, attributes, dry_run, occurred_at,&quot;</tspan></text>
<text x="24" y="3378" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:36 + &quot; command_fingerprint, phase, claimed_at)&quot;</tspan></text>
<text x="24" y="3400" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:37 + &quot; VALUES (:id, :operationId, :action, :actorRef, &#x27;{}&#x27;::jsonb, false, :now,&quot;</tspan></text>
<text x="24" y="3422" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:38 + &quot; :fingerprint, &#x27;CLAIMED&#x27;, :now)&quot;</tspan></text>
<text x="24" y="3444" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:39 + &quot; ON CONFLICT (operation_id) DO NOTHING&quot;,</tspan></text>
<text x="24" y="3466" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:40 nativeQuery = true)</tspan></text>
<text x="24" y="3488" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:41 int claimOperation(</tspan></text>
<text x="24" y="3510" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:42 @org.springframework.data.repository.query.Param(&quot;id&quot;) java.util.UUID id,</tspan></text>
<text x="24" y="3532" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:43 @org.springframework.data.repository.query.Param(&quot;operationId&quot;) String operationId,</tspan></text>
<text x="24" y="3554" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:44 @org.springframework.data.repository.query.Param(&quot;action&quot;) String action,</tspan></text>
<text x="24" y="3576" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:45 @org.springframework.data.repository.query.Param(&quot;actorRef&quot;) String actorRef,</tspan></text>
<text x="24" y="3598" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:46 @org.springframework.data.repository.query.Param(&quot;fingerprint&quot;) String fingerprint,</tspan></text>
<text x="24" y="3620" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditJpaRepository.java:47 @org.springframework.data.repository.query.Param(&quot;now&quot;) java.time.Instant now);</tspan></text>
<text x="24" y="3642" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="3664" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:91 @Override</tspan></text>
<text x="24" y="3686" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:92 public AdminOperationResult save(AdminOperationResult result, AdminActor actor, String action</tspan><tspan x="24" dy="22">) {</tspan></text>
<text x="24" y="3730" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:93 Objects.requireNonNull(result, &quot;result&quot;);</tspan></text>
<text x="24" y="3752" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:94 Objects.requireNonNull(actor, &quot;actor&quot;);</tspan></text>
<text x="24" y="3774" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:95 Objects.requireNonNull(action, &quot;action&quot;);</tspan></text>
<text x="24" y="3796" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:96 audits.saveAndFlush(</tspan></text>
<text x="24" y="3818" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:97 new AdminAuditEntity(</tspan></text>
<text x="24" y="3840" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:98 ids.nextId(),</tspan></text>
<text x="24" y="3862" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:99 result.operationId(),</tspan></text>
<text x="24" y="3884" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:100 action,</tspan></text>
<text x="24" y="3906" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:101 actor.actorRef(),</tspan></text>
<text x="24" y="3928" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:102 result.reasonCodes().isEmpty() ? null : result.reasonCodes().get(0),</tspan></text>
<text x="24" y="3950" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:103 actor.tenantId().map(tenant -&gt; tenant.value()).orElse(null),</tspan></text>
<text x="24" y="3972" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:104 &quot;{}&quot;,</tspan></text>
<text x="24" y="3994" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:105 result.dryRun(),</tspan></text>
<text x="24" y="4016" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:106 clock.instant()));</tspan></text>
<text x="24" y="4038" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:107 return result;</tspan></text>
<text x="24" y="4060" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java:108 }</tspan></text>
<text x="24" y="4082" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="4104" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 그 청구를 부르는 자리</tspan></text>
<text x="24" y="4126" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> operations.claim 을 부르는 main 줄 : 0</tspan></text>
<text x="24" y="4148" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> [대조] operations.save 를 부르는 main 줄 : 4</tspan></text>
<text x="24" y="4170" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaim 을 참조하는 파일 :</tspan></text>
<text x="24" y="4192" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> JpaAdminOperationStore.java</tspan></text>
<text x="24" y="4214" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java</tspan></text>
<text x="24" y="4236" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaim.java</tspan></text>
<text x="24" y="4258" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationStorePort.java</tspan></text>
<text x="24" y="4280" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="4302" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 청구가 넣은 행을 완료로 바꾸는 코드가 있는가</tspan></text>
<text x="24" y="4324" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 프로덕션에서 그 컬럼을 갱신하는 자리 : 0 개</tspan></text>
<text x="24" y="4346" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 문장이 나오는 자리 전부 (시험 포함) :</tspan></text>
<text x="24" y="4368" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:110 &quot;UPDATE notification_admin_audit SET phase = &#x27;ALMOST&#x27; WHERE operation_id = ?&quot;)) {</tspan></text>
<text x="24" y="4390" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity 가 가진 필드 :</tspan></text>
<text x="24" y="4412" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:20 private UUID id;</tspan></text>
<text x="24" y="4434" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:23 private String operationId;</tspan></text>
<text x="24" y="4456" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:26 private String action;</tspan></text>
<text x="24" y="4478" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:29 private String actorRef;</tspan></text>
<text x="24" y="4500" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:32 private String reasonCode;</tspan></text>
<text x="24" y="4522" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:35 private String tenantId;</tspan></text>
<text x="24" y="4544" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:47 private String attributes;</tspan></text>
<text x="24" y="4566" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:50 private boolean dryRun;</tspan></text>
<text x="24" y="4588" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminAuditEntity.java:53 private Instant occurredAt;</tspan></text>
<text x="24" y="4610" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="4632" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 청구 계약 시험이 무엇을 단언하는가</tspan></text>
<text x="24" y="4654" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:54 @BeforeEach</tspan></text>
<text x="24" y="4676" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:55 void migrate() throws SQLException, IOException {</tspan></text>
<text x="24" y="4698" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">--</tspan></text>
<text x="24" y="4720" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:67 @DisplayName(&quot;exactly one of two concurrent claims on the same id wins&quot;)</tspan></text>
<text x="24" y="4742" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:68 void exactlyOneClaimWins() throws SQLException {</tspan></text>
<text x="24" y="4764" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">--</tspan></text>
<text x="24" y="4786" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:76 @DisplayName(&quot;a different operation id claims independently&quot;)</tspan></text>
<text x="24" y="4808" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:77 void aDifferentIdClaimsIndependently() throws SQLException {</tspan></text>
<text x="24" y="4830" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">--</tspan></text>
<text x="24" y="4852" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:83 @DisplayName(&quot;the claim records the phase, so a second caller can tell doing from don</tspan><tspan x="24" dy="22">e&quot;)</tspan></text>
<text x="24" y="4896" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:84 void theClaimRecordsThePhase() throws SQLException {</tspan></text>
<text x="24" y="4918" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">--</tspan></text>
<text x="24" y="4940" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:93 @DisplayName(&quot;the claim records the fingerprint it was made with&quot;)</tspan></text>
<text x="24" y="4962" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:94 void theClaimRecordsItsFingerprint() throws SQLException {</tspan></text>
<text x="24" y="4984" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0">--</tspan></text>
<text x="24" y="5006" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:103 @DisplayName(&quot;the phase check constraint refuses an unknown phase&quot;)</tspan></text>
<text x="24" y="5028" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> AdminOperationClaimContractTest.java:104 void thePhaseConstraintRefusesAnUnknownPhase() throws SQLException {</tspan></text>
<text x="24" y="5050" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 그 시험 파일에서 COMPLETED 가 나오는 줄 : 0 개</tspan></text>
<text x="24" y="5072" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> phase 를 단언하는 줄 : 1 개</tspan></text>
<text x="24" y="5094" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"></tspan></text>
<text x="24" y="5116" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"># 두 연산을 이어 부르면 무엇이 일어나는가</tspan></text>
<text x="24" y="5138" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> postgres:16-alpine 에 V3 51~62 와 V8 12~15·27~29 를 그대로 적용하고 두 삽입을 이어 실행한다.</tspan></text>
<text x="24" y="5160" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 1. claimOperation 의 삽입 (AdminAuditJpaRepository:34-39)</tspan></text>
<text x="24" y="5182" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 2. save 의 삽입 (JpaAdminOperationStore:96-106) — ids.nextId() 로 새 id, 같은 operation_id</tspan></text>
<text x="24" y="5204" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> ERROR: duplicate key value violates unique constraint &quot;uk_notification_admin_operation&quot;</tspan></text>
<text x="24" y="5226" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> DETAIL: Key (operation_id)=(op-1) already exists.</tspan></text>
<text x="24" y="5248" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> 3. 남은 행</tspan></text>
<text x="24" y="5270" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> operation_id | phase | id </tspan></text>
<text x="24" y="5292" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> --------------+---------+--------------------------------------</tspan></text>
<text x="24" y="5314" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> op-1 | CLAIMED | 11111111-1111-1111-1111-111111111111</tspan></text>
<text x="24" y="5336" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> (1 row)</tspan></text>
<text x="24" y="5358" fill="#e6edf3" font-size="14" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" xml:space="preserve"><tspan x="24" dy="0"> </tspan></text>
</svg>