# 이 타입이 존재하는 이유 BoundScopedOperations.java:12 /** BoundScopedOperations.java:13 * {@link ScopedMongoOperations} over one physical collection. BoundScopedOperations.java:14 * BoundScopedOperations.java:15 *

Every method passes the collection this scope was built with. That is the whole implementation BoundScopedOperations.java:16 * and the whole point: a caller cannot supply a different one, so the registered collection profile BoundScopedOperations.java:17 * and any tenant boundary derived from it hold without the caller having to cooperate. BoundScopedOperations.java:18 * BoundScopedOperations.java:19 *

Every query-shaped method also carries the operation's deadline as {@code maxTimeMS}, and that BoundScopedOperations.java:20 * is the difference between a deadline and a report about one. The blocking executor could only BoundScopedOperations.java:21 * measure elapsed time after the callback returned — a Java callback cannot be interrupted mid BoundScopedOperations.java:22 * driver call — so an operation that ran past its budget was detected, never stopped. Sent to the BoundScopedOperations.java:23 * server, the same number ends the work. BoundScopedOperations.java:24 * BoundScopedOperations.java:25 *

It applies to the methods that take a {@link Query} or an {@link Aggregation}, because those BoundScopedOperations.java:26 * are the ones the server can cut short. {@code insert} carries no query to attach it to. BoundScopedOperations.java:27 */ 그 데드라인이 실제로 붙는 자리 : BoundScopedOperations.java:19 *

Every query-shaped method also carries the operation's deadline as {@code maxTimeMS}, and that BoundScopedOperations.java:53 return query.maxTimeMsec(timeout.toMillis()); BoundScopedOperations.java:98 .maxTime(timeout) BoundScopedOperations.java:48 *

Mutates the caller's query rather than copying it, which is what Spring Data's own fluent BoundScopedOperations.java:49 * API does; a copy would silently drop any hint, collation or read preference the caller set. BoundScopedOperations.java:50 */ BoundScopedOperations.java:51 private Query bounded(Query query) { BoundScopedOperations.java:52 Objects.requireNonNull(query, "query"); BoundScopedOperations.java:53 return query.maxTimeMsec(timeout.toMillis()); BoundScopedOperations.java:54 } BoundScopedOperations.java:55 BoundScopedOperations.java:56 @Override BoundScopedOperations.java:92 BoundScopedOperations.java:93 @Override BoundScopedOperations.java:94 public List aggregate(Aggregation aggregation, Class resultType) { BoundScopedOperations.java:95 Aggregation bounded = BoundScopedOperations.java:96 aggregation.withOptions( BoundScopedOperations.java:97 org.springframework.data.mongodb.core.aggregation.AggregationOptions.builder() BoundScopedOperations.java:98 .maxTime(timeout) BoundScopedOperations.java:99 .build()); BoundScopedOperations.java:100 return operations.aggregate(bounded, collection, resultType).getMappedResults(); BoundScopedOperations.java:101 } BoundScopedOperations.java:102 } # 그 타입에 닿는 유일한 문 MongoCollectionAccess.java:24 MongoCollectionAccess.java:25 /** MongoCollectionAccess.java:26 * The operations this callback may perform. MongoCollectionAccess.java:27 * MongoCollectionAccess.java:28 *

Scoped: no method takes a collection name, so the check {@link #collection(String)} performs MongoCollectionAccess.java:29 * cannot be bypassed by not calling it. The previous {@code MongoOperations} accessor could reach MongoCollectionAccess.java:30 * any collection in the database. MongoCollectionAccess.java:31 */ MongoCollectionAccess.java:32 ScopedMongoOperations scoped(); MongoCollectionAccess.java:33 } MongoPlatformCollectionAccess.java:5 /** MongoPlatformCollectionAccess.java:6 * Raw access, for the platform's own executors only. MongoPlatformCollectionAccess.java:7 * MongoPlatformCollectionAccess.java:8 *

The bulk, atomic, aggregation and geospatial executors build driver-level commands and need MongoPlatformCollectionAccess.java:9 * the unrestricted template. They are inside the platform and are the code that installs the MongoPlatformCollectionAccess.java:10 * guardrails, which is a different position from a caller's callback — so the raw handle is a type MongoPlatformCollectionAccess.java:11 * they ask for by name rather than something every callback receives. MongoPlatformCollectionAccess.java:12 * MongoPlatformCollectionAccess.java:13 *

Keeping it out of {@link MongoCollectionAccess} is the point. An interface that offers both a MongoPlatformCollectionAccess.java:14 * scoped API and an escape hatch offers an escape hatch. MongoPlatformCollectionAccess.java:15 */ MongoPlatformCollectionAccess.java:16 public interface MongoPlatformCollectionAccess extends MongoCollectionAccess { MongoPlatformCollectionAccess.java:17 MongoPlatformCollectionAccess.java:18 /** The template bound to the operation's consistency profile, with no collection scoping. */ MongoPlatformCollectionAccess.java:19 MongoOperations rawOperations(); MongoPlatformCollectionAccess.java:20 } # 두 접근자를 부르는 자리 전수 저장소 전체에서 scoped() 를 부르는 줄 : 0 개 저장소 전체에서 rawOperations() 를 부르는 줄 : 7 개 두 이름이 나오는 자리 전부 (선언 포함) : main · SpringMongoGeospatialOperations.java:58 return access.rawOperations().find(near, documentType, access.collection()); main · SpringMongoGeospatialOperations.java:82 return access.rawOperations().find(within, documentType, access.collection()); main · DefaultMongoImperativeExecutor.java:184 public ScopedMongoOperations scoped() { main · DefaultMongoImperativeExecutor.java:192 public MongoOperations rawOperations() { main · MongoCollectionAccess.java:32 ScopedMongoOperations scoped(); main · MongoPlatformCollectionAccess.java:19 MongoOperations rawOperations(); main · MongoAtomicOperationsTemplate.java:84 access.rawOperations(), main · MongoAtomicOperationsTemplate.java:92 access.rawOperations(), main · MongoBulkExecutor.java:69 access.rawOperations().bulkOps(plan.mode().toSpringMode(), access.collection()); main · DefaultReactiveMongoExecutor.java:218 public ReactiveScopedMongoOperations scoped() { main · ReactiveMongoCollectionAccess.java:31 ReactiveScopedMongoOperations scoped(); test · MongoRawAccessBoundaryTest.java:18 *

{@code MongoPlatformCollectionAccess.rawOperations()} and {@code test · MongoRawAccessBoundaryTest.java:37 List.of(".rawOperations()", ".executeInternal("); [대조] 그 검색이 훑은 파일 : 6444 개 # rawOperations 가 무엇을 돌려주는가 DefaultMongoImperativeExecutor.java:180 return physicalCollection; DefaultMongoImperativeExecutor.java:181 } DefaultMongoImperativeExecutor.java:182 DefaultMongoImperativeExecutor.java:183 @Override DefaultMongoImperativeExecutor.java:184 public ScopedMongoOperations scoped() { DefaultMongoImperativeExecutor.java:185 // The deadline travels with the scope. The executor can only measure elapsed time after the DefaultMongoImperativeExecutor.java:186 // callback returns; the scoped operations send the same number to the server as maxTimeMS, DefaultMongoImperativeExecutor.java:187 // where it actually ends the work. DefaultMongoImperativeExecutor.java:188 return new BoundScopedOperations(physicalCollection, operations, timeout); DefaultMongoImperativeExecutor.java:189 } DefaultMongoImperativeExecutor.java:190 DefaultMongoImperativeExecutor.java:191 @Override DefaultMongoImperativeExecutor.java:192 public MongoOperations rawOperations() { DefaultMongoImperativeExecutor.java:193 return operations; DefaultMongoImperativeExecutor.java:194 } DefaultMongoImperativeExecutor.java:195 } DefaultMongoImperativeExecutor.java:196 } # 서버에 데드라인을 실제로 붙이는 다른 경로들 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 } PolicyAwareMongoAggregationExecutor.java:99 PolicyAwareMongoAggregationExecutor.java:100 private static AggregationOptions optionsFor( PolicyAwareMongoAggregationExecutor.java:101 MongoAggregationProfile profile, MongoOperationBudget budget) { PolicyAwareMongoAggregationExecutor.java:102 AggregationOptions.Builder options = PolicyAwareMongoAggregationExecutor.java:103 AggregationOptions.builder() PolicyAwareMongoAggregationExecutor.java:104 .allowDiskUse(profile.allowDiskUse()) PolicyAwareMongoAggregationExecutor.java:105 .cursorBatchSize(budget.cursorBatchSize()) PolicyAwareMongoAggregationExecutor.java:106 .maxTime(Duration.ofMillis(budget.maxTimeMillis())); PolicyAwareMongoAggregationExecutor.java:107 return profile.strictMapping() ? options.strictMapping().build() : options.build(); PolicyAwareMongoAggregationExecutor.java:108 } maxTimeMillis 를 부르는 자리 전부 (에러 메시지 포함) : PolicyAwareMongoAggregationExecutor.java:96 Math.min(registered.maxTimeMillis(), contextMillis), PolicyAwareMongoAggregationExecutor.java:106 .maxTime(Duration.ofMillis(budget.maxTimeMillis())); PolicyAwareMongoQueryBuilder.java:200 query.maxTimeMsec(budget.maxTimeMillis()); MongoBudgetEnforcer.java:36 + requested.maxTimeMillis() MongoBudgetEnforcer.java:44 + registered.maxTimeMillis() MongoReactiveCursorPublisher.java:58 .maxTimeMsec(budget.maxTimeMillis()) context.timeout() 이나 narrowedTo 가 나오는 자리 전부 : PolicyAwareMongoAggregationExecutor.java:72 enforcer.narrow(budget, budget.narrowedTo(budgetFor(context, budget))); PolicyAwareMongoAggregationExecutor.java:92 long contextMillis = Math.max(1L, context.timeout().toMillis()); DefaultMongoImperativeExecutor.java:83 ScopedAccess access = new ScopedAccess(physicalCollection, operations, context.timeout()); DefaultMongoImperativeExecutor.java:93 if (elapsed.compareTo(context.timeout()) > 0) { DefaultMongoImperativeExecutor.java:103 + context.timeout().toMillis() MongoBudgetEnforcer.java:57 return registered.narrowedTo(requested); MongoOperationBudget.java:43 public MongoOperationBudget narrowedTo(MongoOperationBudget other) { DefaultReactiveMongoExecutor.java:76 .timeout(context.timeout()) DefaultReactiveMongoExecutor.java:108 .timeout(context.timeout()) DefaultReactiveMongoExecutor.java:154 context.operationName(), operationType, context.timeout()), # 그 다섯 자리를 가진 세 실행기가 조립되는가 MongoPlatformAutoConfiguration.java:154 .MongoAtomicOperationsTemplate(executor, policies); MongoPlatformAutoConfiguration.java:169 return new dev.caskeleton.adapter.outbound.mongo.imperative.bulk.MongoBulkExecutor( SpringMongoGeospatialOperations.java:31 public SpringMongoGeospatialOperations( MongoAtomicOperationsTemplate.java:27 public MongoAtomicOperationsTemplate( MongoBulkExecutor.java:43 public MongoBulkExecutor( 그 셋이 자기 안에서 데드라인을 따로 거는가 : SpringMongoGeospatialOperations.java maxTime 이나 timeout 이 나오는 줄 : 0 개 MongoAtomicOperationsTemplate.java maxTime 이나 timeout 이 나오는 줄 : 0 개 MongoBulkExecutor.java maxTime 이나 timeout 이 나오는 줄 : 0 개 [대조] 같은 검색을 BoundScopedOperations 에 걸면 : 8 줄 # 저장소가 그 손잡이를 어떻게 지키는가 MongoRawAccessBoundaryTest.java:15 /** MongoRawAccessBoundaryTest.java:16 * The Mongo platform's raw escape hatches stay inside the platform. MongoRawAccessBoundaryTest.java:17 * MongoRawAccessBoundaryTest.java:18 *

{@code MongoPlatformCollectionAccess.rawOperations()} and {@code MongoRawAccessBoundaryTest.java:19 * DefaultMongoImperativeExecutor.executeInternal(...)} hand out Spring Data's unbounded {@code MongoRawAccessBoundaryTest.java:20 * MongoOperations} — no collection binding, no consistency template, no deadline, no result budget. MongoRawAccessBoundaryTest.java:21 * The scoped API exists precisely so a caller cannot reach those, and the only thing stopping one MongoRawAccessBoundaryTest.java:22 * was a javadoc sentence saying "not public API for callers". MongoRawAccessBoundaryTest.java:23 * MongoRawAccessBoundaryTest.java:24 *

Both are genuinely needed *inside* the platform: the geospatial, atomic and bulk operations MongoRawAccessBoundaryTest.java:25 * are built on them. So the rule is not "delete them" but "nobody outside may call them", which is MongoRawAccessBoundaryTest.java:26 * a boundary rather than a visibility problem — the callers live in sibling packages, so Java's MongoRawAccessBoundaryTest.java:27 * package-private cannot express it. MongoRawAccessBoundaryTest.java:28 * MongoRawAccessBoundaryTest.java:29 *

Checked from the composition root because that is the only place that sees both the platform MongoRawAccessBoundaryTest.java:30 * and everything that consumes it. A rule living inside the Mongo leaf could only ever prove the MongoRawAccessBoundaryTest.java:31 * leaf does not violate itself. MongoRawAccessBoundaryTest.java:32 */ MongoRawAccessBoundaryTest.java:35 /** The escape hatches, by the text a caller would have to write to use one. */ MongoRawAccessBoundaryTest.java:36 private static final List ESCAPE_HATCHES = MongoRawAccessBoundaryTest.java:37 List.of(".rawOperations()", ".executeInternal("); MongoRawAccessBoundaryTest.java:38 MongoRawAccessBoundaryTest.java:39 /** Where they may be used: the Mongo adapter's own production sources. */ MongoRawAccessBoundaryTest.java:40 private static final String PLATFORM_SOURCE = MongoRawAccessBoundaryTest.java:41 "/src/adapter/outbound/persistence-mongo/src/main/java/"; # 세 실행기가 대신 하는 것 DefaultMongoImperativeExecutor.java:85 long startedAt = System.nanoTime(); DefaultMongoImperativeExecutor.java:86 try (MongoOperationObservation observation = observer.start(context, operationType)) { DefaultMongoImperativeExecutor.java:87 try { DefaultMongoImperativeExecutor.java:88 T value = callback.doInMongo(access); DefaultMongoImperativeExecutor.java:89 Duration elapsed = Duration.ofNanos(System.nanoTime() - startedAt); DefaultMongoImperativeExecutor.java:90 // A read is not a confirmed write. Recording every successful FIND as WRITE_CONFIRMED made DefaultMongoImperativeExecutor.java:91 // the metric that answers "are writes being acknowledged" count reads as well. DefaultMongoImperativeExecutor.java:92 MongoExecutionOutcome outcome = MongoCompletion.successOutcomeFor(operationType); DefaultMongoImperativeExecutor.java:93 if (elapsed.compareTo(context.timeout()) > 0) { DefaultMongoImperativeExecutor.java:94 // The context declares a deadline on every operation and nothing compared anything to it. DefaultMongoImperativeExecutor.java:95 // A callback cannot be interrupted mid-driver-call, so this cannot cut the work short — DefaultMongoImperativeExecutor.java:96 // but reporting a breach beats reporting success for work that ran past its budget. DefaultMongoImperativeExecutor.java:97 observation.success(outcome); DefaultMongoImperativeExecutor.java:98 throw MongoOperationRejectedException.of( DefaultMongoImperativeExecutor.java:99 context.operationName().value(), DefaultMongoImperativeExecutor.java:100 "the operation ran for " DefaultMongoImperativeExecutor.java:101 + elapsed.toMillis() DefaultMongoImperativeExecutor.java:102 + "ms against a declared timeout of " DefaultMongoImperativeExecutor.java:103 + context.timeout().toMillis() DefaultMongoImperativeExecutor.java:104 + "ms"); DefaultMongoImperativeExecutor.java:105 } DefaultMongoImperativeExecutor.java:106 observation.success(outcome);