--- kind: CASE slug: grpc-client-f02 title: 비원자적 감소가 세대를 영구히 회수 불가로 만든다 topic: state-ownership-and-concurrency project: clean-architecture-backend-template status: 게시 전 sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 rootTreeNode: case:grpc-client-f02 evidenceCapturedOn: 2026-09-01 assets: - key: grpc-client-f02 file: ../../../final/evidence/rendered/grpc-client-f02.svg - key: grpc-client-f02-diagram file: ../../../final/assets/diagrams/grpc-client-f02.svg evidence: - ../../../final/evidence/raw/grpc-client-f02.txt source: - 원본 분석 절은 final/document.md#a20-grpc-client#L149 이다. module: grpc-client priority: P2 --- # 비원자적 감소가 세대를 영구히 회수 불가로 만든다 카운터가 1 일 때 두 스레드가 동시에 끝나면 둘 다 조건을 통과해 둘 다 감소시켜 −1 이 된다. 그 결과가 이 리프에서는 구체적이다. ## 문제 카운터가 1 일 때 두 스레드가 동시에 끝나면 둘 다 조건을 통과해 둘 다 감소시켜 −1 이 된다. 그 결과가 이 리프에서는 구체적이다. ## 결론 정확히 0 을 요구한다. 음수가 되면 조용해짐 판정이 영원히 거짓이고, retireQuiescent 가 그 세대를 결코 제거하지 않는다. 회전이 반복될수록 draining 목록이 자란다. 같은 형태가 이 가족의 다른 두 곳에도 있다(GrpcAdmissionController.release, GrpcStreamAdmission.release). 그쪽은 경계가 느슨해지는 결과였고, 이쪽은 자원이 회수되지 않는 결과다. 수정은 updateAndGet(v -> Math.max(0, v - 1)) 이나 decrementAndGet() 후 하한 보정이다. 같은 가족의 GrpcRetryBudget 이 정확한 비교 후 교체 루프를 이미 쓴다. ## 검증 환경 OpenJDK : 21.0.12 java -version 으로 확인 Gradle : 9.0.0 src/gradle/wrapper/gradle-wrapper.properties 의 distributionUrl 로 확인 확인 방식 : GrpcAdmissionController 참조 16건 검색과 감소 연산의 원자성 확인 소스 수정 : x ## 재현 조건 원문은 final/document.md#a20-grpc-client#L149 에 있다. ## 본문 카운터가 1 일 때 두 스레드가 동시에 끝나면 둘 다 조건을 통과해 둘 다 감소시켜 −1 이 된다. ## 회수가 막히는 자리 :::evidence key="grpc-client-f02-diagram" alt="비원자적 감소에서 계수기 음수로 둘 다 통과가 건너가고 계수기 음수에서 회수 불가로 조용함 거짓이 건너간다" caption="회수가 막히는 자리" zoom="false" ::: 조용해짐 판정이 정확히 0 을 요구하므로, 음수가 되면 그 판정이 영원히 거짓이고 `retireQuiescent` 가 그 세대를 결코 제거하지 않는다. 회전이 반복될수록 `draining` 목록이 자란다. ## GrpcAdmissionController 참조 위치 :::evidence key="grpc-client-f02" alt="코드베이스에서 GrpcAdmissionController 를 검색한 출력 16줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="GrpcAdmissionController 코드베이스 검색 — 16줄 · exit 0" zoom="true" ::: ## 같은 형태가 이 가족의 다른 두 곳에도 있다 `GrpcAdmissionController.release`, `GrpcStreamAdmission.release`. 그쪽은 경계가 느슨해지는 결과였고, 이쪽은 자원이 회수되지 않는 결과다. 수정은 `updateAndGet(v -> Math.max(0, v - 1))` 이나 `decrementAndGet()` 후 하한 보정이다 — 같은 가족의 `GrpcRetryBudget` 이 정확한 비교 후 교체 루프를 이미 쓴다. ## 확인하지 못한 것 동시 해제를 실행으로 재현해 계수기가 음수가 되는 것을 관측하지 않았다. 원자성 분석으로 판정했다.