Transactional Outbox Pattern
๋ง์ดํฌ๋ก์๋น์ค ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ ๋ณ๊ฒฝ๊ณผ ๋ฉ์์ง ๋ฐํ ์ผ๊ด์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํธ๋์ญ์ ๋ ์์๋ฐ์ค(Transactional Outbox) ํจํด์ ์ฌ์ฉํ ์ ์๋ค.
DB ํธ๋์ญ์
+ ์ด๋ฒคํธ ๋ฐํ
๋ ์์ ์ ํ๋์ ํธ๋์ญ์ ์ผ๋ก ๋ฌถ๋ ๋ฐฉ๋ฒ๋ ๊ณ ๋ คํด๋ณผ ์ ์์ง๋ง, ๋ฉ์์ง ๋ธ๋ก์ปค๊ฐ DB ํธ๋์ญ์ ์ ์๋ช ์ฃผ๊ธฐ์ ์ฐธ์ฌํ ์ ์๊ธฐ ๋๋ฌธ์ ํ์ค์ ์ผ๋ก ๋ถ๊ฐ๋ฅํ๋ค.
@Transactional
public void completeOrder(Long orderId) {
// 1. ์ฃผ๋ฌธ ์ํ๋ฅผ '์๋ฃ'๋ก ๋ณ๊ฒฝ
orderRepository.updateStatus(orderId, "COMPLETED");
// 2. ์ฃผ๋ฌธ ์๋ฃ ์ด๋ฒคํธ ๋ฐํ
kafkaProducer.send("order_completed", event);
}
// ๋ชจ๋ ์์
์ด ์ฑ๊ณตํด์ผ COMMIT์ ์ฝ๋์์, ๋ง์ฝ ์ด๋ฒคํธ ๋ฐํ(kafkaProducer.send)์ ์ฑ๊ณตํ์ง๋ง, ๊ทธ ์งํ ์ด๋ ํ ์ด์ ๋ก๋ DB COMMIT์ด ์คํจํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ํฉ์ด ๋ฐ์ํ๋ค.
๋ฉ์์ง ํ: ์ด๋ฏธ ์ฃผ๋ฌธ ์๋ฃ ์ด๋ฒคํธ๋ฅผ ์์ ํ์ฌ ๋ค๋ฅธ ์๋น์ค์ ์ ํ
๋ฐ์ดํฐ๋ฒ ์ด์ค:
COMMIT์คํจ๋ก ์ฃผ๋ฌธ ์ํ ๋ณ๊ฒฝ์ ๋กค๋ฐฑ(Rollback)
๊ฒฐ๊ณผ์ ์ผ๋ก, ๋ฉ์์ง๋ ๋ฐํ๋์์ง๋ง ๋ฐ์ดํฐ๋ ์๋ณต๋๋ ์ฌ๊ฐํ ๋ฐ์ดํฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ๊ฒ ๋๋ค.
ํธ๋์ญ์
๋ ์์๋ฐ์ค ํจํด
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ธ๋ถ ์์คํ ์ ๋ํ ํธ์ถ์ '๋ฐํํ ์ด๋ฒคํธ'(์์ ๋ฐ์ค)๋ผ๋ ๋ฐ์ดํฐ๋ก ๋ณํํ์ฌ, ๋น์ฆ๋์ค ๋ฐ์ดํฐ์ ๋์ผํ ํธ๋์ญ์ ๋ด์์ ์ ์ฅํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
์์์ ์ ์ฅ
UPDATE orders ...์INSERT INTO outbox ...๋ฅผ ํ๋์ ๋ก์ปฌ ํธ๋์ญ์ ์์ ์คํ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ACID ํน์ฑ ๋๋ถ์, ๋ ์์ ์ ๋ฐ๋์ ํจ๊ป ์ฑ๊ณตํ๊ฑฐ๋ ํจ๊ป ์คํจ
์์ ํ ๋ฐํ
outboxํ ์ด๋ธ์ ์ ์ฅ๋ ์ด๋ฒคํธ๋ฅผ ๋ณ๋์ ํ๋ก์ธ์ค๊ฐ ์ฝ์ด ์ธ๋ถ ๋ฉ์์ง ๋ธ๋ก์ปค๋ก ์ ๋ฌ
์ด๋ฒคํธ ๋ฐํ ๋ฐฉ์
outbox ํ
์ด๋ธ์ ์ด๋ฒคํธ๋ฅผ ์ธ๋ถ๋ก ๋ฐํํ๋ ๋ฐฉ์์ ํฌ๊ฒ ๋ ๊ฐ์ง๋ก ๋๋๋ค.
๋์ ๋ฐฉ์
์์ปค ํ๋ก์ธ์ค๊ฐ outbox ํ
์ด๋ธ์ ์ฃผ๊ธฐ์ ์ผ๋ก ์กฐํ(Polling)ํ์ฌ ์ด๋ฒคํธ ๋ฐํ
Debezium๊ณผ ๊ฐ์ CDC ๋๊ตฌ๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํธ๋์ญ์ ๋ก๊ทธ๋ฅผ ์ง์ ๊ฐ์ํ์ฌ ์ด๋ฒคํธ ๋ฐํ
์ฅ์
๋น๊ต์ ๋จ์ํ ๊ตฌํ, ์ธ๋ถ ๋๊ตฌ ์์กด์ฑ ์ต์ํ
์ค์๊ฐ์ ๊ฐ๊น์ด ์ด๋ฒคํธ ์ ํ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ ๋ถํ ์์
๋จ์
ํด๋ง ์ฃผ๊ธฐ์ ๋ฐ๋ฅธ ์ง์ฐ ๋ฐ์, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐํ ๋ถํ ์ ๋ฐ
CDC ๋๊ตฌ ๋์ ๋ฐ ์ด์์ ๋ณต์ก์ฑ, ์ธํ๋ผ ์์กด์ฑ ์ฆ๊ฐ
๊ตฌํ ์ฃผ์ฒด
์ ํ๋ฆฌ์ผ์ด์
์ธํ๋ผ / ํ๋ซํผ
๊ท๋ชจ๋ณ ์ํคํ
์ฒ ์ ๋ต
์ด๋ฒคํธ ๋ฐํ ํ๋ก์ธ์ค๋ฅผ ์ด๋ป๊ฒ ๊ตฌ์ฑํ ๊ฒ์ธ๊ฐ๋ ์ ํํ ์ ๋ต์ด ์์ผ๋ฉฐ, ์์คํ ์ ๊ท๋ชจ์ ์๊ตฌ ์ฌํญ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์๋ค.
์ด๊ธฐ
๋จ์ ์ค์ผ์ค๋ฌ
๋จ์ผ ์ธ์คํด์ค(์ค๋ ๋ ๋ถ๋ฆฌ)
๋ฎ์(๋ฆฌ์์ค ๊ณต์ )
์์ ๊ธฐ
ํธ๋์ญ์ ๋ ์์๋ฐ์ค
๋ ๋ฆฝ๋ ์์ปค ์ธ์คํด์ค
์ค๊ฐ(DB๋ง ๊ณต์ , ๋ฆฌ์์ค ๊ฒฉ๋ฆฌ)
๊ณ ๊ฐ์ฉ์ฑ
์์๋ฐ์ค + ๋ฉ์์ง ํ
์์ ๋ถ์ฐ๋ ์ธ์คํด์ค ๊ทธ๋ฃน
๋์(์๋ฒฝํ ๊ฒฉ๋ฆฌ ๋ฐ ํ์ฅ์ฑ)
๊ทธ ์ธ ๊ณ ๋ ค์ฌํญ
์ ๋ฌ ๋ณด์ฅ ์์ค๊ณผ ์๋น์ ๋ฉฑ๋ฑ์ฑ
์์๋ฐ์ค ํจํด์ ๋์ผํ ์ด๋ฒคํธ๊ฐ ๋ ๋ฒ ์ด์ ๋ฐํ๋ ์ ์๋
์ ์ด๋ ํ ๋ฒ ์ ๋ฌ(At-least-once delivery)์ ๋ณด์ฅ๋ฐ๋ผ์ ์ด๋ฒคํธ๋ฅผ ์์ ํ๋ ๋ชจ๋ ์๋น์๋ ๋ฉฑ๋ฑ์ฑ(idempotency) ๋ณด์ฅ ํ์
์ด๋ฒคํธ ์์ ๋ณด์ฅ
์ด๋ฒคํธ ๋ฐํ ์์๊ฐ ์ค์ํ ๊ฒฝ์ฐ,
outboxํ ์ด๋ธ์ ์ด๋ฒคํธ ์์ฑ ์๊ฐ์ ๊ธฐ๋กํ๊ณ ์์ปค๊ฐ ์ด๋ฅผ ๊ธฐ์ค์ผ๋ก ์์ฐจ์ ์ผ๋ก ๋ฐํํ๋๋ก ๊ตฌํCDC ๋ฐฉ์์ ํธ๋์ญ์ ๋ก๊ทธ ์์๋ฅผ ๋ฐ๋ฅด๋ฏ๋ก ์ด๋ฒคํธ ๋ฐ์ ์์๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ๋ณด์ฅ
์ด๋ฒคํธ ์คํค๋ง ๊ด๋ฆฌ
outboxํ ์ด๋ธ์ ์ ์ฅ๋๋ ์ด๋ฒคํธ ํ์ด๋ก๋์ ์คํค๋ง ๋ฒ์ ์ ๊ด๋ฆฌํ๋ ์ ๋ต ํ์์คํค๋ง๊ฐ ๋ณ๊ฒฝ๋ ๋ ํ์ ํธํ์ฑ์ ๋ณด์ฅํ์ง ์์ผ๋ฉด, ์ด์ ๋ฒ์ ์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํ๋ ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
ํด๋ง ๋ฐฉ์์ ์ต์ ํ
ํด๋ง ๋ฐํ์ ๋ฐฉ์ ์ฌ์ฉ ์,
outboxํ ์ด๋ธ์ ๋ํSELECT์ฟผ๋ฆฌ๊ฐ ๋ถํ๋ฅผ ์ ๋ฐํ์ง ์๋๋ก ์ฒ๋ฆฌ ์ํ๋ฅผ ๋ํ๋ด๋ ์ปฌ๋ผ์ ์ ์ ํ ์ธ๋ฑ์ค๋ฅผ ์์ฑ๋จ์ํ ์ญ์ ํ๋ ๋์ '๋ฐํ ์๋ฃ' ์ํ๋ก ์ ๋ฐ์ดํธํ๋ฉด, ๋ฐํ ์ด๋ ฅ์ ์ถ์ ํ๋ ๋ฐ ์ฉ์ดํ์ง๋ง ํ ์ด๋ธ ํฌ๊ธฐ๊ฐ ๊ณ์ ์ปค์ง๋ ๋จ์ ์กด์ฌ
Last updated
Was this helpful?