# 주제: messaging-outbox-jdbc-postgresql 조립 탐침과, 백오프 지터가 목적을 달성하지 못하는 지점 # revision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916 # ---- 조립 탐침 (new ( 정규식, 완전수식 포함) ---- JdbcOutboxRepository src/main=0 src/test=2 <-- 스타터가 만들지 않는다 OutboxRelay src/main=1 src/test=3 OutboxRelayWorker src/main=1 src/test=1 OutboxRetryScheduler src/main=2 src/test=6 OutboxCleanupJob src/main=1 src/test=3 OutboxEnvelopeFactory src/main=0 src/test=5 <-- 스타터가 만들지 않는다 JdbcAdminOperationJournal src/main=0 src/test=2 <-- 스타터는 InMemory 를 쓴다 DebeziumOutboxEventRouter src/main=1 src/test=2 (DebeziumOutboxRecordMapper 내부 필드) DebeziumOutboxRecordMapper src/main=0 src/test=1 <-- 아무도 만들지 않는다 DebeziumOutboxProfile src/main=1 src/test=3 (자기 polling() 팩토리) # src/main 생성 상세: DebeziumOutboxProfile.java:44 new DebeziumOutboxProfile(RelayMode.POLLING, "", false) (자기 팩토리) DebeziumOutboxRecordMapper.java:32 new DebeziumOutboxEventRouter() (자기 필드) DebeziumOutboxRecordMapper.java:63 new DebeziumMappedRecord(...) (자기 반환) OutboxRetryScheduler.java:84 new OutboxRetryScheduler(OutboxProperties.defaults(), 5분) (자기 standard()) MessagingReliabilityAutoConfiguration:63 new OutboxRetryScheduler(properties, Duration.ofMinutes(1)) MessagingReliabilityAutoConfiguration:89 new OutboxRelay(...) MessagingReliabilityAutoConfiguration:109 new OutboxRelayWorker(relay, scheduler) MessagingReliabilityAutoConfiguration:141 new OutboxCleanupJob(outbox, properties, 20) # => 스타터 밖 생성은 전부 이 리프 내부의 자기 참조다. # JdbcOutboxRepository / OutboxEnvelopeFactory / JdbcAdminOperationJournal 은 # 애플리케이션이 직접 빈으로 등록해야 한다. # ---- 릴레이는 실제로 기동된다 (대조군) ---- # command: git grep -n "\.start()" -- src/messaging # MessagingOutboxRelayLifecycle.java:42 worker.start(); # => OutboxRelayWorker javadoc:18-21 이 기록한 과거 결함 # ("The relay, its retry scheduler and the attempt budget all existed and nothing ever called # runOnce")이 실제로 수정되어 배선까지 완료되어 있다. # 이 문서에서 확인한 "선언되고 배선되지 않은" 항목들과 대비되는 사례다. # ---- CDC 경로 전체가 소비되지 않는다 ---- # command: git grep -n "requireExactlyOneRelay|DebeziumOutboxProfile.polling|RelayMode" -- src # 전부 DebeziumOutboxProfile.java 자기 자신 + DebeziumOutboxRecordMapperTest # => requireExactlyOneRelay 프로덕션 호출부 0건. # DebeziumOutboxProfile 클래스 javadoc:9-13 은 # "the incompatibility is therefore enforced at startup instead of documented" # 라고 쓰지만, 기동 시 이것을 부르는 코드가 없다. # 두 릴레이가 동시에 켜지는 구성을 막는 주체가 없다. # ---- 백오프 지터가 복제본을 분산시키지 못한다 ---- # OutboxRetryScheduler.java:18-20 클래스 javadoc: # "Jitter is applied deterministically from the attempt count rather than randomly. Several relay # instances that all started at deployment time would otherwise synchronise their retries into a # thundering herd, and a random source would make the schedule impossible to test." # # OutboxRetryScheduler.java:98-109 # public Duration backoff(int consecutiveEmptyOrFailedPasses) { # if (consecutiveEmptyOrFailedPasses <= 0) return baseInterval; # int exponent = Math.min(consecutiveEmptyOrFailedPasses, 20); # long scaled = baseInterval.toMillis() << exponent; # long capped = Math.min(scaled, maxInterval.toMillis()); # long jittered = capped - (capped / 8) * (exponent % 3); # return Duration.ofMillis(Math.max(baseInterval.toMillis(), jittered)); # } # # jittered 는 exponent 만의 함수다. exponent 는 워커의 unproductivePasses 카운터에서 온다 # (OutboxRelayWorker.java:183, 192). # 동시에 배포되어 같은 브로커 장애를 겪는 복제본들은 같은 카운터 값을 갖게 되므로 # 같은 backoff 를 계산한다. 즉 지터가 인스턴스 간 위상차를 만들지 않는다. # 지터는 시도 횟수에 따라 값을 바꿀 뿐(0, 1/8, 2/8 감산), 인스턴스에 따라 바꾸지 않는다. # # 주: 행(row) 단위 백오프(nextAttemptAt)는 DB 컬럼 next_attempt_at 에 기록되므로 # 이 문제와 무관하다. 문제는 pass 단위 백오프에만 해당한다. # javadoc 이 말하는 "several relay instances … synchronise their retries" 는 pass 단위 얘기다.