# 관측 인터페이스가 세우는 계약
  MongoOperationObservation.java:6 /**
  MongoOperationObservation.java:7  * One in-flight operation's observation scope (design §27).
  MongoOperationObservation.java:8  *
  MongoOperationObservation.java:9  * <p>{@link AutoCloseable} so the scope closes on every path, including the one where the callback
  MongoOperationObservation.java:10  * throws something the platform never classified. An observation that only closes on success
  MongoOperationObservation.java:11  * produces metrics that look healthy precisely when the system is not.
  MongoOperationObservation.java:12  */
  MongoOperationObservation.java:13 public interface MongoOperationObservation extends AutoCloseable {
  MongoOperationObservation.java:14 
  MongoOperationObservation.java:15   /** Records a successful completion and its write outcome. */
  MongoOperationObservation.java:16   void success(MongoExecutionOutcome outcome);
  MongoOperationObservation.java:17 
  MongoOperationObservation.java:18   /** Records a failure, using only the bounded metadata the failure context is allowed to carry. */
  MongoOperationObservation.java:19   void failure(MongoFailureContext failureContext);
  MongoOperationObservation.java:20 
  MongoOperationObservation.java:21   /** The trace identifier of this observation, or an empty string when tracing is off. */
  MongoOperationObservation.java:22   String traceId();
  MongoOperationObservation.java:23 
  MongoOperationObservation.java:24   @Override
  MongoOperationObservation.java:25   void close();
  MongoOperationObservation.java:26 }

# 예산 초과 분기
  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);
  DefaultMongoImperativeExecutor.java:107         return new MongoOperationResult<>(context, value, outcome, elapsed);
  DefaultMongoImperativeExecutor.java:108       } catch (MongoPersistenceException alreadyTranslated) {
  DefaultMongoImperativeExecutor.java:109         observation.failure(alreadyTranslated.failureContext());
  DefaultMongoImperativeExecutor.java:110         throw alreadyTranslated;
  DefaultMongoImperativeExecutor.java:111       } catch (MongoException driverFailure) {
  DefaultMongoImperativeExecutor.java:112         throw translated(context, operationType, startedAt, observation, driverFailure);
  DefaultMongoImperativeExecutor.java:113       } catch (DataAccessException springFailure) {
  DefaultMongoImperativeExecutor.java:114         throw translated(context, operationType, startedAt, observation, unwrap(springFailure));
  DefaultMongoImperativeExecutor.java:115       }
  DefaultMongoImperativeExecutor.java:116     }
  DefaultMongoImperativeExecutor.java:117   }
  DefaultMongoImperativeExecutor.java:118 

# 그 예외가 무엇을 상속하는가
  MongoOperationRejectedException.java:1 package dev.caskeleton.adapter.outbound.mongo.api.error;
  MongoOperationRejectedException.java:2 
  MongoOperationRejectedException.java:3 import dev.caskeleton.adapter.outbound.mongo.api.MongoOperationName;
  MongoOperationRejectedException.java:4 import java.io.Serial;
  MongoOperationRejectedException.java:5 
  MongoOperationRejectedException.java:6 /**
  MongoOperationRejectedException.java:7  * The platform refused the operation locally, before any server was contacted (design §15, §16.2).
  MongoOperationRejectedException.java:8  *
  MongoOperationRejectedException.java:9  * <p>This is the guardrail exception: unregistered field, unregistered operator, raised budget,
  MongoOperationRejectedException.java:10  * write stage in a read API, deep skip, missing tenant context, admin command on a runtime client.
  MongoOperationRejectedException.java:11  * It is always {@code NOT_SENT} and never retryable, because retrying a rejected request only
  MongoOperationRejectedException.java:12  * repeats the rejection.
  MongoOperationRejectedException.java:13  */
  MongoOperationRejectedException.java:14 public final class MongoOperationRejectedException extends MongoPersistenceException {
  MongoOperationRejectedException.java:15 
  MongoOperationRejectedException.java:16   @Serial private static final long serialVersionUID = 1L;
  MongoOperationRejectedException.java:17 
  MongoOperationRejectedException.java:18   public MongoOperationRejectedException(String summary, MongoFailureContext failureContext) {
  MongoOperationRejectedException.java:19     super(summary, failureContext);
  MongoOperationRejectedException.java:20   }
  MongoOperationRejectedException.java:21 
  MongoOperationRejectedException.java:22   /** Rejects an operation identified only by name, before profiles were resolved. */
  MongoOperationRejectedException.java:23   public static MongoOperationRejectedException of(String operationName, String summary) {
  MongoOperationRejectedException.java:24     return new MongoOperationRejectedException(
  MongoOperationRejectedException.java:25         summary, MongoFailureContext.rejected(new MongoOperationName(operationName)));
  MongoOperationRejectedException.java:26   }
  MongoOperationRejectedException.java:27 

# 한 관측에 두 번 기록되는가
  try 블록 안에서 observation.success 를 부르는 줄 : 2 개
  같은 try 의 catch 가 observation.failure 를 부르는 줄 : 1 개
  이 파일에서 두 메서드를 부르는 자리 전부 :
    DefaultMongoImperativeExecutor.java:97  observation.success(outcome);
    DefaultMongoImperativeExecutor.java:106  observation.success(outcome);
    DefaultMongoImperativeExecutor.java:109  observation.failure(alreadyTranslated.failureContext());
    DefaultMongoImperativeExecutor.java:129  observation.failure(translated.failureContext().withTraceId(observation.traceId()));

# 출하 구현이 그것을 어떻게 다루는가
  MicrometerMongoOperationObserver.java:62     private final Tags baseTags;
  MicrometerMongoOperationObserver.java:63 
  MicrometerMongoOperationObserver.java:64     private Tags outcomeTags;
  MicrometerMongoOperationObserver.java:65 
  MicrometerMongoOperationObserver.java:66     private boolean stopped;
  MicrometerMongoOperationObserver.java:67 
  MicrometerMongoOperationObserver.java:68     private MicrometerObservation(MeterRegistry registry, Timer.Sample sample, Tags baseTags) {
  MicrometerMongoOperationObserver.java:69       this.registry = registry;
  MicrometerMongoOperationObserver.java:70       this.sample = sample;
  MicrometerMongoOperationObserver.java:71       this.baseTags = baseTags;
  MicrometerMongoOperationObserver.java:72       this.outcomeTags = Tags.of("result", "unknown", "failureCategory", "none");
  MicrometerMongoOperationObserver.java:73     }
  MicrometerMongoOperationObserver.java:74 
  MicrometerMongoOperationObserver.java:75     @Override
  MicrometerMongoOperationObserver.java:76     public void success(MongoExecutionOutcome outcome) {
  MicrometerMongoOperationObserver.java:77       outcomeTags = Tags.of("result", outcome.name(), "failureCategory", "none");
  MicrometerMongoOperationObserver.java:78     }
  MicrometerMongoOperationObserver.java:79 
  MicrometerMongoOperationObserver.java:80     @Override
  MicrometerMongoOperationObserver.java:81     public void failure(MongoFailureContext failureContext) {
  MicrometerMongoOperationObserver.java:82       outcomeTags =
  MicrometerMongoOperationObserver.java:83           Tags.of(
  MicrometerMongoOperationObserver.java:84               "result",
  MicrometerMongoOperationObserver.java:85               failureContext.outcome().name(),
  MicrometerMongoOperationObserver.java:86               "failureCategory",
  MicrometerMongoOperationObserver.java:87               failureContext.category().name());
  MicrometerMongoOperationObserver.java:88     }
  MicrometerMongoOperationObserver.java:89 
  MicrometerMongoOperationObserver.java:90     @Override
  MicrometerMongoOperationObserver.java:91     public String traceId() {
  MicrometerMongoOperationObserver.java:92       return "";
  MicrometerMongoOperationObserver.java:93     }
  MicrometerMongoOperationObserver.java:94 
  MicrometerMongoOperationObserver.java:95     @Override
  MicrometerMongoOperationObserver.java:96     public void close() {
  MicrometerMongoOperationObserver.java:97       if (stopped) {
  MicrometerMongoOperationObserver.java:98         return;
  MicrometerMongoOperationObserver.java:99       }
  MicrometerMongoOperationObserver.java:100       stopped = true;
  MicrometerMongoOperationObserver.java:101       sample.stop(registry.timer(TIMER_NAME, baseTags.and(outcomeTags)));
  MicrometerMongoOperationObserver.java:102     }
  MicrometerMongoOperationObserver.java:103   }

# 다른 구현이 있는가
  MongoOperationObservation 을 구현하는 자리 : 2 개
    main · NoOpMongoOperationObserver.java:29  private static final class NoOpObservation implements MongoOperationObservation {
    main · MicrometerMongoOperationObserver.java:56  private static final class MicrometerObservation implements MongoOperationObservation {
  [대조] MongoOperationObserver 를 구현하는 자리 : 2 개

  NoOpMongoOperationObserver.java:27   }
  NoOpMongoOperationObserver.java:28 
  NoOpMongoOperationObserver.java:29   private static final class NoOpObservation implements MongoOperationObservation {
  NoOpMongoOperationObserver.java:30 
  NoOpMongoOperationObserver.java:31     @Override
  NoOpMongoOperationObserver.java:32     public void success(MongoExecutionOutcome outcome) {
  NoOpMongoOperationObserver.java:33       // Nothing is recorded when telemetry is not configured.
  NoOpMongoOperationObserver.java:34     }
  NoOpMongoOperationObserver.java:35 
  NoOpMongoOperationObserver.java:36     @Override
  NoOpMongoOperationObserver.java:37     public void failure(MongoFailureContext failureContext) {
  NoOpMongoOperationObserver.java:38       // Nothing is recorded when telemetry is not configured.
  NoOpMongoOperationObserver.java:39     }
  NoOpMongoOperationObserver.java:40 
  NoOpMongoOperationObserver.java:41     @Override
  NoOpMongoOperationObserver.java:42     public String traceId() {
  NoOpMongoOperationObserver.java:43       return "";
  NoOpMongoOperationObserver.java:44     }
  NoOpMongoOperationObserver.java:45 
  NoOpMongoOperationObserver.java:46     @Override
  NoOpMongoOperationObserver.java:47     public void close() {
  NoOpMongoOperationObserver.java:48       // Nothing to release.
  NoOpMongoOperationObserver.java:49     }
  NoOpMongoOperationObserver.java:50   }
