# 레지스트리가 펴는 주장
  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 개
