1. Pertanyaan
Setelah pengguna mengirimkan pesanan, layanan inventaris, pembayaran, pengiriman, dan kupon harus menyelesaikan pemenuhan pesanan tersebut. Langkah apa pun dapat gagal karena aturan bisnis atau gangguan jaringan, dan transaksi jarak jauh (remote transaction) yang telah di-commit tidak dapat di-rollback. Rancang Saga yang berakhir pada CONFIRMED atau CANCELLED, dengan menjelaskan setiap transaksi lokal dan kompensasinya.
2. Batasan dan klarifikasi
- Setiap layanan hanya menjalankan transaksi ACID lokal terhadap basis datanya sendiri.
- Konsistensi akhir (eventual consistency) dapat diterima, tetapi penahanan (hold) inventaris dan pembayaran tidak boleh berlangsung selamanya.
- Percobaan ulang (retries) dapat mengeksekusi langkah lebih dari sekali; layanan harus melindungi status dengan kunci idempotensi (idempotency keys).
- Bedakan kegagalan teknis yang dapat dicoba ulang, penolakan bisnis yang tidak dapat dicoba ulang, dan kegagalan kompensasi yang memerlukan intervensi manusia.
3. Pendekatan utama
Saga mendekomposisi transaksi panjang menjadi transaksi lokal yang berurutan T1 ... Tn. Setiap langkah yang berhasil mencatat progres dan memicu langkah berikutnya; jika langkah selanjutnya gagal, langkah-langkah yang telah selesai menjalankan kompensasi Ck ... C1 dalam urutan terbalik. Kompensasi adalah operasi bisnis baru, seperti melepaskan inventaris, membatalkan otorisasi pembayaran, membatalkan pengiriman, atau mengembalikan kupon, dan bukan merupakan rollback basis data.
Saga yang diorkestrasi (orchestrated Saga) memiliki koordinator persisten yang menyimpan status dan tindakan berikutnya, yang cocok untuk persyaratan alur kerja eksplisit, batas waktu (timeouts), dan intervensi manusia. Koreografi berbasis peristiwa (event-driven choreography) menghilangkan koordinator pusat tetapi membuat visibilitas dan kontrol siklus menjadi lebih sulit. Pada kedua gaya tersebut, sertakan saga_id, step_id, versi, dan kunci idempotensi pada setiap perintah dan peristiwa.
4. Referensi implementasi
start(order):
saga = create_saga(order.id, state="RESERVE_STOCK")
dispatch(saga, "ReserveStock")
on_step_result(saga_id, step_id, result):
saga = load_and_lock(saga_id)
require result.version == saga.version + 1
if result.success:
saga.completed_steps.append(step_id)
saga.version += 1
next = next_step(saga)
persist(saga)
dispatch(next) if next else finish_confirmed(saga)
else if result.business_rejection:
saga.state = "COMPENSATING"
persist(saga)
dispatch(compensation_for_last_completed(saga))
else:
schedule_retry_or_timeout(saga, step_id)
on_compensation_result(saga_id, step_id, result):
record_attempt(saga_id, step_id, result)
if result.success:
dispatch(previous_compensation(saga))
else:
mark_manual_intervention(saga, reason=result.error)5. Konsistensi dan kebenaran
Status koordinator harus tahan lama (durable), jika tidak, kerusakan sistem (crash) dapat menghilangkan tindakan berikutnya. Setiap perintah dan peristiwa menggunakan kunci idempotensi; konsumen mencatat step_id yang telah diproses sebelum melakukan commit terhadap perubahan bisnisnya, mencegah percobaan ulang mereservasi inventaris dua kali. Ada juga celah pengiriman (send gap) antara menyelesaikan langkah dan menerbitkan perintah berikutnya, sehingga diperlukan outbox, antrean yang andal, atau CDC untuk visibilitas akhir.
Kompensasi biasanya berjalan dalam urutan terbalik dari langkah yang berhasil, tetapi tidak setiap tindakan memiliki kebalikan yang pasti. Beberapa dampak memerlukan penyesuaian bisnis, seperti pengembalian dana alih-alih menarik kembali notifikasi yang telah dikirim. Kueri status harus menampilkan langkah saat ini, langkah yang telah selesai, jumlah percobaan ulang, dan alasan intervensi manusia, alih-alih melaporkan "sedang diproses" sebagai keberhasilan.
6. Tindak lanjut dan jebakan
- Jangan mendeskripsikan Saga sebagai rollback atomik di seluruh basis data; Saga menyediakan konsistensi akhir yang dapat dipulihkan (recoverable eventual consistency).
- Kompensasi juga bisa gagal, jadi tambahkan percobaan ulang, dead letters, peringatan (alerts), dan pengambilalihan oleh manusia alih-alih perulangan otomatis tanpa akhir.
- Penguncian terdistribusi global memperluas domain kegagalan dan tidak dapat membatalkan commit bisnis jarak jauh.
- Tentukan TTL untuk penahanan inventaris dan pembayaran; lepaskan dengan timer atau peristiwa saat Saga kedaluwarsa.
7. Bacaan lanjutan
Bandingkan orkestrasi dengan koreografi: orkestrasi memusatkan status, urutan, dan batas waktu, sedangkan koreografi memisahkan (decouple) layanan melalui peristiwa tetapi membuat penelusuran menyeluruh (end-to-end tracing) menjadi lebih sulit. Diskusikan efek samping yang tidak dapat dikompensasi, penguncian semantik (semantic locks), konflik versi, log audit, dan kapan transaksi basis data tunggal menjadi lebih sederhana.
8. Poin penilaian wawancara
Mampu mendekomposisi transaksi lokal
Kandidat harus mendaftar langkah-langkah inventaris, pembayaran, pengiriman, dan kupon, serta menyatakan bahwa setiap layanan hanya melakukan commit pada transaksi lokalnya sendiri.
Mampu merancang mesin status kompensasi
Kandidat harus menunjukkan jalur keberhasilan, kegagalan, dan kompensasi terbalik, membedakan penolakan bisnis, percobaan ulang teknis, dan pengambilalihan oleh manusia.
Mampu menangani idempotensi dan perpesanan yang andal
Kandidat harus menggunakan saga_id, step_id, kunci idempotensi, dan outbox atau antrean yang andal untuk menangani kerusakan koordinator dan celah pengiriman perintah.
Mampu menyatakan batasan bisnis
Kandidat harus mengakui bahwa kompensasi bukanlah rollback dan mendiskusikan TTL, efek samping yang tidak dapat dikompensasi, kueri status, serta pengalaman pengguna terhadap konsistensi akhir.