Files
llm-wiki/raw/official-docs/outbox-skip-locked-microservices-io.md

128 lines
11 KiB
Markdown

---
title: Transactional Outbox Pattern (microservices.io / Chris Richardson)
source_type: official-doc
url: https://microservices.io/patterns/data/transactional-outbox.html
archive_url:
status: raw
confidence: medium
tags: [ca-outbox-pattern, outbox, skip-locked, polling, baseline, microservices-io, official-doc]
related_projects: [ca-skeleton-operational-contract]
related_branches: [feature-domain-event-outbox-contract, feature-background-job-async-contract]
created: 2026-05-22
last_reviewed: 2026-05-27
---
# Transactional Outbox Pattern (microservices.io)
> Layer: `raw/official-docs/` — Chris Richardson microservices.io "Pattern: Transactional outbox" 의 **원문 발췌·출처 기록**.
> ca-tmpl 이 채택한 **DB outbox + polling + FOR UPDATE SKIP LOCKED** 결정의 baseline 정의 문서. 다른 대안들과 비교할 기준점.
## Parent / 활용 branch (필수)
| Branch | 이 자료가 정당화하는 결정 |
|---|---|
| [[raw/branch-notes/feature-domain-event-outbox-contract]] | Topic 3 — Outbox Pattern baseline 채택 (SKIP LOCKED polling) 의 1차 근거 — Chris Richardson 원형 정의 |
| [[raw/branch-notes/feature-background-job-async-contract]] | Background job / async event 발행에서 outbox + polling 으로 dual-write 회피 결정 근거 |
| [[raw/project-notes/ca-skeleton-operational-contract]] | §18 (Control Plane Contract) / §19 (Domain Application Readiness Contract) 의 outbox 발행 채택안 baseline |
## 컨텍스트
ca-tmpl 이 채택한 **DB outbox + polling + FOR UPDATE SKIP LOCKED** 결정의 baseline 정의 문서. business write + event write 를 동일 트랜잭션 안에서 묶고, 별도 Message Relay 가 outbox 를 polling 하여 broker 로 발행하는 패턴.
## 출처 / Source
- 원본 URL: https://microservices.io/patterns/data/transactional-outbox.html
- 보조 URL (Polling publisher 패턴): https://microservices.io/patterns/data/polling-publisher.html
- 아카이브 URL: (미수집)
- 저자 / 조직: Chris Richardson — microservices.io
- 발행일: rolling docs (페이지 자체에 명시 없음)
- 마지막 확인일 (capture): 2026-05-22
- 마지막 재검증 시도: 2026-05-27
- **[2026-05-25 capture]**: user 가 2026-05-22 수집한 인용 원형 유지 (재검증 보류 마커는 2026-05-25 부여된 상태).
- **재검증 결과 [2026-05-27 verified attempt]**:
- 1차 URL `https://microservices.io/patterns/data/transactional-outbox.html` WebFetch 성공. 페이지에는 "Pattern: Transactional outbox" 제목 + Context/Problem/Forces/Solution/Result context/Related patterns/Learn more 섹션 존재.
- C1 (Solution): user 수집본 verbatim 과 **불일치**. 실제 페이지 표현은 "The solution is for the service that sends the message to first store the message in the database as part of the transaction that updates the business entities." → 의미는 동일하나 wording 다름. → user 수집본은 paraphrase 로 reclassify.
- C2 (Message Relay): 부분 일치. 실제 페이지 "A separate process then sends the messages to the message broker." → wording 차이.
- C3, C4 (Polling drawback, SKIP LOCKED): 1차 URL 의 본문 발췌에서 NOT FOUND. 보조 페이지 `https://microservices.io/patterns/data/polling-publisher.html` 도 WebFetch 했으나 두 문장 모두 NOT FOUND (해당 페이지는 high-level pattern description 만 포함).
- **재검증 한계**: WebFetch 페이지 발췌 범위가 항상 페이지 전체를 노출하지는 않을 수 있어 NOT FOUND 가 absence 의 결정적 증거는 아니나, 4개 quote 모두 verbatim 형태로 확인되지 않음 → 본 문서 인용은 모두 `needs-confirmation` Strength **유지** (Strength 상향 없음). C1/C2 는 paraphrase 가능성 노출, C3/C4 는 출처 페이지 재확정 필요.
## 핵심 인용 / Key quotes (verbatim claimed, user 수집본 [2026-05-25 capture] — [2026-05-27 verified attempt] verbatim 재확인 실패 / NOT FOUND, 아래 §재검증 결과 참조)
> [§Transactional outbox — Solution] "As part of the database transaction that updates the business entity, the service inserts a message into an OUTBOX table."
> — [2026-05-27 verified attempt]: 실제 페이지 wording 은 "The solution is for the service that sends the message to first store the message in the database as part of the transaction that updates the business entities." → 의미 동일, verbatim 불일치 → paraphrase 로 처리.
> [§Message Relay] "A separate Message Relay process publishes the events inserted into database to a message broker."
> — [2026-05-27 verified attempt]: 실제 페이지 표현 "A separate process then sends the messages to the message broker." → 부분 일치, wording 차이.
> [§Polling publisher — Drawback] "Polling the database is a simple approach that works reasonably well at low scale. The disadvantage is that frequently polling the database can be expensive."
> — [2026-05-27 verified attempt]: 1차 URL 및 보조 페이지 `polling-publisher.html` 본문 발췌에서 NOT FOUND.
> [§SKIP LOCKED parallelism] "Some databases, such as PostgreSQL and MySQL, support the SKIP LOCKED clause which enables multiple instances of the Message Relay to safely poll the OUTBOX table in parallel."
> — [2026-05-27 verified attempt]: 1차 URL 및 보조 페이지 `polling-publisher.html` 본문 발췌에서 NOT FOUND.
## Claims Extracted / 추출된 주장
> 본 raw 의 모든 quote 는 2026-05-22 user 수집본이며 2026-05-27 WebFetch 차단으로 verbatim 재확인 보류 → 전체 `needs-confirmation`.
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|---|---|---|---|---|---|
| OUTBOX-MIO-C1 | business entity 를 변경하는 DB 트랜잭션의 일부로 OUTBOX 테이블에 메시지를 insert 한다 (원자성 확보) | [§Transactional outbox — Solution] "As part of the database transaction that updates the business entity, the service inserts a message into an OUTBOX table." | `needs-confirmation` | 단일 RDB 트랜잭션으로 business write + outbox write 가 가능한 모든 환경 | OUTBOX 스키마 (payload, aggregate_id, status 등) 의 구체적 컬럼 설계는 본 인용에 포함되지 않음 |
| OUTBOX-MIO-C2 | OUTBOX 테이블의 이벤트를 broker 로 발행하는 별도의 Message Relay 프로세스가 존재한다 | [§Message Relay] "A separate Message Relay process publishes the events inserted into database to a message broker." | `needs-confirmation` | outbox 패턴의 모든 변형 (polling, CDC 모두 Message Relay 를 가짐) | Message Relay 가 polling 인지 CDC 인지 본 인용은 중립 — 둘 다 가능 |
| OUTBOX-MIO-C3 | DB polling 방식은 단순하며 저-규모에서 합리적으로 동작하나, 자주 polling 하면 비용이 비싸다 | [§Polling publisher — Drawback] "Polling the database is a simple approach that works reasonably well at low scale. The disadvantage is that frequently polling the database can be expensive." | `needs-confirmation` | RDB outbox + polling Message Relay 변형 | "low scale" / "expensive" 의 정확한 임계 (TPS, interval) 는 본 인용에 없음 — 환경별 측정 필요 |
| OUTBOX-MIO-C4 | PostgreSQL 과 MySQL 같은 일부 DB 는 SKIP LOCKED 절을 지원하며, 이로 인해 여러 Message Relay 인스턴스가 OUTBOX 테이블을 병렬로 안전하게 polling 할 수 있다 | [§SKIP LOCKED parallelism] "Some databases, such as PostgreSQL and MySQL, support the SKIP LOCKED clause which enables multiple instances of the Message Relay to safely poll the OUTBOX table in parallel." | `needs-confirmation` | Postgres 9.5+ / MySQL 8.0+ 환경 | "safely poll in parallel" 이 순서 보장을 포함하지 않음 — 본 인용은 "안전" = lock contention 회피 의미만 |
### Strength 정책
본 문서의 모든 claim 은 `needs-confirmation`. 이유: 2026-05-27 재검증 시점에 WebFetch 가 차단되어 user 수집본 (2026-05-22) 의 verbatim 일치 여부를 공식 페이지 대조로 확인 못함. wiki 승급 전 재확인 필수.
## Usage Boundaries / 적용 경계
- **이 자료가 직접 증명하는 것** (재확인 시):
- `OUTBOX-MIO-C1`: outbox 패턴의 핵심 — business write + outbox write 의 트랜잭션적 결합
- `OUTBOX-MIO-C2`: Message Relay 분리 (구체 구현은 polling/CDC 중립)
- `OUTBOX-MIO-C3`: polling 의 trade-off (단순성 vs 비용)
- `OUTBOX-MIO-C4`: SKIP LOCKED 가 polling Message Relay 의 수평 확장 메커니즘
- **이 자료가 증명하지 않는 것**:
- outbox + polling 의 정확한 lag 수치
- OUTBOX schema 설계 (payload, status flag, partition key)
- SKIP LOCKED 가 순서 보장을 제공한다는 주장 (오히려 본 인용 + skip-locked-postgres-docs 의 "inconsistent view" 와 결합 시 순서 비보장)
- exactly-once delivery — outbox + polling 은 at-least-once (별도 근거 필요)
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
- ca-tmpl 의 polling interval 결정 (TPS · lag · DB 부하 균형)
- 다중 publisher 인스턴스 수 (운영 단순성 vs 처리량)
- outbox archive / partition / vacuum 정책 (hot table 방지)
## 메모 / Notes (내 프로젝트 해석 — 직접 인용 아님)
- 적용 시나리오: 모놀리식 / MSA 모두. CDC 인프라가 없거나 도입을 미루고 싶을 때, 한 DB 트랜잭션 안에서 business write + event write 를 묶고 싶을 때.
- 장점:
- 인프라 추가 없음 (DB 만 있으면 됨)
- business write 와 event write 의 원자성 보장 → dual-write 문제 회피
- SKIP LOCKED 로 다중 publisher 인스턴스 수평 확장 가능
- 구현 단순, 디버깅 쉬움 (SQL 로 직접 확인 가능)
- 단점:
- polling lag (interval 만큼 지연)
- polling 부하 (interval 을 줄이면 DB I/O 증가)
- outbox 테이블이 hot table 이 되기 쉬움 → 주기적 archive/delete 필요
- 순서 보장은 publisher 단일 인스턴스 또는 partition key 설계가 필요
- ca-tmpl (SKIP LOCKED polling) 과의 차이: **동일 패턴**. baseline.
- 운영 복잡도: 낮음. 추가 컴포넌트 없음.
- exactly-once / at-least-once 보장 수준: **at-least-once**. broker 발행 후 outbox row 삭제/마킹 사이에 크래시 시 중복 발행 가능 → consumer 측 idempotency 필수.
- 외부 의존성 추가 여부: 없음.
- 대안 그룹 (Topic 3 — Outbox Pattern, 대안 6종): **SKIP LOCKED polling** / Debezium CDC / Kafka Connect SMT / Dual-write [금지] / Event sourcing / Spring @TransactionalEventListener
- 본 source 의 위치: ca-tmpl 채택안 baseline (SKIP LOCKED polling, Chris Richardson 원형)
## Related / 관련
- 같은 주제 다른 official-doc:
- [[raw/official-docs/outbox-debezium-official-docs]] (대안 1: CDC)
- [[raw/official-docs/skip-locked-postgres-docs]] (메커니즘 — Postgres 공식)
- [[raw/official-docs/dual-write-antipattern-microservices-io]] (negative reference)
- 같은 주제 company-tech-blog:
- [[raw/company-tech-blogs/outbox-woowahan-techblog-pattern]]
- 인용하는 branch / project:
- [[raw/branch-notes/feature-domain-event-outbox-contract]]
- [[raw/branch-notes/feature-background-job-async-contract]]
- [[raw/project-notes/ca-skeleton-operational-contract]]
- 인용한 wiki 요약: (미작성)