Permintaan yang berbahaya bukan yang pertama. Itu permintaan yang sama datang lagi, karena pengirim tidak mendengar jawaban pertama, atau mendengarnya dan tetap mencoba lagi. Penyedia pembayaran melakukan ini. Antrean melakukan ini. Peramban melakukan ini saat seseorang menekan tombol dua kali. Jika sistem memperlakukan salinan kedua sebagai kerja baru, Anda menagih dua kali, mengirim dua kali, atau menulis dua baris buku untuk satu taruhan.
Anggap duplikatnya datang
Saya merancang penangan agar pengiriman kedua adalah keberhasilan yang tidak melakukan apa-apa. Yang pertama mengerjakan dan menyimpan kunci: id peristiwa penyedia, atau kunci yang dikirim klien, unik di basis data. Yang kedua menemukan kunci dan mengembalikan hasil yang sama. Tidak ada tagihan kedua. Tidak ada surel kedua. Tidak ada baris kedua.
Batasan unik adalah bagian yang saya percaya. Pemeriksaan di kode aplikasi, sesaat sebelum insert, kalah balapan saat dua worker bangun bersamaan. Basis data tidak kalah balapan itu. Yang kalah menerima konflik dan mengubahnya menjadi “sudah selesai”.
| Kedatangan pertama | Kedatangan kedua | |
|---|---|---|
| Pencarian | Kuncinya baru | Kunci sudah tersimpan |
| Menulis | Satu baris, satu efek samping | Tidak ada baris baru |
| Balas | Diterima | Yang sama, sudah diterima |
| Jika proses mati di tengah jalan | Kotak keluar masih berutang pesan itu | Tidak berutang yang kedua |
Pesan pergi bersama penulisan, atau tidak sama sekali
Mengirim surel, atau pesan antrean, di dalam permintaan dan sebelum commit adalah cara Anda mendapat pesan untuk transaksi yang sudah dibatalkan. Saya menulis pesan keluar dalam transaksi yang sama dengan baris bisnis, dan worker terpisah menerbitkannya. Jika proses mati, baris dan pesan ada keduanya atau tidak ada sama sekali. Worker boleh mengulang penerbitan. Penangan tidak boleh mengulang tagihan.
Void adalah entri baru, bukan suntingan
Saat sesuatu harus dibatalkan, saya tidak menimpa baris asli. Entri penyeimbang mengatakan apa yang berubah dan membiarkan baris pertama tetap terbaca. Seminggu kemudian Anda masih bisa menjawab apa yang dipercaya pada 14:02. Mengedit baris hanya menjawab apa yang dipercaya sekarang, dan tanpa saksi.
Jika percobaan ulang bisa membuat operasi terjadi dua kali, operasi itu belum selesai.
Beri nama kuncinya sebelum menulis handler
Kuncinya bukan “hash seluruh permintaan” kecuali Anda sudah memeriksa apa yang diubah pengirim di antara percobaan ulang. Penyedia pembayaran akan mengirim ulang id peristiwa yang sama dengan cap waktu pengiriman yang baru. Jika cap itu ada di dalam kunci, pengiriman kedua tampak baru, dan Anda membangun bug yang ingin dicegah. Saya memakai id yang dijanjikan pengirim stabil. Jika tidak menjanjikan, saya mewajibkan pemanggil mengirim satu, dan menolak panggilan yang datang tanpanya.
Lalu saya tulis kegagalan di tempat yang sama dengan keberhasilan. Konflik bukan halaman kesalahan bagi pengirim. Itu jawaban yang diharapkan atas percobaan ulang. Pengirim harus bisa berhenti. Jika saya menjawab 500, mereka mencoba kali ketiga, dan keempat, dan insiden menjadi antrean pekerjaan yang sama.
Saya tidak menganggap ini selesai karena jalur bahagia mengembalikan 200. Tes mengirim muatan yang sama lagi dan memastikan satu efek samping. Lalu ia mematikan worker di antara commit dan penerbitan, dan memastikan pesan tetap keluar, sekali. Dua tes itu lebih pendek daripada insiden yang mereka gantikan. Saya mulai di situ.
