# 회전 — 읽고 판단한 뒤 조건 없이 쓴다 AtomicReference holder = current.get(next.profileName()); if (holder == null || holder.get() == null) { return install(next); } GrpcChannelRuntime previous = holder.get(); if (!previous.generation().supersededBy(next)) { throw new IllegalArgumentException( "generation " GrpcChannelRuntime replacement = new GrpcChannelRuntime(next); holder.set(replacement); previous.beginDrain(); draining # 같은 클래스의 첫 설치는 비교 후 교체를 쓴다 AtomicReference holder = current.computeIfAbsent(generation.profileName(), key -> new AtomicReference<>()); if (!holder.compareAndSet(null, runtime)) { # 그 파일의 compareAndSet 매치 수: 1 # 덮인 대체본을 담는 곳 — draining 에 add 되는 값: 108: .add(previous); # 이 세대가 들고 있는 것은 채널이 아니다 17: private final GrpcChannelGeneration generation; 18: private final AtomicInteger inFlightUnaryCalls = new AtomicInteger(); 19: private final AtomicInteger openStreams = new AtomicInteger(); 20: private volatile boolean draining; # 이 모듈 main 의 io.grpc 참조: 0 # 계수기 — 확인하고 별도 연산으로 줄인다 public void finishUnaryCall() { if (inFlightUnaryCalls.get() > 0) { inFlightUnaryCalls.decrementAndGet(); } } public void closeStream() { if (openStreams.get() > 0) { openStreams.decrementAndGet(); } } # 이 파일의 compareAndSet / updateAndGet 매치: 0 # 올리는 쪽은 배수가 시작되면 거절한다 public boolean startUnaryCall() { if (draining) { return false; } inFlightUnaryCalls.incrementAndGet(); # 조용함 판정 public boolean quiescent() { return inFlightUnaryCalls.get() == 0 && openStreams.get() == 0; } # finishUnaryCall 의 프로덕션 호출자: 0 # 회수 — 동기화 목록을 잠그지 않고 순회한다 107: next.profileName(), key -> java.util.Collections.synchronizedList(new ArrayList<>())) public List draining(GrpcChannelProfileName profileName) { return List.copyOf(draining.getOrDefault(profileName, List.of())); } public int retireQuiescent(GrpcChannelProfileName profileName) { List runtimes = draining.get(profileName); if (runtimes == null) { return 0; } List quiescent = runtimes.stream().filter(GrpcChannelRuntime::quiescent).toList(); runtimes.removeAll(quiescent); return quiescent.size(); } # retireQuiescent 의 프로덕션 호출자: 0 # 옆 모듈 — 배수 완료는 판단조차 없다 /** Forgets the draining generation once its work has finished or been cancelled. */ public void completeDrain() { State observed = state.get(); state.set(new State(observed.current(), null, null)); } # 그 파일의 compareAndSet / updateAndGet / synchronized 매치: 0 # 두 모듈의 런타임 소속: grpc-policy=[] grpc-client=[]