feat(retention): DB-per-service retention/cleanup 스케줄러 (구현 ② PR3)#72
Merged
Conversation
…② PR3) processed_events/outbox_events 만료 행을 ShedLock 배치 스케줄러로 정리하고, 보존기간이 D5 floor(4창 max) 미만이면 부팅 실패(fail-fast)한다 (ADR-0012 D5). - common: IdempotencyRetentionProperties(@AssertTrue cross-field)·OutboxRetentionProperties — 소유 서비스만 @EnableConfigurationProperties 활성(user 누출 없음) - 스케줄러 소유 매트릭스(물리 배치): processed=product/order/payment/notification, outbox=product/order/payment, user=0 - 배치 삭제: cutoff 1회 계산 + batch-size×max-batches-per-run 반복, per-batch @transactional - outbox predicate: status=PUBLISHED AND published_at<cutoff (PENDING/FAILED/NULL 보존) - notification 신규 ShedLock 인프라(dep·ShedLockConfig·@EnableScheduling·shedlock 테이블 V2) - 정책키 base-only(app.idempotency.*·app.outbox.retention) — ADR-0007 - V2: 삭제 기준 컬럼 인덱스
- floor cross-field 검증·max 계산(단위) + fail-fast(ApplicationContextRunner) - base-only 정책키 배치 가드(RetentionPolicyPlacementTest, ADR-0007) - processed/outbox 다중 batch 삭제 + PENDING/FAILED/published_at NULL/미만료 보존(통합) - 서비스×잡 매트릭스: product/order/payment=both, notification=processed only, user=0
- PR3(P16~P19) 서비스×잡 매트릭스·floor cross-field·배치 삭제·base-only·replay 트레이드오프 (plan-review 2 loop: 5→3건 수렴) - work diff-review audit(GW-2 3 P2 반영)
- TASKS ② 서비스별 DB 분리 🔄→✅ (PR3 #72, L-008/011 종결) + 구현 focus ③ 이동 - PHASE4 PR3 이력(retention/cleanup·floor fail-fast·매트릭스·notification ShedLock)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
한눈에
각 서비스가 이벤트를 주고받으면서 쌓아두는 두 종류의 이력 테이블이 계속 커지기만 하고 지워지지 않던 문제를 닫습니다.
processed_events— "이 이벤트 이미 처리했어" 라고 기록해두는 중복 방지(멱등성) 장부outbox_events— "이 이벤트 카프카로 보냈어" 라고 기록해두는 발행 이력둘 다 시간이 지나면 오래된 행은 쓸모가 없어지는데, 지금까지는 지우는 로직이 없어서 무한히 쌓였습니다. 이번 PR이 오래된 행을 주기적으로 자동 정리하는 스케줄러를 붙입니다. (구현 ② "서비스별 DB 분리"의 마지막 PR3, ADR-0012 §D5)
왜 필요한가
processed_events는 함부로 지우면 안 됩니다. 너무 일찍 지우면 "이미 처리한 이벤트"인데 장부에 기록이 없어서 같은 이벤트를 두 번 처리해버릴 수 있기 때문입니다.그래서 "얼마나 오래 보관해야 안전한가"에 하한선(floor)이 있습니다. ADR-0012 D5는 이 하한선을 아래 4가지 중 가장 큰 값 이상으로 잡으라고 정합니다.
보관 기간을 이 하한선보다 짧게 설정하면 애초에 서버가 뜨지 않도록(fail-fast) 막았습니다. 잘못된 설정이 조용히 배포돼서 중복 처리 사고로 이어지는 걸 원천 차단하는 게 목적입니다.
무엇을 했나
processed_events정리 스케줄러 — 보관 기간이 지난 행을 주기적으로 삭제합니다.outbox_events정리 스케줄러 — 이미 발행 완료(PUBLISHED)된 지 오래된 행만 삭제합니다.어떻게 작동하나
하한선 검사는 "설정값들을 서로 비교"하는 문제
단순히 "이 값이 null 이 아닌가" 같은 필드 하나짜리 검사가 아니라,
보관기간 ≥ max(4개 창)이라는 여러 필드를 엮은 비교입니다. 그래서common모듈에 설정 클래스(IdempotencyRetentionProperties)를 하나 두고@AssertTrue로 이 교차 비교를 검증합니다.이 설정은 cleanup 잡을 실제로 가진 서비스에서만 켜지도록(
@EnableConfigurationProperties) 해서, 이 잡이 필요 없는 서비스(user)에는 새어 들어가지 않습니다.삭제는 "한 번에 다 지우지 않고" 잘게 나눠서
오래된 행이 수백만 개 쌓여 있을 때
DELETE한 방으로 지우면 테이블이 오래 잠기고 트랜잭션이 길어져 다른 작업에 영향을 줍니다. 그래서:cutoff)을 실행 시작 때 한 번만 계산하고,batch-size×max-batches-per-run).outbox 는 "보낸 것만" 지운다 (유실 방지)
정리 조건은 딱
상태 = PUBLISHED 이고 발행시각이 기준보다 오래됨뿐입니다. 그 결과:즉 아직 보내야 할 이벤트가 실수로 지워지는 일은 없습니다.
서비스마다 "필요한 잡만" 돈다
정리 잡 클래스를 그 잡이 필요한 서비스 모듈에만 물리적으로 배치했습니다(기존 코드의 복제 패턴 그대로). 그래서 잘못된 잡이 엉뚱한 서비스에 뜨는 오배선이 구조적으로 불가능합니다.
설정은 전부 base 파일에
보관 기간·하한선·배치 크기 같은 동작 정책은 환경마다 달라지면 안 되므로 전부
application.yml(base)에 뒀고, 환경별 프로파일(-local/-k8s)에는 두지 않았습니다. (ADR-0007)기본값: 보관 7일 / 하한선 max(7일·24시간·7일·7일)=7일 →
7일 ≥ 7일통과.Test plan
./gradlew build test8모듈 BUILD SUCCESSFUL관련
docs/TASKS.md— 구현 ② 서비스별 DB 분리 (PR3, L-008/011 종결)docs/plans/task-impl2-db-per-service.md