Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/analysis-finding-a06-f010.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

243 lines
18 KiB
Plaintext

# 레지스트리가 펴는 주장
MongoCollectionProfileRegistry.java:9 /**
MongoCollectionProfileRegistry.java:10 * Maps registered collection profiles to physical collection names (design §16.2, §28).
MongoCollectionProfileRegistry.java:11 *
MongoCollectionProfileRegistry.java:12 * <p>This indirection is what makes "no dynamic collection names" enforceable. Application code
MongoCollectionProfileRegistry.java:13 * names a profile; only this registry knows the physical name, and it is fixed at startup. A
MongoCollectionProfileRegistry.java:14 * collection name assembled from a request value therefore cannot reach the driver, because there
MongoCollectionProfileRegistry.java:15 * is no path from a string to a collection that does not pass through here.
MongoCollectionProfileRegistry.java:16 */
MongoCollectionProfileRegistry.java:17 public final class MongoCollectionProfileRegistry {
MongoCollectionProfileRegistry.java:18
MongoCollectionProfileRegistry.java:19 private final Map<CollectionProfileName, String> physicalNames;
MongoCollectionProfileRegistry.java:20
MongoCollectionProfileRegistry.java:21 private MongoCollectionProfileRegistry(Map<CollectionProfileName, String> physicalNames) {
MongoCollectionProfileRegistry.java:22 this.physicalNames = physicalNames;
MongoCollectionProfileRegistry.java:23 }
MongoCollectionProfileRegistry.java:24
MongoCollectionProfileRegistry.java:25 /** Starts a registry declaration. */
MongoCollectionProfileRegistry.java:26 public static Builder builder() {
MongoCollectionProfileRegistry.java:27 return new Builder();
MongoCollectionProfileRegistry.java:28 }
MongoCollectionProfileRegistry.java:29
MongoCollectionProfileRegistry.java:30 /**
MongoCollectionProfileRegistry.java:31 * The physical collection a profile maps to.
MongoCollectionProfileRegistry.java:32 *
MongoCollectionProfileRegistry.java:33 * @throws MongoOperationRejectedException when the profile was never registered
MongoCollectionProfileRegistry.java:34 */
MongoCollectionProfileRegistry.java:35 public String require(CollectionProfileName profile) {
MongoCollectionProfileRegistry.java:36 Objects.requireNonNull(profile, "profile");
MongoCollectionProfileRegistry.java:37 String physical = physicalNames.get(profile);
MongoCollectionProfileRegistry.java:38 if (physical == null) {
MongoCollectionProfileRegistry.java:39 throw MongoOperationRejectedException.of(
MongoCollectionProfileRegistry.java:40 "collection.profile", "collection profile '" + profile + "' is not registered");
MongoCollectionProfileRegistry.java:41 }
MongoCollectionProfileRegistry.java:42 return physical;
MongoCollectionProfileRegistry.java:43 }
MongoCollectionProfileRegistry.java:44
MongoCollectionProfileRegistry.java:45 /** True when the profile is registered. */
MongoCollectionProfileRegistry.java:46 public boolean isRegistered(CollectionProfileName profile) {
MongoCollectionProfileRegistry.java:47 return physicalNames.containsKey(Objects.requireNonNull(profile, "profile"));
MongoCollectionProfileRegistry.java:48 }
# 그 주장을 지키는 쪽
DefaultMongoImperativeExecutor.java:80
DefaultMongoImperativeExecutor.java:81 String physicalCollection = collections.require(context.collectionProfile());
DefaultMongoImperativeExecutor.java:82 MongoOperations operations = consistency.templateFor(context.consistency());
DefaultMongoImperativeExecutor.java:83 ScopedAccess access = new ScopedAccess(physicalCollection, operations, context.timeout());
DefaultMongoImperativeExecutor.java:84
require 를 부르는 main 줄 :
SpringReactiveChangeStreamSource.java:47 .watchCollection(collections.require(subscription.collectionProfile()))
DefaultMongoImperativeExecutor.java:81 String physicalCollection = collections.require(context.collectionProfile());
DefaultReactiveMongoExecutor.java:130 String physicalCollection = collections.require(context.collectionProfile());
# 컨텍스트가 이미 들고 있는 것
MongoOperationContext.java:1 package dev.caskeleton.adapter.outbound.mongo.api;
MongoOperationContext.java:2
MongoOperationContext.java:3 import dev.caskeleton.adapter.outbound.mongo.api.consistency.MongoConsistencyProfile;
MongoOperationContext.java:4 import java.time.Duration;
MongoOperationContext.java:5 import java.util.Objects;
MongoOperationContext.java:6
MongoOperationContext.java:7 /**
MongoOperationContext.java:8 * Immutable execution context every platform operation requires (design §7.1).
MongoOperationContext.java:9 *
MongoOperationContext.java:10 * <p>The context is deliberately the only way to reach an execution path: it forces the caller to
MongoOperationContext.java:11 * name the operation, the database and collection profiles, the consistency guarantee it is asking
MongoOperationContext.java:12 * for, and a positive timeout. Nothing here identifies a document, a tenant or a user, so the whole
MongoOperationContext.java:13 * context can be attached to telemetry without a redaction step.
MongoOperationContext.java:14 */
MongoOperationContext.java:15 public record MongoOperationContext(
MongoOperationContext.java:16 MongoOperationName operationName,
MongoOperationContext.java:17 DatabaseProfileName databaseProfile,
MongoOperationContext.java:18 CollectionProfileName collectionProfile,
MongoOperationContext.java:19 MongoConsistencyProfile consistency,
MongoOperationContext.java:20 Duration timeout) {
MongoOperationContext.java:21
MongoOperationContext.java:22 public MongoOperationContext {
MongoOperationContext.java:23 Objects.requireNonNull(operationName, "operationName");
MongoOperationContext.java:24 Objects.requireNonNull(databaseProfile, "databaseProfile");
MongoOperationContext.java:25 Objects.requireNonNull(collectionProfile, "collectionProfile");
MongoOperationContext.java:26 Objects.requireNonNull(consistency, "consistency");
MongoOperationContext.java:27 Objects.requireNonNull(timeout, "timeout");
MongoOperationContext.java:28 if (timeout.isZero() || timeout.isNegative()) {
MongoOperationContext.java:29 throw new IllegalArgumentException("MongoDB operation timeout must be positive");
MongoOperationContext.java:30 }
MongoOperationContext.java:31 }
MongoOperationContext.java:32
MongoOperationContext.java:33 /** Convenience factory for the common case where profile names are plain registered strings. */
MongoOperationContext.java:34 public static MongoOperationContext of(
MongoOperationContext.java:35 String operationName,
MongoOperationContext.java:36 String databaseProfile,
MongoOperationContext.java:37 String collectionProfile,
MongoOperationContext.java:38 MongoConsistencyProfile consistency,
MongoOperationContext.java:39 Duration timeout) {
MongoOperationContext.java:40 return new MongoOperationContext(
MongoOperationContext.java:41 new MongoOperationName(operationName),
MongoOperationContext.java:42 new DatabaseProfileName(databaseProfile),
MongoOperationContext.java:43 new CollectionProfileName(collectionProfile),
MongoOperationContext.java:44 consistency,
MongoOperationContext.java:45 timeout);
MongoOperationContext.java:46 }
# 집계 실행기의 서명
PolicyAwareMongoAggregationExecutor.java:24 public final class PolicyAwareMongoAggregationExecutor {
PolicyAwareMongoAggregationExecutor.java:25
PolicyAwareMongoAggregationExecutor.java:26 private final MongoOperations operations;
PolicyAwareMongoAggregationExecutor.java:27
PolicyAwareMongoAggregationExecutor.java:28 private final MongoBudgetPolicyRegistry budgets;
PolicyAwareMongoAggregationExecutor.java:29
PolicyAwareMongoAggregationExecutor.java:30 private final MongoBudgetEnforcer enforcer;
PolicyAwareMongoAggregationExecutor.java:31
PolicyAwareMongoAggregationExecutor.java:32 public PolicyAwareMongoAggregationExecutor(
PolicyAwareMongoAggregationExecutor.java:33 MongoOperations operations, MongoBudgetPolicyRegistry budgets, MongoBudgetEnforcer enforcer) {
PolicyAwareMongoAggregationExecutor.java:34 this.operations = Objects.requireNonNull(operations, "operations");
PolicyAwareMongoAggregationExecutor.java:35 this.budgets = Objects.requireNonNull(budgets, "budgets");
PolicyAwareMongoAggregationExecutor.java:36 this.enforcer = Objects.requireNonNull(enforcer, "enforcer");
PolicyAwareMongoAggregationExecutor.java:37 }
PolicyAwareMongoAggregationExecutor.java:38
PolicyAwareMongoAggregationExecutor.java:39 /**
PolicyAwareMongoAggregationExecutor.java:40 * Validates a plan against a profile without executing it.
PolicyAwareMongoAggregationExecutor.java:41 *
PolicyAwareMongoAggregationExecutor.java:42 * <p>Separate from execution so a startup check or a test can prove a pipeline is admissible
PolicyAwareMongoAggregationExecutor.java:43 * without a server.
PolicyAwareMongoAggregationExecutor.java:44 */
PolicyAwareMongoAggregationExecutor.java:45 public void validate(MongoAggregationPlan plan, MongoAggregationProfile profile) {
PolicyAwareMongoAggregationExecutor.java:46 Objects.requireNonNull(plan, "plan");
PolicyAwareMongoAggregationExecutor.java:47 Objects.requireNonNull(profile, "profile");
PolicyAwareMongoAggregationExecutor.java:48 profile.requireStageCount(plan.stages().size());
PolicyAwareMongoAggregationExecutor.java:49 plan.stages().forEach(profile::requireAllowed);
PolicyAwareMongoAggregationExecutor.java:50 plan.lookupCollections().forEach(profile::requireLookupCollection);
PolicyAwareMongoAggregationExecutor.java:51 }
PolicyAwareMongoAggregationExecutor.java:52
PolicyAwareMongoAggregationExecutor.java:53 /**
PolicyAwareMongoAggregationExecutor.java:54 * Executes a typed aggregation.
PolicyAwareMongoAggregationExecutor.java:55 *
PolicyAwareMongoAggregationExecutor.java:56 * @throws MongoOperationRejectedException when a stage, lookup, budget or result size is not
PolicyAwareMongoAggregationExecutor.java:57 * permitted
PolicyAwareMongoAggregationExecutor.java:58 */
PolicyAwareMongoAggregationExecutor.java:59 public <T> List<T> execute(
PolicyAwareMongoAggregationExecutor.java:60 MongoOperationContext context,
PolicyAwareMongoAggregationExecutor.java:61 MongoAggregationProfile profile,
PolicyAwareMongoAggregationExecutor.java:62 MongoAggregationPlan plan,
PolicyAwareMongoAggregationExecutor.java:63 String collection,
PolicyAwareMongoAggregationExecutor.java:64 Class<T> outputType) {
PolicyAwareMongoAggregationExecutor.java:65 Objects.requireNonNull(context, "context");
PolicyAwareMongoAggregationExecutor.java:66 Objects.requireNonNull(collection, "collection");
PolicyAwareMongoAggregationExecutor.java:67 Objects.requireNonNull(outputType, "outputType");
PolicyAwareMongoAggregationExecutor.java:68 validate(plan, profile);
PolicyAwareMongoAggregationExecutor.java:69
PolicyAwareMongoAggregationExecutor.java:70 MongoOperationBudget budget = budgets.require(context.operationName());
PolicyAwareMongoAggregationExecutor.java:71 MongoOperationBudget effective =
PolicyAwareMongoAggregationExecutor.java:72 enforcer.narrow(budget, budget.narrowedTo(budgetFor(context, budget)));
PolicyAwareMongoAggregationExecutor.java:73
PolicyAwareMongoAggregationExecutor.java:74 Aggregation aggregation =
PolicyAwareMongoAggregationExecutor.java:75 Aggregation.newAggregation(plan.operations()).withOptions(optionsFor(profile, effective));
PolicyAwareMongoAggregationExecutor.java:76 AggregationResults<T> results = operations.aggregate(aggregation, collection, outputType);
PolicyAwareMongoAggregationExecutor.java:77 List<T> mapped = results.getMappedResults();
PolicyAwareMongoAggregationExecutor.java:78 if (mapped.size() > effective.maxResults()) {
PolicyAwareMongoAggregationExecutor.java:79 throw MongoOperationRejectedException.of(
PolicyAwareMongoAggregationExecutor.java:80 "aggregation.result",
PolicyAwareMongoAggregationExecutor.java:81 "the aggregation returned "
PolicyAwareMongoAggregationExecutor.java:82 + mapped.size()
PolicyAwareMongoAggregationExecutor.java:83 + " documents, above the budget of "
PolicyAwareMongoAggregationExecutor.java:84 + effective.maxResults());
PolicyAwareMongoAggregationExecutor.java:85 }
PolicyAwareMongoAggregationExecutor.java:86 return mapped;
PolicyAwareMongoAggregationExecutor.java:87 }
PolicyAwareMongoAggregationExecutor.java:88
PolicyAwareMongoAggregationExecutor.java:89 private static MongoOperationBudget budgetFor(
PolicyAwareMongoAggregationExecutor.java:90 MongoOperationContext context, MongoOperationBudget registered) {
PolicyAwareMongoAggregationExecutor.java:91 // The context's timeout is the caller's deadline; it may tighten maxTimeMS but never extend it.
PolicyAwareMongoAggregationExecutor.java:92 long contextMillis = Math.max(1L, context.timeout().toMillis());
PolicyAwareMongoAggregationExecutor.java:93 return new MongoOperationBudget(
PolicyAwareMongoAggregationExecutor.java:94 registered.maxResults(),
PolicyAwareMongoAggregationExecutor.java:95 registered.maxResultBytes(),
PolicyAwareMongoAggregationExecutor.java:96 Math.min(registered.maxTimeMillis(), contextMillis),
PolicyAwareMongoAggregationExecutor.java:97 registered.cursorBatchSize());
PolicyAwareMongoAggregationExecutor.java:98 }
# 그 실행기가 레지스트리를 아는가
이 파일이 MongoCollectionProfileRegistry 를 언급하는 줄 : 0 개
이 파일이 context.collectionProfile() 을 부르는 줄 : 0 개
[대조] 같은 두 grep 을 블로킹 실행기에 걸면 : 2 개 · 1 개
이 파일의 import 전부 :
:3 import dev.caskeleton.adapter.outbound.mongo.api.MongoOperationContext;
:4 import dev.caskeleton.adapter.outbound.mongo.api.error.MongoOperationRejectedException;
:5 import dev.caskeleton.adapter.outbound.mongo.query.budget.MongoBudgetEnforcer;
:6 import dev.caskeleton.adapter.outbound.mongo.query.budget.MongoBudgetPolicyRegistry;
:7 import dev.caskeleton.adapter.outbound.mongo.query.budget.MongoOperationBudget;
:8 import java.time.Duration;
:9 import java.util.List;
:10 import java.util.Objects;
:11 import org.springframework.data.mongodb.core.MongoOperations;
:12 import org.springframework.data.mongodb.core.aggregation.Aggregation;
:13 import org.springframework.data.mongodb.core.aggregation.AggregationOptions;
:14 import org.springframework.data.mongodb.core.aggregation.AggregationResults;
# 같은 모양의 서명이 또 있는가
MongoReactiveCursorPublisher.java:32 public <T> Flux<T> stream(
MongoReactiveCursorPublisher.java:33 MongoOperationContext context,
MongoReactiveCursorPublisher.java:34 ReactiveMongoOperations operations,
MongoReactiveCursorPublisher.java:35 Query query,
MongoReactiveCursorPublisher.java:36 Class<T> documentType,
MongoReactiveCursorPublisher.java:37 String collection,
MongoReactiveCursorPublisher.java:38 MongoOperationBudget budget,
MongoReactiveCursorPublisher.java:39 int batchSize) {
MongoReactiveCursorPublisher.java:40 Objects.requireNonNull(context, "context");
MongoReactiveCursorPublisher.java:41 Objects.requireNonNull(operations, "operations");
MongoReactiveCursorPublisher.java:42 Objects.requireNonNull(query, "query");
MongoReactiveCursorPublisher.java:43 Objects.requireNonNull(documentType, "documentType");
MongoReactiveCursorPublisher.java:44 Objects.requireNonNull(collection, "collection");
MongoReactiveCursorPublisher.java:45 guard.requireBoundedBatch(budget, batchSize);
MongoReactiveCursorPublisher.java:46
MongoReactiveCursorPublisher.java:55 Query bounded =
MongoReactiveCursorPublisher.java:56 Query.of(query)
MongoReactiveCursorPublisher.java:57 .cursorBatchSize(batchSize)
MongoReactiveCursorPublisher.java:58 .maxTimeMsec(budget.maxTimeMillis())
MongoReactiveCursorPublisher.java:59 .limit(budget.maxResults());
MongoReactiveCursorPublisher.java:60 MongoResultBudgetTracker tracker = new MongoResultBudgetTracker(budget, null);
MongoReactiveCursorPublisher.java:61 return guard.guard(
MongoReactiveCursorPublisher.java:62 TrackingLease::new,
MongoReactiveCursorPublisher.java:63 lease ->
MongoReactiveCursorPublisher.java:64 Flux.from(operations.find(bounded, documentType, collection))
MongoReactiveCursorPublisher.java:65 // Counted client-side as well: a server limit bounds one query, and this bounds the
이 파일이 MongoCollectionProfileRegistry 를 언급하는 줄 : 0 개
이 파일이 collectionProfile() 을 부르는 줄 : 0 개
context 가 나오는 줄 전부 :
MongoReactiveCursorPublisher.java:33 MongoOperationContext context,
MongoReactiveCursorPublisher.java:40 Objects.requireNonNull(context, "context");
MongoOperationContext 와 String collection 을 함께 받는 main 파일 :
PolicyAwareMongoAggregationExecutor.java
MongoAtomicOperationsTemplate.java
MongoReactiveCursorPublisher.java
# 이 실행기가 조립되는가
main · PolicyAwareMongoAggregationExecutor.java:24 public final class PolicyAwareMongoAggregationExecutor {
main · PolicyAwareMongoAggregationExecutor.java:32 public PolicyAwareMongoAggregationExecutor(
test · PolicyAwareMongoAggregationExecutorTest.java:14 class PolicyAwareMongoAggregationExecutorTest {
main 에서 그것을 만드는 줄 : 0 개
[대조] main 에서 MongoBudgetEnforcer 를 만드는 줄 : 1 개
MongoReactiveCursorPublisher 를 만드는 main 줄 : 0 개
[대조] 시험이 집계 실행기를 만드는 줄 : 0 개