# 레지스트리가 펴는 주장 MongoCollectionProfileRegistry.java:9 /** MongoCollectionProfileRegistry.java:10 * Maps registered collection profiles to physical collection names (design §16.2, §28). MongoCollectionProfileRegistry.java:11 * MongoCollectionProfileRegistry.java:12 *

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 physicalNames; MongoCollectionProfileRegistry.java:20 MongoCollectionProfileRegistry.java:21 private MongoCollectionProfileRegistry(Map 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 *

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 *

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 List 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 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 results = operations.aggregate(aggregation, collection, outputType); PolicyAwareMongoAggregationExecutor.java:77 List 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 Flux stream( MongoReactiveCursorPublisher.java:33 MongoOperationContext context, MongoReactiveCursorPublisher.java:34 ReactiveMongoOperations operations, MongoReactiveCursorPublisher.java:35 Query query, MongoReactiveCursorPublisher.java:36 Class 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 개