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>
251 lines
23 KiB
Plaintext
251 lines
23 KiB
Plaintext
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
|
|
|
|
=== JpaAdapterComponentsConfig package scan ===
|
|
1 package dev.caskeleton.adapter.outbound.persistence.config;
|
|
2
|
|
3 import org.springframework.context.annotation.ComponentScan;
|
|
4 import org.springframework.context.annotation.Configuration;
|
|
5
|
|
6 /**
|
|
7 * Registers the JPA adapter's scanned components, which nothing registered (JPA-INT-006).
|
|
8 *
|
|
9 * <p>The composition root's {@code @ComponentScan} excludes {@code
|
|
10 * dev.caskeleton.adapter.outbound.persistence.**} by regex, and that exclusion is correct: it is
|
|
11 * what makes an optional capability optional, so a deployment with JPA off assembles no persistence
|
|
12 * beans rather than assembling them and hoping each one remembered to carry the switch.
|
|
13 *
|
|
14 * <p>What was missing is the other half. Eight classes in this leaf are written as scanned
|
|
15 * components — {@code SpringTransactionPort}, {@code PersistenceExceptionTranslator}, {@code
|
|
16 * StandardSqlStateErrorMapping}, {@code DomainContextAuditContextPort}, the idempotency store and
|
|
17 * its reaper, and the outbox store and its reaper — and once the broad scan stopped reaching them,
|
|
18 * nothing else did. They are annotated {@code @Component} and {@code @Repository} and were beans in
|
|
19 * no running application: {@code TransactionPort} in particular had no implementation at all, so
|
|
20 * every use case that opens a transaction had no port to open it with.
|
|
21 *
|
|
22 * <p>It surfaced as an unsatisfied dependency the first time a capability that needs a transaction
|
|
23 * was actually assembled — the notification orchestrator, in the local-notification-ingest lane —
|
|
24 * rather than as anything a unit test could see, because each of these classes is constructed
|
|
25 * directly by its own tests.
|
|
26 *
|
|
27 * <p>So the scan is restored, narrowed to the packages it should always have covered and reachable
|
|
28 * only through {@code PersistenceJpaRootAutoConfiguration}, which carries the JPA master switch.
|
|
29 * Off is still structural.
|
|
30 *
|
|
31 * <p>Two packages are deliberately absent:
|
|
32 *
|
|
33 * <ul>
|
|
34 * <li>{@code ..persistence.fileserver} — gated on its own capability switch, scanned by {@link
|
|
35 * dev.caskeleton.adapter.outbound.persistence.fileserver.FileserverJpaPersistenceConfig};
|
|
36 * <li>{@code ..persistence.notification} — assembled explicitly, bean by bean, by {@code
|
|
37 * NotificationJpaPersistenceFacade}.
|
|
38 * </ul>
|
|
39 *
|
|
40 * <p>Components under these packages keep their own {@code @ConditionalOnProperty} guards; being
|
|
41 * scanned makes them candidates, not unconditional beans.
|
|
42 *
|
|
43 * <p>{@code ..persistence.lock} is in the list for the same reason and with the same history.
|
|
44 * {@code DistributedLockPersistenceConfig} owns both lock providers — the in-process registry and
|
|
45 * the JDBC one — and was registered by nothing but a test calling {@code ctx.register(...)}. So a
|
|
46 * single-instance deployment had no {@code DistributedLockPort} at all, and a multi-instance one
|
|
47 * could not start: the composition root's own {@code DistributedLockConfig} asks for a bean
|
|
48 * qualified {@code jdbcDistributedLock} that only that configuration declares.
|
|
49 *
|
|
50 * <p>Each package's {@code @ConfigurationProperties} type is enabled by a configuration inside that
|
|
51 * same package — {@code JpaTransactionConfig} for {@code JpaTransactionSettings}, {@code
|
|
52 * DistributedLockPersistenceConfig} for {@code LockSettings} — rather than from here. Enabling them
|
|
53 * centrally would give {@code config} an edge to {@code lock} and {@code transaction} that the
|
|
54 * module map does not grant it, and the map is right: this class knows which packages to scan, not
|
|
55 * what is inside them. They need enabling at all because {@code @ConfigurationPropertiesScan}
|
|
56 * excludes this tree as deliberately as {@code @ComponentScan} does.
|
|
57 */
|
|
58 @Configuration(proxyBeanMethods = false)
|
|
59 @ComponentScan(
|
|
60 basePackages = {
|
|
61 "dev.caskeleton.adapter.outbound.persistence.audit",
|
|
62 "dev.caskeleton.adapter.outbound.persistence.failure",
|
|
63 "dev.caskeleton.adapter.outbound.persistence.idempotency",
|
|
64 "dev.caskeleton.adapter.outbound.persistence.lock",
|
|
65 "dev.caskeleton.adapter.outbound.persistence.outbox",
|
|
66 "dev.caskeleton.adapter.outbound.persistence.transaction"
|
|
67 })
|
|
68 public class JpaAdapterComponentsConfig {}
|
|
|
|
=== runtime-role policy production callers ===
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/PostgreSqlRuntimeRoleVerifier.java:57: policy.requireSafe(verify(dataSource));
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicy.java:19:public record DatabaseRolePolicy(Set<String> allowedRoles, SearchPathPolicy searchPathPolicy) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicy.java:55: searchPathPolicy.requireSafe(report.searchPath());
|
|
|
|
=== runtime-role policy all callers ===
|
|
src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/platform/PostgreSqlSecurityContractTest.java:89: new DatabaseRolePolicy(
|
|
src/adapter/outbound/persistence-jpa/src/postgresqlIntegrationTest/java/dev/caskeleton/adapter/outbound/persistence/platform/PostgreSqlSecurityContractTest.java:92: new PostgreSqlRuntimeRoleVerifier().requireSafe(runtime, policy);
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:15: new DatabaseRolePolicy(
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:21: policy.requireSafe(new DatabasePrivilegeReport("app_runtime", "app, pg_catalog", false, false));
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:29: policy.requireSafe(new DatabasePrivilegeReport("app_runtime", "app", true, false)))
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:38: () -> policy.requireSafe(new DatabasePrivilegeReport("postgres", "app", false, false)))
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:48: policy.requireSafe(
|
|
src/adapter/outbound/persistence-jpa/src/test/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicyTest.java:57: policy.requireSafe(new DatabasePrivilegeReport("app_runtime", "\"$user\", app", false, false));
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/PostgreSqlRuntimeRoleVerifier.java:57: policy.requireSafe(verify(dataSource));
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicy.java:19:public record DatabaseRolePolicy(Set<String> allowedRoles, SearchPathPolicy searchPathPolicy) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/security/DatabaseRolePolicy.java:55: searchPathPolicy.requireSafe(report.searchPath());
|
|
|
|
=== runtime role advertised Stable + report semantics ===
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformAutoConfiguration.java:61: CapabilitySupport.stable(JpaCapability.RUNTIME_ROLE_VERIFICATION),
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformAutoConfiguration.java:112: return roleVerifier.verify(dataSource);
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformReport.java:25: boolean runtimeRoleVerified,
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformReport.java:56: privileges != null && !privileges.holdsCreatePrivilege(),
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformReport.java:62: return openInViewDisabled && runtimeRoleVerified;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/autoconfigure/jpa/JpaPlatformRuntimeAutoConfiguration.java:194: public InitializingBean jpaPlatformStartupCheck(
|
|
|
|
=== startup contract docs ===
|
|
## Startup verification
|
|
|
|
`PostgreSqlRuntimeRoleVerifier` asks the *server* what the connection can do:
|
|
|
|
```sql
|
|
select current_user,
|
|
current_setting('search_path'),
|
|
has_schema_privilege(current_user, current_schema(), 'CREATE'),
|
|
has_database_privilege(current_user, current_database(), 'CREATE')
|
|
```
|
|
|
|
Configuration cannot answer this. Effective privileges come from direct grants, inherited role
|
|
memberships, `PUBLIC` grants, and schema ownership, and no reading of a deployment manifest
|
|
reconstructs that combination reliably.
|
|
|
|
Startup fails when the runtime role is not on the allowlist, or holds `CREATE` on the schema or the
|
|
database.
|
|
|
|
## search_path
|
|
|
|
`SearchPathPolicy` is an allowlist. `search_path` decides which schema an unqualified name resolves
|
|
to, so a writable untrusted schema on it — classically `public`, where `CREATE` was granted broadly
|
|
before PostgreSQL 15 — lets a planted table, function, or operator shadow the real one, and the
|
|
application executes it without noticing. `$user` is exempt: only the connected role owns it.
|
|
|
|
Refusing the runtime role `CREATE` closes the same route from the other side.
|
|
|
|
### 41.2 search_path
|
|
|
|
Application role의 `search_path`를 고정하고 untrusted schema의 object resolution을 차단한다. startup verifier가 current_user, current_schema, search_path, schema CREATE privilege를 검사한다.
|
|
|
|
### 41.3 Injection 방어
|
|
|
|
```text
|
|
JPQL/Native values → parameter binding
|
|
Dynamic sort → allowlist
|
|
Dynamic table/column → enum/catalog mapping만
|
|
Entity → API mass binding 금지
|
|
```
|
|
|
|
### 41.4 Secret
|
|
|
|
DB password는 secret manager/workload identity에서 주입하고 config·log·metric에 기록하지 않는다.
|
|
|
|
---
|
|
|
|
## 42. Spring Boot AutoConfiguration
|
|
|
|
### 42.1 Properties
|
|
|
|
```yaml
|
|
backend:
|
|
jpa:
|
|
enabled: true
|
|
require-postgresql: true
|
|
open-in-view: false
|
|
schema-management: VALIDATE
|
|
transaction-profiles: {}
|
|
retry-profiles: {}
|
|
observability:
|
|
hibernate-statistics: true
|
|
sql-parameters: false
|
|
security:
|
|
verify-runtime-role: true
|
|
verify-search-path: true
|
|
```
|
|
|
|
### 42.2 Startup Failures
|
|
|
|
```text
|
|
spring.jpa.open-in-view=true
|
|
prod ddl-auto != validate/none
|
|
unsupported PostgreSQL version
|
|
runtime role has DDL privilege
|
|
migration checksum mismatch
|
|
required transaction profile timeout missing
|
|
Experimental module enabled without feature flag
|
|
```
|
|
|
|
=== outbox composition path ===
|
|
src/app-bootstrap/src/main/resources/application.yml:662: # metrics, or store binding at all; relay-enabled below only decides whether the scheduler runs.
|
|
src/app-bootstrap/src/main/resources/application.yml:666: relay-enabled: ${APP_OUTBOX_RELAY_ENABLED:false}
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxMetrics.java:5:import dev.caskeleton.application.outbox.OutboxStorePort;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxMetrics.java:30: private final OutboxStorePort store;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxMetrics.java:35: public OutboxMetrics(OutboxStorePort store, ObjectProvider<MeterRegistry> meterRegistryProvider) {
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxRelayBrokerRequirementValidator.java:10: * <p>{@code ca-skeleton.outbox.relay-enabled} defaults to true and {@code app.messaging.broker}
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxRelayBrokerRequirementValidator.java:23: private static final String RELAY_KEY = "ca-skeleton.outbox.relay-enabled";
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxRelayScheduler.java:5:import dev.caskeleton.application.outbox.PublishPendingOutboxEventsUseCase;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxRelayScheduler.java:19: private final PublishPendingOutboxEventsUseCase relayUseCase;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxRelayScheduler.java:24: PublishPendingOutboxEventsUseCase relayUseCase, OutboxMetrics metrics, Clock clock) {
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:6:import dev.caskeleton.application.outbox.OutboxStorePort;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:7:import dev.caskeleton.application.outbox.PublishPendingOutboxEventsUseCase;
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:24: * <p>{@code ca-skeleton.outbox.enabled} is the capability switch; {@code relay-enabled} only
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:52: name = "ca-skeleton.outbox.relay-enabled",
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:56: OutboxStorePort store,
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:64: PublishPendingOutboxEventsUseCase relayUseCase =
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:65: new PublishPendingOutboxEventsUseCase(
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/outbox/OutboxConfig.java:95: OutboxStorePort store, ObjectProvider<MeterRegistry> meterRegistryProvider) {
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/activation/CapabilityDependencyValidator.java:50: boolean relay = isOn(environment, "ca-skeleton.outbox.relay-enabled");
|
|
src/app-bootstrap/src/main/java/dev/caskeleton/bootstrap/activation/CapabilityDependencyValidator.java:67: "ca-skeleton.outbox.relay-enabled=true only starts the scheduler for a capability that is "
|
|
src/application-core/src/main/java/dev/caskeleton/application/inbox/InboxStorePort.java:22: InboxTransitionOutcome markDead(InboxOwner owner, Duration retention, OperationId operationId);
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/DefaultCleanupService.java:87: markFailed(item, "BATCH_BYTE_BUDGET_EXHAUSTED", now);
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/DefaultCleanupService.java:123: markFailed(item, "CLEANUP_ATTEMPT_FAILED", now);
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/DefaultCleanupService.java:140: markFailed(item, "ACTIVE_WRITER_LEASE", now);
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/DefaultCleanupService.java:206: private void markFailed(CleanupItem item, String reasonCode, Instant now) {
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/DefaultCleanupService.java:209: queue.markFailed(item, reasonCode, now.plus(retryBackoff));
|
|
src/application-core/src/main/java/dev/caskeleton/application/fileserver/cleanup/CleanupQueue.java:21: void markFailed(CleanupItem item, String reasonCode, Instant nextAttemptAt);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxRelayResult.java:7: * Result returned by {@link PublishPendingOutboxEventsUseCase} after one relay cycle: the number of
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:35:public class PublishPendingOutboxEventsUseCase
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:38: private final OutboxStorePort store;
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:60: public PublishPendingOutboxEventsUseCase(
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:61: OutboxStorePort store,
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:90: List<OutboxEvent> claimed = tx.inWrite(() -> store.claimBatch(batchSize, now, inFlightTimeout));
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:141: tx.inWrite(() -> store.markPublished(event.eventId()));
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:159: tx.inWrite(() -> store.markFailed(event.eventId(), nextAttemptAt));
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/PublishPendingOutboxEventsUseCase.java:175: tx.inWrite(() -> store.markDead(event.eventId()));
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxStorePort.java:14:public interface OutboxStorePort {
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxStorePort.java:27: List<OutboxEvent> claimBatch(int batchSize, Instant now, Duration inFlightTimeout);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxStorePort.java:35: void markPublished(String eventId);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxStorePort.java:45: void markFailed(String eventId, Instant nextAttemptAt);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxStorePort.java:54: void markDead(String eventId);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/OutboxEvent.java:7: * Immutable view of an outbox event row returned by {@link OutboxStorePort#claimBatch}.
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/v2/OutboxPollingDeliveryPortV2.java:9: List<ClaimedOutboxDelivery> claimBatch(OutboxDeliveryClaimRequest request);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/v2/OutboxPollingDeliveryPortV2.java:11: OutboxDeliveryTransitionOutcome markPublished(OutboxDeliveryTransition transition);
|
|
src/application-core/src/main/java/dev/caskeleton/application/outbox/v2/OutboxPollingDeliveryPortV2.java:16: OutboxDeliveryTransitionOutcome markDead(OutboxDeliveryTransition transition, String errorCode);
|
|
src/application-core/src/main/java/dev/caskeleton/application/idempotency/v2/IdempotencyStorePortV2.java:28: IdempotencyFailOutcome markFailed(
|
|
src/application-core/src/main/java/dev/caskeleton/application/idempotency/v2/IdempotencyExecutorV2.java:217: store.markFailed(
|
|
src/application-core/src/main/java/dev/caskeleton/application/idempotency/v2/IdempotencyExecutorV2.java:282: store.markFailed(
|
|
src/application-core/src/main/java/dev/caskeleton/application/notification/platform/callback/ProviderEventLedger.java:26: void markFailed(ProviderEventRecordId eventId, String errorCode);
|
|
src/application-core/src/main/java/dev/caskeleton/application/notification/platform/callback/ProviderEventProjectionService.java:58: ledger.markFailed(event.id(), "NO_PROJECTOR");
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/fileserver/JpaCleanupQueue.java:145: public void markFailed(CleanupItem item, String reasonCode, Instant nextAttemptAt) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:8:import dev.caskeleton.application.outbox.OutboxStorePort;
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:18: * JPA-backed {@link OutboxAppendPort} + {@link OutboxStorePort}. {@link #append} and the claim/mark
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:24:public class OutboxStoreAdapter implements OutboxAppendPort, OutboxStorePort {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:29: public OutboxStoreAdapter(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:53: public List<OutboxEvent> claimBatch(int batchSize, Instant now, Duration inFlightTimeout) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:72: public void markPublished(String eventId) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:84: public void markFailed(String eventId, Instant nextAttemptAt) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/outbox/OutboxStoreAdapter.java:99: public void markDead(String eventId) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/inbox/PostgreSqlSameStoreInboxAdapter.java:350: public InboxTransitionOutcome markDead(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/outbox/PostgreSqlPollingDeliveryAdapter.java:297: public List<ClaimedOutboxDelivery> claimBatch(OutboxDeliveryClaimRequest request) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/outbox/PostgreSqlPollingDeliveryAdapter.java:313: public OutboxDeliveryTransitionOutcome markPublished(OutboxDeliveryTransition transition) {
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/outbox/PostgreSqlPollingDeliveryAdapter.java:366: public OutboxDeliveryTransitionOutcome markDead(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/postgresql/idempotency/PostgreSqlOwnerSafeIdempotencyStore.java:294: public IdempotencyFailOutcome markFailed(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/notification/platform/RecipientDeliveryJpaRepository.java:36: List<Object[]> claimBatch(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/notification/platform/JpaRecipientLeaseStore.java:47: return recipients.claimBatch(now, limit, workerId, leaseUntil).stream()
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/notification/platform/JpaProviderEventLedger.java:146: public void markFailed(ProviderEventRecordId eventId, String errorCode) {
|
|
|
|
=== durable/live adapter construction references ===
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/liveevent/JpaLiveEventReplayAdapter.java:54: public JpaLiveEventReplayAdapter(
|
|
src/adapter/outbound/persistence-jpa/src/main/java/dev/caskeleton/adapter/outbound/persistence/operation/DurableOperationStoreAdapter.java:39: public DurableOperationStoreAdapter(DurableOperationJpaRepository repository) {
|