Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/a20-f006-grpcadmissioncontroller-tryadmit.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

152 lines
10 KiB
Plaintext

# 승인 메서드 셋의 본문
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