# 승인 메서드 셋의 본문
  GrpcAdmissionController.java:50   /** Admits a call, queues it, or refuses it. */
  GrpcAdmissionController.java:51   public Decision tryAdmit() {
  GrpcAdmissionController.java:52     int running = inFlight.get();
  GrpcAdmissionController.java:53     if (running < maxConcurrentCalls) {
  GrpcAdmissionController.java:54       inFlight.incrementAndGet();
  GrpcAdmissionController.java:55       return new Decision(true, inFlight.get(), queued.get(), "within the concurrency bound");
  GrpcAdmissionController.java:56     }
  GrpcAdmissionController.java:57     int waiting = queued.get();
  GrpcAdmissionController.java:58     if (waiting < maxQueuedCalls) {
  GrpcAdmissionController.java:59       queued.incrementAndGet();
  GrpcAdmissionController.java:60       return new Decision(true, running, queued.get(), "queued within the admission bound");
  GrpcAdmissionController.java:61     }
  GrpcAdmissionController.java:62     rejected.incrementAndGet();
  GrpcAdmissionController.java:63     return new Decision(
  GrpcAdmissionController.java:64         false,
  GrpcAdmissionController.java:65         running,
  GrpcAdmissionController.java:66         waiting,
  GrpcAdmissionController.java:67         "at capacity: "
  GrpcAdmissionController.java:68             + running
  GrpcAdmissionController.java:69             + " in flight and "
  GrpcAdmissionController.java:70             + waiting
  GrpcAdmissionController.java:71             + " queued. Refusing is better than accepting work whose caller will have given up.");
  GrpcAdmissionController.java:72   }
  GrpcAdmissionController.java:73 
  GrpcAdmissionController.java:74   /** Moves a queued call into execution. */
  GrpcAdmissionController.java:75   public void promoteFromQueue() {
  GrpcAdmissionController.java:76     if (queued.get() > 0) {
  GrpcAdmissionController.java:77       queued.decrementAndGet();
  GrpcAdmissionController.java:78       inFlight.incrementAndGet();
  GrpcAdmissionController.java:79     }
  GrpcAdmissionController.java:80   }
  GrpcAdmissionController.java:81 
  GrpcAdmissionController.java:82   /** Releases a finished call. */
  GrpcAdmissionController.java:83   public void release() {
  GrpcAdmissionController.java:84     if (inFlight.get() > 0) {
  GrpcAdmissionController.java:85       inFlight.decrementAndGet();
  GrpcAdmissionController.java:86     }
  GrpcAdmissionController.java:87   }
  계수기 필드 :
  GrpcAdmissionController.java:17   private final AtomicInteger inFlight = new AtomicInteger();
  GrpcAdmissionController.java:18   private final AtomicInteger queued = new AtomicInteger();
  GrpcAdmissionController.java:19   private final AtomicInteger rejected = new AtomicInteger();

# 클래스가 자기 존재 이유를 무엇이라고 적는가
  GrpcAdmissionController.java:5 /**
  GrpcAdmissionController.java:6  * Refuses work the server cannot do, before it spends anything on it.
  GrpcAdmissionController.java:7  *
  GrpcAdmissionController.java:8  * <p>Rejecting with {@code RESOURCE_EXHAUSTED} is a better outcome than queueing for two reasons
  GrpcAdmissionController.java:9  * that both matter under load: the client learns immediately and can shed or retry elsewhere, and
  GrpcAdmissionController.java:10  * the server stops accumulating work whose callers have already given up. A server that queues
  GrpcAdmissionController.java:11  * instead spends its capacity finishing requests nobody is reading.
  GrpcAdmissionController.java:12  */
  GrpcAdmissionController.java:13 public final class GrpcAdmissionController {

# 이 타입이 조립되는가, 그리고 승인 API 를 부르는 자리는 어디인가
  main · GrpcDrainCoordinator.java:3 import dev.caskeleton.grpc.server.GrpcAdmissionController;
  main · GrpcDrainCoordinator.java:25   private final GrpcAdmissionController admission;
  main · GrpcDrainCoordinator.java:42       GrpcAdmissionController admission,
  test · GrpcDrainCoordinatorTest.java:6 import dev.caskeleton.grpc.server.GrpcAdmissionController;
  test · GrpcDrainCoordinatorTest.java:27         health, new GrpcAdmissionController(8, 8), policy, unaryCalls::get, streams::get);
  main · GrpcAdmissionController.java:13 public final class GrpcAdmissionController {
  main · GrpcAdmissionController.java:22   public GrpcAdmissionController(int maxConcurrentCalls, int maxQueuedCalls) {
  main · GrpcAdmissionController.java:31   public static GrpcAdmissionController forExecutor(GrpcExecutorProfile profile) {
  main · GrpcAdmissionController.java:35     return new GrpcAdmissionController(
  test · GrpcServerProfileTest.java:105     GrpcAdmissionController controller = new GrpcAdmissionController(2, 1);
  test · GrpcServerProfileTest.java:110     GrpcAdmissionController.Decision refused = controller.tryAdmit();
  test · GrpcServerProfileTest.java:122     GrpcAdmissionController controller = new GrpcAdmissionController(1, 1);
  test · GrpcServerProfileTest.java:136     GrpcAdmissionController controller = GrpcAdmissionController.forExecutor(EXECUTOR);
  main · GrpcPlatformAutoConfiguration.java:11 import dev.caskeleton.grpc.server.GrpcAdmissionController;
  main · GrpcPlatformAutoConfiguration.java:56   public GrpcAdmissionController grpcAdmissionController(GrpcExecutorProfile executorProfile) {
  main · GrpcPlatformAutoConfiguration.java:57     return GrpcAdmissionController.forExecutor(executorProfile);
  tryAdmit · promoteFromQueue · release 를 부르는 자리 :
    test · GrpcServerProfileTest.java:107     assertThat(controller.tryAdmit().admitted()).isTrue();
    test · GrpcServerProfileTest.java:108     assertThat(controller.tryAdmit().admitted()).isTrue();
    test · GrpcServerProfileTest.java:109     assertThat(controller.tryAdmit().admitted()).isTrue();
    test · GrpcServerProfileTest.java:110     GrpcAdmissionController.Decision refused = controller.tryAdmit();
    test · GrpcServerProfileTest.java:123     controller.tryAdmit();
    test · GrpcServerProfileTest.java:124     controller.tryAdmit();
    test · GrpcServerProfileTest.java:126     controller.release();
    test · GrpcServerProfileTest.java:127     controller.promoteFromQueue();
    test · GrpcServerProfileTest.java:139     assertThat(controller.tryAdmit().admitted()).isTrue();
    그중 main 소스 세트 : 0 개 파일
  main 에서 이 타입을 들고 있는 GrpcDrainCoordinator 가 하는 일 :
    GrpcDrainCoordinator.java:148  return admission.inFlight();
  그 빈을 만드는 자동설정이 언제 켜지는가 :
    GrpcPlatformAutoConfiguration.java:29 @Configuration(proxyBeanMethods = false)
    GrpcPlatformAutoConfiguration.java:30 @ConditionalOnProperty(
    GrpcPlatformAutoConfiguration.java:31     prefix = "ca-skeleton.grpc.platform",
    GrpcPlatformAutoConfiguration.java:32     name = "enabled",
    GrpcPlatformAutoConfiguration.java:33     havingValue = "true",
    GrpcPlatformAutoConfiguration.java:34     matchIfMissing = false)
    GrpcPlatformAutoConfiguration.java:35 @EnableConfigurationProperties(GrpcPlatformProperties.class)
    GrpcPlatformAutoConfiguration.java:36 public class GrpcPlatformAutoConfiguration {
    그 프로퍼티를 켜는 설정 파일 : 0 개
    src/grpc 밖에서 이 starter 에 의존하는 모듈 : 0 개
  main 소비자 GrpcDrainCoordinator 가 생성되는 자리 :
    test · GrpcDrainCoordinatorTest.java:26     return new GrpcDrainCoordinator(
    src/grpc 안의 ApplicationContextRunner : 0 개 파일

# 저장소가 쓰는 승격 순서
  GrpcServerProfileTest.java:119   @Test
  GrpcServerProfileTest.java:120   @DisplayName("a released call makes room, and a queued call is promoted into it")
  GrpcServerProfileTest.java:121   void releaseAndPromotionTrackCapacity() {
  GrpcServerProfileTest.java:122     GrpcAdmissionController controller = new GrpcAdmissionController(1, 1);
  GrpcServerProfileTest.java:123     controller.tryAdmit();
  GrpcServerProfileTest.java:124     controller.tryAdmit();
  GrpcServerProfileTest.java:125 
  GrpcServerProfileTest.java:126     controller.release();
  GrpcServerProfileTest.java:127     controller.promoteFromQueue();
  GrpcServerProfileTest.java:128 
  GrpcServerProfileTest.java:129     assertThat(controller.inFlight()).isEqualTo(1);
  GrpcServerProfileTest.java:130     assertThat(controller.queued()).isZero();
  GrpcServerProfileTest.java:131   }

# 같은 가족의 올바른 형태 — 재시도 예산
  src/grpc/grpc-policy/src/main/java/dev/caskeleton/grpc/resilience/GrpcRetryBudget.java
  GrpcRetryBudget.java:50   public boolean tryConsume() {
  GrpcRetryBudget.java:51     while (true) {
  GrpcRetryBudget.java:52       long observed = tokens.get();
  GrpcRetryBudget.java:53       if (observed < tokensPerRetry) {
  GrpcRetryBudget.java:54         return false;
  GrpcRetryBudget.java:55       }
  GrpcRetryBudget.java:56       if (tokens.compareAndSet(observed, observed - tokensPerRetry)) {
  GrpcRetryBudget.java:57         return true;
  GrpcRetryBudget.java:58       }
  GrpcRetryBudget.java:59     }
  GrpcRetryBudget.java:60   }
  GrpcRetryBudget.java:61 
  compareAndSet 을 쓰는 grpc main 파일 :
    GrpcChannelRuntimeRegistry.java
    GrpcCancellationToken.java
    GrpcRetryBudget.java
    GrpcAdmissionController.java compareAndSet 0 건
    GrpcStreamAdmission.java     compareAndSet 0 건

# 프로브 — 아래 소스를 그 자리에서 컴파일해 돌린다
    GrpcAdmissionController paired = new GrpcAdmissionController(1, 8);
    for (int i = 0; i < 6; i++) paired.tryAdmit();
    for (int i = 0; i < 5; i++) { paired.release(); paired.promoteFromQueue(); }
    GrpcAdmissionController solo = new GrpcAdmissionController(1, 8);
    for (int i = 0; i < 6; i++) solo.tryAdmit();
    for (int i = 0; i < 5; i++) solo.promoteFromQueue();
  가. 저장소 시험과 같은 순서 — 해제 하나에 승격 하나
     여섯 번 승인 후        : 진행 중 1 · 대기 5
     해제·승격을 다섯 쌍 후 : 진행 중 1 · 대기 0

  나. 해제 없이 승격만 — 승격이 해제를 앞지른 경우
     여섯 번 승인 후        : 진행 중 1 · 대기 5
     승격만 다섯 번 후      : 진행 중 6 · 대기 0
     동시성 상한            : 1
