Transactional Outbox Pattern
λΆμ° νκ²½μμμ λ¬Έμ λ βμ΄μ€ μ°κΈ°β λ¬Έμ μ λλ€.
DBμλ μ μ₯μ΄ λλλ°, μΉ΄νμΉ΄ λ©μμ§ λ°νμ΄ μ€ν¨νλ€λ©΄?
λ°λλ‘ λ©μμ§λ 보λλλ° DB μ μ₯μ μ€ν¨ν΄ λ‘€λ°±λλ€λ©΄?
μ΄λ¬ν λ°μ΄ν° λΆμΌμΉ μν©μμ μμ£Ό νμ©λλ κ°λ ₯ν ν¨ν΄μΈ νΈλμμ λ μμλ°μ€ ν¨ν΄μ΄ μμ΅λλ€.
Dual Write Problem
μ£Όλ¬Έ μλΉμ€μμ μ£Όλ¬Έ μμ±κ³Ό μ¬κ³ κ°μ μ΄λ²€νΈ λ°νμ λμμ μ§ννλ κ²½μ°λ₯Ό λ³΄κ² μ΅λλ€.
[μν©]
DB νΈλμμ :
μ£Όλ¬Έ λ°μ΄ν°λ₯Ό DBμ μ μ₯ (INSERT)
μΈλΆ νΈμΆ:
Kafkaλ‘ μ¬κ³ κ°μ μ΄λ²€νΈ λ°ν (SEND)
[λ¬Έμ ]
DB μ μ₯μ μ±κ³΅νκ³ , Kafka μ΄λ²€νΈ λ°ν μ§μ μ μλ²κ° λ€μ΄ β μ£Όλ¬Έμ μμ±λμ§λ§ μ¬κ³ λ μ€μ΄λ€μ§ μμ
Kafkaμ μ΄λ²€νΈλ λ°ννμ§λ§, DB μ μ₯ μ€ λ‘€λ°± β μ£Όλ¬Έμ μλλ° μ¬κ³ κ° μ€μ΄λ¬ (λ°μ΄ν° λΆμΌμΉ)
λ μμ μ μλ‘ λ€λ₯Έ μμ€ν μ΄κΈ° λλ¬Έμ νλμ μμμ μΈ νΈλμμ μΌλ‘ λ¬Άμ μ μμ΅λλ€.
μ΄μ²λΌ λ κ°μ μ΄μ’ μμ€ν μ λμμ μ°κΈ° μμ μ μ§ννλ€κ° μ€ν¨ν κ²½μ° μμΈ νΈλ€λ§μ μλͺ» ν΄μ£Όλ©΄ λ°μ΄ν° μΌκ΄μ±μ΄ κΉ¨μ§ μ μμ΅λλ€.
Transactional Outbox Pattern
νΈλμμ λ μμλ°μ€ ν¨ν΄μ λ¨μΌ DBμ λν΄μλ νΈλμμ λν μ²λ¦¬κ° κ°λ₯νλ€λ κ²μ νμ©ν©λλ€.
μ¦, βλ©μμ§ λ°νλ DB νΈλμμ μμμ μ²λ¦¬νμβλ μλ―Έμ λλ€.
Kafkaμ μ§μ λ©μμ§λ₯Ό 보λ΄λ λμ , βλ³΄λΌ λ©μΈμ§βλ₯Ό DB ν μ΄λΈ(Outbox)μ μ μ₯νλ κ²μ λλ€. μ΄λ κ²νλ©΄ λΉμ¦λμ€ λ‘μ§κ³Ό λ©μμ§ μ μ₯μ΄ κ°μ DB νΈλμμ μΌλ‘ λ¬Άμ΄λ―λ‘ μμμ±μ΄ 보μ₯λ©λλ€.
[λμ μ리]
νΈλμμ μμ
λΉμ¦λμ€ λ‘μ§: Order ν μ΄λΈμ μ£Όλ¬Έ μ 보 μ μ₯ (
INSERT)Outbox μ μ₯: Outbox ν μ΄λΈμ λ°νν μ΄λ²€νΈ μ 보(Payload) μ μ₯ (
INSERT)outbox_events table μ:
{ "eventType": "ORDER_CREATED", "payload": { ... }, "status": "PENDING" }
column_nametypedescriptionid
BIGINT
PK
event_type
VARCHAR
μ΄λ²€νΈ μ’ λ₯ ꡬλΆ
payload
JSON, TEXT
μ΄λ²€νΈ κ΄λ ¨ ν΅μ¬ λ°μ΄ν°
status
VARCHAR
μ΄λ²€νΈ μ²λ¦¬ μν
created_date
TIMESTAMP
μ΄λ²€νΈ μμ± μκ°
νΈλμμ 컀λ°: λ λ°μ΄ν°κ° λμμ μ μ₯λ¨ (μ€ν¨ μ λ λ€ λ‘€λ°±)
λ©μμ§ λ¦΄λ μ΄(Relay): λ³λμ νλ‘μΈμ€κ°
Outboxν μ΄λΈμ κ°μνλ€κ°, μλ‘μ΄ λ°μ΄ν°κ° 보μ΄λ©΄ Kafkaλ‘ μ€μ λ©μμ§λ₯Ό λ°ννκ³ μνλ₯ΌPUBLISHEDλ‘ λ³κ²½ν¨.
Outbox Pattern μμ
1. λ¬Έμ μν© (Dual Write)
κ²°κ³Ό:
μμΈκ° λ°μν΄μ DBμ μ£Όλ¬Έ μ 보λ λ‘€λ°±λμ΄ μ¬λΌμ‘μ΅λλ€.
νμ§λ§ μ΄λ―Έ λ°νλ Kafka λ©μμ§λ μ·¨μν μ μμ΄, μ¬κ³ μλΉμ€λ μ¬κ³ λ₯Ό μ€μ¬λ²λ¦¬λ μ¬κ³ κ° λ°μν©λλ€.
2. Outbox Pattern μ μ©
κ²°κ³Ό:
μμΈ λ°μ μ DB νΈλμμ μ΄ λ‘€λ°±λλ―λ‘, μ£Όλ¬Έ μ 보μ Outbox μ΄λ²€νΈκ° λͺ¨λ κΉλνκ² μ¬λΌμ§λλ€.
Kafka λ©μμ§λ μμ λ°ν μλμ‘°μ°¨ νμ§ μμμΌλ―λ‘ λ°μ΄ν° μΌκ΄μ±μ΄ μλ²½νκ² μ§μΌμ§λλ€.
μμλ°μ€ μ΄λ²€νΈ μ€ννκΈ°: Message Relay
DBμ μ μ₯λ μ΄λ²€νΈλ₯Ό κΊΌλ΄μ μ€μ λ‘ Kafkaμ 보λ΄μ£Όλ μν μ΄ νμν©λλ€. μ΄λ₯Ό Message RelayλΌκ³ ν©λλ€.
ꡬν λ°©μ 1: Polling Publisher
κ°μ₯ μ½κ³ μ§κ΄μ μΈ λ°©λ²μ λλ€. μ€μΌμ€λ¬κ° μ£ΌκΈ°μ μΌλ‘ DBλ₯Ό μ‘°νν΄μ μλ‘μ΄ μ΄λ²€νΈκ° μμΌλ©΄ λ©μμ§ λΈλ‘μ»€λ‘ λ°νν©λλ€.
μ₯μ : ꡬνμ΄ μ½κ³ DBλ§ μμΌλ©΄ λ©λλ€.
λ¨μ :
Polling μ£ΌκΈ°:
λ무 κΈΈλ©΄ μ€μκ°μ±μ΄ λ¨μ΄μ§ μ μμ΅λλ€.
κ·Έλ λ€κ³ μ£ΌκΈ°κ° λ무 μ§§μΌλ©΄ DBμ μ¬ν λΆνλ₯Ό μ£Όκ² λ κ²μ λλ€.
μ€λ³΅ λ°ν: μ¬λ¬ μλ²κ° λμμ Polling νλ©΄ κ°μ λ©μμ§λ₯Ό μ€λ³΅ λ°νν μ μμ΅λλ€. (μ€μΌμ€λ¬ λ½ λ±μΌλ‘ λ°©μ΄ νμ)
μ€μΌμ€λ§μ μν ν΄λ§ λ°©μμ΄ μλλλΌλ λ©μμ§ λ¦΄λ μ΄λ₯Ό ν λ μ£Όμν΄μΌ ν μ¬νμ 무μμΌκΉμ?
κ·Έμ€ νλλ λ©μμ§ λ¦΄λ¦¬μμ΄ μ€ν¨νμ λ μ΄λ»κ² μ²λ¦¬ν κ²μΈμ§ μ λλ€. (ex: μΉ΄νμΉ΄ λ©μμ§ λ°μ μ€ν¨)
μ¬μλλ₯Ό νλ λ± λ€μν μλ¨μ λμ
ν΄μΌ νκ³ , Posion Pillμ΄λΌκ³ λΆλ₯΄λ λ©μμ§ μμ²΄κ° μ ν¨νμ§ μμμ 무쑰건 μ€ν¨νλ λ©μμ§λ 무νμ μ¬μλνλλ‘ κ΅¬ννλ©΄ μ λ©λλ€.
μ΄λ° λΆλΆμ κ³ λ €νλ€λ©΄ μμλ°μ€ μ΄λ²€νΈ ν μ΄λΈμ μ¬μλ νμλ₯Ό ν¨κ» μ μ₯νλ κ²λ μ’μ λ°©λ²μ΄ λ μ μμ΅λλ€.
ꡬν λ°©μ 2: Transaction Log Tailing (CDC)
μ€λ¬΄μμ λκ·λͺ¨ νΈλν½ μ²λ¦¬μ κΆμ₯λλ λ°©μμ λλ€. Debezium κ°μ CDC(Change Data Capture) λꡬλ₯Ό μ¬μ©ν©λλ€.
λμ: μ ν리μΌμ΄μ μ
Outboxν μ΄λΈμINSERTλ§ ν©λλ€. CDC λκ΅¬κ° DBμ νΈλμμ λ‘κ·Έ(Binlog)λ₯Ό μ€μκ°μΌλ‘ κ°μνλ€κ°, λ³κ²½ μ¬νμ κ°μ§νμ¬ μλμΌλ‘ Kafkaμ λ©μμ§λ₯Ό μ΄μ€λλ€.μ₯μ : μ€μκ°μ±μ΄ λκ³ μ ν리μΌμ΄μ λΆνκ° μμ΅λλ€.
λ¨μ : μΈνλΌ κ΅¬μ±(Kafka Connect, Debezium λ±)μ΄ λ³΅μ‘ν©λλ€.
Summary
Transactional Outbox Patternμ λ©μμ§ μ μ€μ λ§λ κ°λ ₯ν λꡬμ§λ§, λ§λ₯μ μλλλ€.
λ©μμ§ μμ:
Polling λ°©μμ΄λ λ©ν° μ€λ λ νκ²½μμλ λ©μμ§ λ°ν μμκ° λ€λ°λ μ μμ΅λλ€.
μ격ν μμκ° νμνλ€λ©΄ Kafka νν°μ λ μ λ΅κ³Ό ν¨κ» κ³ λ €ν΄μΌ ν©λλ€.
λ©±λ±μ±(Idempotency) νμ:
Relayκ° λ©μμ§λ₯Ό 보λ΄κ³ DB μνλ₯Ό
PUBLISHEDλ‘ λ°κΎΈκΈ° μ§μ μ μ£½λλ€λ©΄? μ¬μ€ν μ λ©μμ§κ° μ€λ³΅ λ°νλ μ μμ΅λλ€.Consumer μͺ½μμλ λ°λμ μ€λ³΅ λ©μμ§λ₯Ό κ±Έλ¬λ΄λ(λ©±λ±μ±) μ²λ¦¬λ₯Ό ν΄μΌ ν©λλ€.
Last updated