Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/289-reliability-api-two-generations.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

115 lines
14 KiB
Plaintext

# revision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
## A. this leaf has no test source set at all
main
## B. thirteen public types, files referencing them outside the owning leaf (qualified name)
OutboxRepository 7
OutboxRecord 13
OutboxStatus 7
OutboxLease 6
OutboxTransitionResult 6
OutboxCanonicalMetadata 8
InboxRepository 6
InboxRecord 0
InboxResult 2
ClaimCheckReference 6
IdempotentMessageHandler 1
TransactionalMessageAction 1
ReliableMessagePublisher 0
## C. the two with no consumer
ReliableMessagePublisher NONE
InboxRecord NONE
## D. implementors of the four ports
src/messaging/messaging-inbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/inbox/JdbcInboxRepository.java:30:public final class JdbcInboxRepository implements InboxRepository {
src/messaging/messaging-inbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/inbox/TransactionalInboxHandler.java:31:public final class TransactionalInboxHandler<T> implements IdempotentMessageHandler<T> {
src/messaging/messaging-inbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/inbox/IdempotentConsumerTest.java:91:final class InMemoryInbox implements InboxRepository {
src/messaging/messaging-inbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/inbox/InboxOperationsTest.java:24: private static final class InMemoryInbox implements InboxRepository {
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/JdbcOutboxRepository.java:48:public final class JdbcOutboxRepository implements OutboxRepository {
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxOperationsTest.java:23: private static final class RecordingRepository implements OutboxRepository {
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:223:final class InMemoryOutboxRepository implements OutboxRepository {
src/messaging/messaging-spring-boot-starter/src/test/java/dev/caskeleton/messaging/autoconfigure/MessagingOutboxRelayLifecycleTest.java:109: static final class CountingOutboxRepository implements OutboxRepository {
## E. OutboxRepository carries two generations of the same five transitions
22: void append(OutboxRecord record);
67: void markPublished(MessageId messageId, Instant now);
74: OutboxTransitionResult markPublished(OutboxLease lease, Instant now);
85: void markAmbiguous(MessageId messageId, String failureCode, Instant now);
88: OutboxTransitionResult markAmbiguous(OutboxLease lease, String failureCode, Instant now);
91: OutboxTransitionResult markAmbiguous(
100: OutboxTransitionResult markExhausted(OutboxLease lease, String reason, Instant now);
109: void markFailed(MessageId messageId, String failureCode, Instant now);
112: OutboxTransitionResult markFailed(OutboxLease lease, String failureCode, Instant now);
119: void releaseLease(MessageId messageId);
122: OutboxTransitionResult releaseLease(OutboxLease lease);
138: int purgePublishedBefore(Instant publishedBefore);
151: int purgePublishedBefore(Instant publishedBefore, int limit);
## F. the older generation is called deprecated in prose but is not annotated
43: * inspection paths and is deprecated for the relay's use.
--- any @Deprecated in the leaf?
exit=1 (1 = none)
## G. which generation the messaging relay actually calls
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/OutboxRelay.java:158: repository.claimBatch(owner, batchSize, leaseDuration, now, scheduler.maxAttempts());
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/OutboxRelay.java:171: if (repository.markPublished(lease, now) == OutboxTransitionResult.APPLIED) {
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/OutboxRelay.java:189: .map(reason -> repository.markExhausted(lease, reason, now))
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/OutboxRelay.java:192: repository.markAmbiguous(
src/messaging/messaging-outbox-jdbc-postgresql/src/main/java/dev/caskeleton/messaging/outbox/OutboxRelay.java:205: if (repository.markFailed(
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:92: List<OutboxRecord> leased = repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:111: List<OutboxRecord> first = repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:112: List<OutboxRecord> second = repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:121: repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:124: repository.leaseBatch(10, Duration.ofSeconds(30), NOW.plusSeconds(31));
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:133: repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:135: repository.markAmbiguous(record.messageId(), "CONFIRM_TIMEOUT", NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:139: assertThat(repository.leaseBatch(10, Duration.ofSeconds(30), NOW))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:148: repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:150: repository.markPublished(record.messageId(), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:154: assertThat(repository.leaseBatch(10, Duration.ofSeconds(30), NOW.plusSeconds(60))).isEmpty();
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:161: repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:163: repository.markFailed(record.messageId(), "INVALID_TOPIC", NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:165: assertThat(repository.leaseBatch(10, Duration.ofSeconds(30), NOW.plusSeconds(60))).isEmpty();
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:200: repository.markPublished(published.messageId(), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:321: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:326: repository.claimBatch("relay-b", 10, Duration.ofSeconds(30), NOW.plusSeconds(31));
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:331: assertThat(repository.markPublished(second.get(0), NOW.plusSeconds(32)))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:335: assertThat(repository.markAmbiguous(first.get(0), "CONFIRM_TIMEOUT", NOW.plusSeconds(40)))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:347: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:349: assertThat(repository.markPublished(lease, NOW))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:351: assertThat(repository.markPublished(lease, NOW))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:364: repository.claimBatch("relay-a", 1, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:366: repository.claimBatch("relay-b", 1, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:379: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW).get(0).token();
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:381: repository.claimBatch("relay-b", 10, Duration.ofSeconds(30), NOW.plusSeconds(31)).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:392: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW, 10).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:394: repository.markAmbiguous(lease, "CONFIRM_TIMEOUT", NOW, NOW.plusSeconds(120));
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:396: assertThat(repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW.plusSeconds(1), 10))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:402: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW.plusSeconds(121), 10))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:414: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), clock, 3).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:415: repository.markAmbiguous(lease, "CONFIRM_TIMEOUT", clock, clock);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:419: assertThat(repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), clock, 3))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:429: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW, 10).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:431: repository.markExhausted(lease, "budget spent without a confirmation", NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:437: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW.plusSeconds(600), 10))
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:446: repository.claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW, 10).get(0);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:447: repository.markAmbiguous(first, "CONFIRM_TIMEOUT", NOW, NOW.plusSeconds(60));
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:451: .claimBatch("relay-a", 10, Duration.ofSeconds(30), NOW.plusSeconds(61), 10)
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxPostgresIT.java:453: repository.markPublished(second, NOW.plusSeconds(62));
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:127: assertThat(repository.leaseBatch(10, Duration.ofSeconds(30), NOW)).isEmpty();
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:134: List<OutboxRecord> first = repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:135: List<OutboxRecord> second = repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:144: repository.leaseBatch(10, Duration.ofSeconds(30), NOW);
src/messaging/messaging-outbox-jdbc-postgresql/src/test/java/dev/caskeleton/messaging/outbox/OutboxRelayTest.java:147: repository.leaseBatch(10, Duration.ofSeconds(30), NOW.plusSeconds(31));
## H. OutboxStatus.FAILED means the opposite of the legacy application enum
237: "APPLICATION_DOES_NOT_DEPEND_ON_THE_MESSAGING_PLATFORM: the application owns its "
238- + "publish port and outbox model; a bridge adapter translates, and the two "
239- + "outbox status models mean opposite things under the same names")
240- .allowEmptyShould(true);
241-
242- /**
243- * The two notification models must not learn about each other.