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)

๊ฒฐ๊ณผ์ ์œผ๋กœ, ๋ฉ”์‹œ์ง€๋Š” ๋ฐœํ–‰๋˜์—ˆ์ง€๋งŒ ๋ฐ์ดํ„ฐ๋Š” ์›๋ณต๋˜๋Š” ์‹ฌ๊ฐํ•œ ๋ฐ์ดํ„ฐ ๋ถˆ์ผ์น˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.

ํŠธ๋žœ์žญ์…”๋„ ์•„์›ƒ๋ฐ•์Šค ํŒจํ„ด

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์™ธ๋ถ€ ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ํ˜ธ์ถœ์„ '๋ฐœํ–‰ํ•  ์ด๋ฒคํŠธ'(์•„์›ƒ ๋ฐ•์Šค)๋ผ๋Š” ๋ฐ์ดํ„ฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ, ๋น„์ฆˆ๋‹ˆ์Šค ๋ฐ์ดํ„ฐ์™€ ๋™์ผํ•œ ํŠธ๋žœ์žญ์…˜ ๋‚ด์—์„œ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•œ๋‹ค.

  1. ์›์ž์  ์ €์žฅ

    • UPDATE orders ... ์™€ INSERT INTO outbox ... ๋ฅผ ํ•˜๋‚˜์˜ ๋กœ์ปฌ ํŠธ๋žœ์žญ์…˜์—์„œ ์‹คํ–‰

    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ACID ํŠน์„ฑ ๋•๋ถ„์—, ๋‘ ์ž‘์—…์€ ๋ฐ˜๋“œ์‹œ ํ•จ๊ป˜ ์„ฑ๊ณตํ•˜๊ฑฐ๋‚˜ ํ•จ๊ป˜ ์‹คํŒจ

  2. ์•ˆ์ „ํ•œ ๋ฐœํ–‰

    • outbox ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ ์ด๋ฒคํŠธ๋ฅผ ๋ณ„๋„์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฝ์–ด ์™ธ๋ถ€ ๋ฉ”์‹œ์ง€ ๋ธŒ๋กœ์ปค๋กœ ์ „๋‹ฌ

์ด๋ฒคํŠธ ๋ฐœํ–‰ ๋ฐฉ์‹

outbox ํ…Œ์ด๋ธ”์˜ ์ด๋ฒคํŠธ๋ฅผ ์™ธ๋ถ€๋กœ ๋ฐœํ–‰ํ•˜๋Š” ๋ฐฉ์‹์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€๋กœ ๋‚˜๋‰œ๋‹ค.

๊ตฌ๋ถ„
ํด๋ง ๋ฐœํ–‰์ž(Polling Publisher)
๋ณ€๊ฒฝ ๋ฐ์ดํ„ฐ ์บก์ฒ˜(Change Data Capture)

๋™์ž‘ ๋ฐฉ์‹

์›Œ์ปค ํ”„๋กœ์„ธ์Šค๊ฐ€ outbox ํ…Œ์ด๋ธ”์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์กฐํšŒ(Polling)ํ•˜์—ฌ ์ด๋ฒคํŠธ ๋ฐœํ–‰

Debezium๊ณผ ๊ฐ™์€ CDC ๋„๊ตฌ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ํŠธ๋žœ์žญ์…˜ ๋กœ๊ทธ๋ฅผ ์ง์ ‘ ๊ฐ์‹œํ•˜์—ฌ ์ด๋ฒคํŠธ ๋ฐœํ–‰

์žฅ์ 

๋น„๊ต์  ๋‹จ์ˆœํ•œ ๊ตฌํ˜„, ์™ธ๋ถ€ ๋„๊ตฌ ์˜์กด์„ฑ ์ตœ์†Œํ™”

์‹ค์‹œ๊ฐ„์— ๊ฐ€๊นŒ์šด ์ด๋ฒคํŠธ ์ „ํŒŒ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ๋ถ€ํ•˜ ์—†์Œ

๋‹จ์ 

ํด๋ง ์ฃผ๊ธฐ์— ๋”ฐ๋ฅธ ์ง€์—ฐ ๋ฐœ์ƒ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ ๋ถ€ํ•˜ ์œ ๋ฐœ

CDC ๋„๊ตฌ ๋„์ž… ๋ฐ ์šด์˜์˜ ๋ณต์žก์„ฑ, ์ธํ”„๋ผ ์˜์กด์„ฑ ์ฆ๊ฐ€

๊ตฌํ˜„ ์ฃผ์ฒด

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

์ธํ”„๋ผ / ํ”Œ๋žซํผ

๊ทœ๋ชจ๋ณ„ ์•„ํ‚คํ…์ฒ˜ ์ „๋žต

์ด๋ฒคํŠธ ๋ฐœํ–‰ ํ”„๋กœ์„ธ์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•  ๊ฒƒ์ธ๊ฐ€๋Š” ์ •ํ™•ํ•œ ์ •๋‹ต์ด ์—†์œผ๋ฉฐ, ์‹œ์Šคํ…œ์˜ ๊ทœ๋ชจ์™€ ์š”๊ตฌ ์‚ฌํ•ญ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.

๋‹จ๊ณ„
ํ•ต์‹ฌ ๊ธฐ์ˆ 
์ธํ”„๋ผ ๊ตฌ์กฐ
๊ฒฉ๋ฆฌ ์ˆ˜์ค€

์ดˆ๊ธฐ

๋‹จ์ˆœ ์Šค์ผ€์ค„๋Ÿฌ

๋‹จ์ผ ์ธ์Šคํ„ด์Šค(์Šค๋ ˆ๋“œ ๋ถ„๋ฆฌ)

๋‚ฎ์Œ(๋ฆฌ์†Œ์Šค ๊ณต์œ )

์•ˆ์ •๊ธฐ

ํŠธ๋žœ์žญ์…”๋„ ์•„์›ƒ๋ฐ•์Šค

๋…๋ฆฝ๋œ ์›Œ์ปค ์ธ์Šคํ„ด์Šค

์ค‘๊ฐ„(DB๋งŒ ๊ณต์œ , ๋ฆฌ์†Œ์Šค ๊ฒฉ๋ฆฌ)

๊ณ ๊ฐ€์šฉ์„ฑ

์•„์›ƒ๋ฐ•์Šค + ๋ฉ”์‹œ์ง€ ํ

์™„์ „ ๋ถ„์‚ฐ๋œ ์ธ์Šคํ„ด์Šค ๊ทธ๋ฃน

๋†’์Œ(์™„๋ฒฝํ•œ ๊ฒฉ๋ฆฌ ๋ฐ ํ™•์žฅ์„ฑ)

๊ทธ ์™ธ ๊ณ ๋ ค์‚ฌํ•ญ

  • ์ „๋‹ฌ ๋ณด์žฅ ์ˆ˜์ค€๊ณผ ์†Œ๋น„์ž ๋ฉฑ๋“ฑ์„ฑ

    • ์•„์›ƒ๋ฐ•์Šค ํŒจํ„ด์€ ๋™์ผํ•œ ์ด๋ฒคํŠธ๊ฐ€ ๋‘ ๋ฒˆ ์ด์ƒ ๋ฐœํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์ ์–ด๋„ ํ•œ ๋ฒˆ ์ „๋‹ฌ(At-least-once delivery)์„ ๋ณด์žฅ

    • ๋”ฐ๋ผ์„œ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ ํ•˜๋Š” ๋ชจ๋“  ์†Œ๋น„์ž๋Š” ๋ฉฑ๋“ฑ์„ฑ(idempotency) ๋ณด์žฅ ํ•„์š”

  • ์ด๋ฒคํŠธ ์ˆœ์„œ ๋ณด์žฅ

    • ์ด๋ฒคํŠธ ๋ฐœํ–‰ ์ˆœ์„œ๊ฐ€ ์ค‘์š”ํ•œ ๊ฒฝ์šฐ, outbox ํ…Œ์ด๋ธ”์— ์ด๋ฒคํŠธ ์ƒ์„ฑ ์‹œ๊ฐ„์„ ๊ธฐ๋กํ•˜๊ณ  ์›Œ์ปค๊ฐ€ ์ด๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ˆœ์ฐจ์ ์œผ๋กœ ๋ฐœํ–‰ํ•˜๋„๋ก ๊ตฌํ˜„

    • CDC ๋ฐฉ์‹์€ ํŠธ๋žœ์žญ์…˜ ๋กœ๊ทธ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฅด๋ฏ€๋กœ ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์ˆœ์„œ๋ฅผ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ณด์žฅ

  • ์ด๋ฒคํŠธ ์Šคํ‚ค๋งˆ ๊ด€๋ฆฌ

    • outbox ํ…Œ์ด๋ธ”์— ์ €์žฅ๋˜๋Š” ์ด๋ฒคํŠธ ํŽ˜์ด๋กœ๋“œ์˜ ์Šคํ‚ค๋งˆ ๋ฒ„์ „์„ ๊ด€๋ฆฌํ•˜๋Š” ์ „๋žต ํ•„์š”

    • ์Šคํ‚ค๋งˆ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ๋ณด์žฅํ•˜์ง€ ์•Š์œผ๋ฉด, ์ด์ „ ๋ฒ„์ „์˜ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ ๋ฐœ์ƒ ๊ฐ€๋Šฅ

  • ํด๋ง ๋ฐฉ์‹์˜ ์ตœ์ ํ™”

    • ํด๋ง ๋ฐœํ–‰์ž ๋ฐฉ์‹ ์‚ฌ์šฉ ์‹œ, outbox ํ…Œ์ด๋ธ”์— ๋Œ€ํ•œ SELECT ์ฟผ๋ฆฌ๊ฐ€ ๋ถ€ํ•˜๋ฅผ ์œ ๋ฐœํ•˜์ง€ ์•Š๋„๋ก ์ฒ˜๋ฆฌ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ปฌ๋Ÿผ์— ์ ์ ˆํ•œ ์ธ๋ฑ์Šค๋ฅผ ์ƒ์„ฑ

    • ๋‹จ์ˆœํžˆ ์‚ญ์ œํ•˜๋Š” ๋Œ€์‹  '๋ฐœํ–‰ ์™„๋ฃŒ' ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ฉด, ๋ฐœํ–‰ ์ด๋ ฅ์„ ์ถ”์ ํ•˜๋Š” ๋ฐ ์šฉ์ดํ•˜์ง€๋งŒ ํ…Œ์ด๋ธ” ํฌ๊ธฐ๊ฐ€ ๊ณ„์† ์ปค์ง€๋Š” ๋‹จ์  ์กด์žฌ

Last updated

Was this helpful?