Pola Konsumen Idempogen

Merancang konsumen pesan sehingga memproses pesan yang sama lebih dari sekali memiliki efek yang sama seperti memprosesnya sekali. Sistem olahpesan yang menjamin pengiriman setidaknya sekali dapat mengirimkan pesan yang sama beberapa kali. Tanpa ketahanan terhadap duplikat, pemrosesan ulang pesan dapat membuat rekaman duplikat, menagih ganda pelanggan, atau memiliki efek lain yang tidak diinginkan.

Konteks dan masalah

Aplikasi terdistribusi biasanya bertukar pekerjaan melalui broker pesan alih-alih panggilan sinkron langsung. Sebagian besar broker, termasuk Azure Service Bus, Azure Event Hubs, Apache Kafka, dan RabbitMQ, menyediakan pengiriman setidaknya sekali. Jaminan ini memastikan bahwa pesan mencapai konsumen bahkan ketika kegagalan terjadi, tetapi juga berarti bahwa broker dapat mengirimkan pesan yang sama lebih dari sekali.

Duplikat muncul dari beberapa sumber:

  • Percobaan ulang produser

    Produsen mengirim pesan, tidak menerima konfirmasi karena gangguan jaringan sementara atau batas waktu terlampaui, lalu mengirim pesan itu lagi. Broker sekarang menyimpan dua salinan meskipun pengiriman berhasil pertama kali.

  • Pengiriman ulang setelah konfirmasi yang hilang

    Konsumen menerima dan memproses pesan, tetapi gagal mengirimkan konfirmasi penerimaan karena mengalami kegagalan, kunci kedaluwarsa, atau konfirmasi penerimaan hilang. Broker mengasumsikan pesan tidak diproses dan mengirimkannya lagi.

  • Kegagalan konsumen di tengah proses

    Konsumen menyelesaikan penulisan ke database tetapi mengalami kerusakan sebelum mengonfirmasi pesan. Ketika instans lain mengambil pesan yang disebarkan ulang, instans lain akan mengulangi penulisan.

Pengiriman tepat satu kali dalam sistem terdistribusi tidak praktis untuk dijamin. Bahkan broker yang mengklaim semantik exactly-once hanya menjamin operasi yang secara langsung mereka kendalikan, seperti mengirimkan pesan kepada konsumen atau menulis data kembali ke broker. Mereka tidak dapat menjamin dampak eksternal yang dilakukan oleh konsumen pada sistem lain. Solusi tahan lama bukan untuk menghilangkan pengiriman duplikat. Ini untuk membuat konsumen mentolerirnya. Ketika Anda menggabungkan pengiriman minimal satu kali dengan konsumen yang mengabaikan duplikat, Anda memperoleh pemrosesan yang secara efektif tepat satu kali.

Solusi

Jadikan konsumen bersifat idempoten dengan menyimpan catatan pesan yang telah diproses dan melewati pesan apa pun yang sudah pernah diterimanya sebelumnya. Konsumen mendasarkan keputusan ini pada pengidentifikasi stabil yang tetap ada meskipun terjadi pengiriman ulang, memeriksa penyimpanan persisten untuk menentukan apakah pengidentifikasi tersebut telah diproses sebelumnya, lalu memproses pesan itu atau mengabaikannya sebagai duplikat.

Langkah-langkah berikut menjelaskan alur inti:

  1. Baca pesan dan ekstrak kunci deduplikasinya.
  2. Periksa penyimpanan deduplikasi untuk kunci tersebut.
  3. Jika kunci ada, perlakukan pesan sebagai duplikat. Akui lalu hentikan, dengan opsi mengembalikan hasil yang telah dicatat sebelumnya.
  4. Jika kunci tidak ada, proses pesan dan rekam kunci dalam satu operasi atomik, lalu akui pesan tersebut.

Pilih kunci deduplikasi yang stabil

Kunci harus mengidentifikasi pesan logis secara unik dan konsisten pada setiap pengiriman ulang. Gunakan pengidentifikasi pesan yang ditetapkan produsen atau kunci idempotensi tingkat bisnis yang mengidentifikasi operasi logis tertentu, bukan konteks korelasi bersama yang dapat dibawa oleh beberapa pesan. Dalam Azure Service Bus, MessageId properti melayani tujuan ini karena secara unik mengidentifikasi pesan dan payload-nya. Jangan gunakan CorrelationId sebagai kunci, karena mengelompokkan pesan terkait, seperti permintaan dan balasannya. Untuk peristiwa yang mengikuti spesifikasi CloudEvents, kombinasi atribut source dan id secara unik mengidentifikasi suatu peristiwa dan tetap stabil di seluruh pengiriman ulang.

Jangan mengandalkan pengenal tingkat transport yang dibuat ulang oleh broker saat pengiriman ulang atau nilai yang diturunkan dari upaya pengiriman, karena nilai-nilai tersebut berubah di antara pesan duplikat dan menggagalkan deteksi. Hindari juga menurunkan kunci dari kolom yang volatil, seperti stempel waktu penerimaan.

Ketika lebih dari satu konsumen independen memproses kanal yang sama, seperti beberapa subskriber dalam desain publish-subscribe, setiap konsumen memang memproses salinan pesannya sendiri dan perlu secara mandiri melacak penyelesaian pemrosesan pesan. Jika konsumen tersebut berbagi satu penyimpanan deduplikasi, gunakan kunci untuk rekaman berdasarkan gabungan identitas konsumen dan identitas pesan. Penyimpanan data yang diindeks hanya berdasarkan identitas pesan memungkinkan konsumen pertama mencegah pemrosesan oleh semua konsumen lainnya.

Memutuskan tempat menyimpan kunci yang diproses

Anda memiliki dua opsi umum:

  • Tabel deduplikasi khusus. Konsumen mempertahankan tabel terpisah, kadang-kadang disebut kotak masuk, yang menyimpan satu baris per kunci yang diproses. Pendekatan ini membuat masalah deduplikasi terpisah dari data bisnis dan berfungsi dengan baik ketika banyak jenis pesan berbagi satu mekanisme.

  • Entitas bisnis itu sendiri. Konsumen menyimpan kunci pada catatan yang dibuat atau diperbarui oleh pesan. Pendekatan ini menghindari tabel terpisah, tetapi mengaitkan deduplikasi dengan struktur data bisnis.

Menerapkan penanda dan efek samping secara atomik

Alur check-then-process memiliki jendela kegagalan. Jika konsumen memproses pesan lalu mencatat kunci dalam langkah terpisah, kegagalan sistem di antara kedua operasi tersebut dapat menyebabkan efek samping sudah telanjur diterapkan, tetapi kuncinya belum tercatat, sehingga pengiriman berikutnya akan memproses ulang pesan tersebut.

Atasi celah kegagalan ini dengan menuliskan penanda deduplikasi dan efek samping pada sisi bisnis dalam transaksi yang sama. Ketika keduanya dikomitkan bersama-sama atau tidak sama sekali, pengiriman ulang akan menemukan penanda yang sudah dikomitkan lalu melewatinya, atau tidak menemukan penanda karena transaksi di-rollback dan memprosesnya ulang dengan aman. Varian transaksional ini adalah pola kotak masuk, dan merupakan pendamping sisi konsumsi untuk pola Transactional Outbox di sisi produksi.

Menjaga dari duplikat bersamaan

Dalam mekanisme pengiriman setidaknya sekali dengan beberapa konsumen yang saling bersaing, dua instans dapat menerima salinan dari pesan yang sama pada saat yang sama. Keduanya dapat lolos dari pengecekan keberadaan sebelum salah satunya melakukan commit, sehingga pengecekan saja tidak mencegah pemrosesan ganda.

Menerapkan kebenaran di penyimpanan data alih-alih dalam logika aplikasi:

  • Gunakan batasan unik pada kunci deduplikasi. Kedua transaksi mencoba memasukkan kunci, tetapi hanya satu yang berhasil. Yang satunya tidak memenuhi kendala dan menganggap pesan tersebut sebagai duplikat. Pendekatan ini menjadikan database sebagai arbiter tunggal dari perlombaan.

  • Hindari kondisi balapan check-then-set pada cache. Pola yang memeriksa sebuah kunci lalu menetapkannya melalui dua operasi terpisah menyisakan celah yang memungkinkan percobaan ulang serentak sama-sama mengklaim kunci tersebut. Gunakan operasi tulis bersyarat atomik, seperti penyisipan yang gagal saat terjadi konflik atau operasi tetapkan-jika-belum-ada, sehingga pengambilan alih kunci dilakukan dalam satu langkah atomik.

Menangani efek samping yang tidak dapat bergabung dengan transaksi

Beberapa proses tidak dapat berpartisipasi dalam transaksi database konsumen, seperti memanggil API pihak ketiga atau menulis ke penyimpanan eksternal. Untuk proses ini, gunakan pendekatan dua fase:

  1. Rekam kunci dengan status sedang berlangsung sebelum Anda melakukan tindakan eksternal.
  2. Jalankan proses.
  3. Perbarui catatan untuk diselesaikan dan simpan hasilnya.

Saat pengiriman ulang, catatan selesai memungkinkan Anda untuk tidak perlu mengulangi panggilan. Catatan dalam proses menunjukkan bahwa upaya sebelumnya mungkin hanya selesai sebagian atau sedang dikerjakan oleh konsumer lain.

Masalah dan pertimbangan

Pertimbangkan poin-poin berikut saat Anda memutuskan cara menerapkan pola ini:

  • Utamakan operasi yang secara alami bersifat idempoten. Beberapa operasi secara inheren bersifat idempoten dan tidak memerlukan pencatatan deduplikasi. Upsert yang menggunakan pengenal bisnis sebagai kunci, operasi tulis yang menetapkan nilai absolut alih-alih penambahan bertahap, atau permintaan HTTP PUT ke pengenal sumber daya menghasilkan hasil yang sama, baik dijalankan sekali maupun berkali-kali.

    Anda terkadang dapat membuat suatu operasi secara alami bersifat idempoten melalui transfer keadaan yang dibawa peristiwa, di mana pesan membawa keadaan absolut hasil akhirnya, seperti status baru pesanan, sehingga konsumen memprosesnya sebagai upsert alih-alih sebagai perubahan relatif.

    Tip

    Rancang agar idempotensi tercapai secara alami terlebih dahulu, dan tambahkan teknik deduplikasi hanya untuk operasi yang tidak dapat dibuat secara alami menjadi idempoten.

  • Mengelola siklus hidup rekaman deduplikasi. Rekaman deduplikasi akan terus bertambah kecuali jika Anda menetapkan masa kedaluwarsanya. Pertahankan setiap rekaman setidaknya selama broker dapat mengirim ulang pesan asli. Rentang waktu ini bergantung pada jumlah maksimum upaya pengiriman yang ditetapkan broker, batas waktu penguncian atau visibilitas, dan masa aktif pesan. Tetapkan masa berlaku pada catatan deduplikasi yang melampaui jangka waktu ini agar pengiriman ulang yang terlambat masih dapat menemukan penandanya. Menghapus rekaman terlalu dini membuka kembali jendela untuk duplikat. Perhitungkan pesan yang dikirim ulang oleh operator dari antrean dead-letter, karena pengiriman ulang tersebut dapat terjadi jauh setelah jangka waktu pengiriman ulang normal.

  • Gunakan kerangka kerja perpesanan alih-alih membangun deduplikasi sendiri. Menerapkan penyimpanan deduplikasi, komit atomik, dan pembersihan catatan dengan benar rawan kesalahan. Kerangka kerja berbasis pesan menyediakan pola ini sebagai fitur bawaan.

    Misalnya, NServiceBus menghapus duplikasi pesan masuk berdasarkan pengidentifikasi pesan dan menyediakan retensi serta pembersihan data deduplikasi yang dapat dikonfigurasi. Kotak masuk konsumen MassTransit melacak pesan yang diterima berdasarkan pengidentifikasi pesannya untuk menyediakan perilaku konsumen tepat satu kali.

  • Deduplikasi pada broker mengurangi, tetapi tidak menghilangkan kebutuhan akan logika konsumen yang idempoten. Beberapa platform menyaring duplikat pada lapisan transportasi. Azure Service Bus deteksi duplikat membuang pesan yang berisi MessageId yang berulang dalam jendela waktu yang dikonfigurasi, sehingga menekan duplikat yang disebabkan oleh percobaan ulang pengiriman oleh produsen. Fitur ini bekerja pada sisi pengiriman dan dalam jendela terbatas. Ini tidak mencegah konsumen memproses pesan yang sama dua kali setelah pengiriman ulang, jadi Anda masih memerlukan logika konsumen yang idempoten. Perlakukan fitur platform sebagai lapisan pertahanan pertama yang menurunkan volume duplikat, bukan sebagai pengganti pola.

  • Perhitungkan urutan pesan. Deduplikasi menghapus duplikat tetapi tidak menjamin urutan. Jika konsumen bergantung pada pesanan pemrosesan, gabungkan pola ini dengan mekanisme pemesanan, seperti sesi pesan Azure Service Bus, atau sertakan urutan atau data versi yang memungkinkan konsumen menolak pesan kedaluarsa.

  • Instrumen untuk pengamatan. Keluarkan kunci deduplikasi dan pengidentifikasi korelasi dalam log terstruktur, dan lacak metrik untuk duplikat yang terdeteksi. Tingkat duplikat yang meningkat dapat menunjukkan kesalahan konfigurasi produsen, pengakuan berukuran kecil atau jendela kunci, atau konsumen yang tidak sehat. Gunakan pelacakan dan korelasi end-to-end untuk melacak pesan di seluruh layanan.

  • Menyebarkan idempotensi ke panggilan hilir. Menjadikan satu konsumen idempoten tidak melindungi layanan yang dipanggilnya. Ketika konsumen memanggil layanan hilir sebagai bagian dari pemrosesan, teruskan kunci idempoten agar setiap lapisan dapat menghilangkan duplikasi pekerjaannya sendiri.

Kapan menggunakan pola ini

Gunakan pola ini ketika:

  • Anda menerima pesan dari broker yang menyediakan jaminan pengiriman setidaknya satu kali, yang merupakan opsi bawaan untuk sebagian besar broker.

  • Pemrosesan ulang pesan menghasilkan hasil yang salah, seperti transaksi keuangan duplikat, pembuatan sumber daya duplikat, atau pemberitahuan berulang.

  • Beberapa konsumen yang bersaing memproses saluran yang sama, yang membuat kemungkinan pengiriman duplikat bersamaan.

Pola ini mungkin tidak cocok ketika:

  • Setiap operasi yang dilakukan oleh konsumen secara alami sudah bersifat idempoten, sehingga pemrosesan ulang tidak menimbulkan masalah dan pencatatan deduplikasi hanya menambah biaya tanpa manfaat.

  • Beban kerja dapat mentoleransi dampak dari pemrosesan duplikat yang sesekali terjadi, dan biaya penyimpanan deduplikasi lebih besar daripada dampak adanya duplikat.

Pemrosesan idempotensi di luar olahpesan

Pola ini menerapkan prinsip idempoten pada konsumen pesan, tetapi pemrosesan idempoten merupakan prinsip keandalan yang lebih luas. Operasi apa pun yang dapat berjalan lebih dari sekali melalui tugas yang identik mendapat manfaat darinya. Prinsip ini mencakup transformasi extract, transform, load (ETL) yang memproses ulang data yang diputar ulang, pemrosesan aliran yang dilanjutkan dari titik pemeriksaan, pekerjaan terjadwal yang tumpang tindih atau dimulai ulang, serta titik akhir webhook atau HTTP yang menerima pengiriman duplikat.

Dalam setiap kasus, teknik inti yang sama berlaku:

  1. Identifikasi unit kerja dengan kunci yang stabil.
  2. Rekam apa yang sudah Anda proses.
  3. Lewati atau serap duplikat sehingga mengulangi pekerjaan tidak mengubah hasilnya.

Mekanisme dalam artikel ini, seperti kunci stabil, penanda atom, dan batasan unik, mentransfer ke konteks tersebut bahkan ketika tidak ada broker pesan yang terlibat.

Desain beban kerja

Evaluasikan cara menggunakan pola Idempotent Consumer dalam desain beban kerja untuk memenuhi tujuan dan prinsip yang tercakup dalam pilar Azure Well-Architected Framework. Tabel berikut memberikan panduan tentang bagaimana pola ini mendukung tujuan setiap pilar.

Prinsip Dasar Bagaimana pola ini mendukung tujuan pilar
Keputusan desain Keandalan membantu beban kerja Anda menjadi tangguh tidak berfungsi dan memastikan bahwa memulihkan ke keadaan yang berfungsi penuh setelah kegagalan terjadi. Pola ini memungkinkan beban kerja menggunakan pengiriman setidaknya sekali dan percobaan ulang yang aman tanpa merusak data, yang mengubah pengiriman duplikat dari risiko kebenaran menjadi kondisi yang ditoleransi.

- RE:07 Perlindungan Diri
- Menangani kesalahan Sementara

Jika pola ini memperkenalkan kompromi di dalam pilar, bandingkan dengan tujuan pilar lain.

Example

Contoh berikut menunjukkan konsumen idempoten yang memproses pesanan dari Azure Service Bus serta menyimpan status secara persisten di Azure Cosmos DB for NoSQL.

Produsen menetapkan Bus Layanan MessageId sebagai pengenal pesanan tingkat bisnis. Konsumen menerima pesan dalam mode PeekLock, yang mengirimkan kembali pesan jika konsumen tidak berhasil menyelesaikannya dalam masa penguncian. Kontainer Azure Cosmos DB milik konsumen dipartisi berdasarkan pengidentifikasi pesanan (/orderId) dan menetapkan id dokumen ke pengidentifikasi pesanan yang sama, sehingga setiap salinan dari pesanan tertentu dipetakan ke partisi logis yang sama dan catatan pesanan itu sendiri berfungsi sebagai penanda deduplikasi.

Konsumen memproses setiap pesan sebagai berikut:

  1. Baca pesan dan gunakan MessageId sebagai kunci deduplikasi.
  2. Buat dokumen pesanan dengan id dan kunci partisi keduanya diatur ke pengidentifikasi pesanan.
  3. Jika pembuatan berhasil, tandai pesan sebagai selesai agar Bus Layanan menghapus pesan tersebut dari antrean.
  4. Jika pembuatan gagal dengan status HTTP 409 (Konflik) karena dokumen dengan id tersebut sudah ada, baca dokumen yang sudah ada dan bandingkan dokumen tersebut dengan pesan saat ini. Jika hash permintaan yang tersimpan atau field bisnis yang tidak dapat diubah cocok, perlakukan pesan tersebut sebagai duplikat, tandai sebagai selesai, dan lewati pemrosesannya. Jika tidak cocok, produsen mungkin telah menggunakan kembali pengidentifikasi untuk konten yang berbeda, atau detail pesanan mungkin telah berubah sejak pertama kali diproses, jadi dead-letter pesan atau menaikkan pemberitahuan alih-alih membuangnya secara diam-diam.
  5. Jika pemrosesan gagal karena alasan sementara, tinggalkan pesan sehingga Bus Layanan mengirimkannya kembali, atau biarkan kunci kedaluwarsa sehingga konsumen lain menerimanya.

Operasi pembuatan bersifat atomik, sehingga berfungsi sekaligus sebagai pemeriksaan deduplikasi dan penulisan data. Dua konsumen yang menerima salinan pesan yang sama tidak dapat membuat pesanan keduanya. Satu membuat kemenangan, dan yang lain mengembalikan konflik dan dengan aman membuang duplikatnya.

Saat memproses harus menulis lebih dari satu dokumen, gunakan batch transaksional yang menyertakan dokumen deduplikasi dan dokumen bisnis dalam kunci partisi yang sama. Karena batch transaksional beroperasi dalam satu partisi logis, pilih kunci partisi yang sama untuk semua dokumen dalam satu pesan. Batch mengomiten semua dokumen sekaligus atau tidak sama sekali, sehingga kegagalan sistem di antara pemrosesan dan konfirmasi tidak akan membuat penanda deduplikasi dan data bisnis menjadi tidak sinkron. Batch yang mencoba membuat dokumen yang sudah ada akan mengembalikan status 409 (Konflik), yang menandakan adanya duplikat.

Untuk membuat konsumen ini juga tangguh terhadap percobaan ulang pengiriman duplikat, aktifkan deteksi duplikat pada antrean. Deteksi duplikat mencegah pengiriman berulang dalam periode riwayatnya, dan konsumen idempoten menangani duplikat yang berada di luar periode tersebut atau yang dihasilkan dari pengiriman ulang.

Langkah selanjutnya