Pola untuk menjaga konsistensi dalam sistem yang didistribusikan dan bersumber dari acara?

12

Saya telah membaca tentang sumber acara akhir-akhir ini dan benar-benar menyukai ide di baliknya tetapi saya terjebak dengan masalah berikut.

Katakanlah Anda memiliki N proses bersamaan yang menerima perintah (misalnya server web), menghasilkan peristiwa sebagai hasilnya dan menyimpannya di toko terpusat. Mari kita juga berasumsi bahwa semua keadaan aplikasi sementara dipertahankan dalam memori proses individu dengan secara berurutan menerapkan peristiwa dari toko.

Sekarang, katakanlah kita memiliki aturan bisnis berikut: setiap pengguna yang berbeda harus memiliki nama pengguna yang unik.

Jika dua proses menerima perintah pendaftaran pengguna untuk nama pengguna X yang sama, mereka berdua memeriksa bahwa X tidak ada dalam daftar nama pengguna mereka, aturan memvalidasi untuk kedua proses dan mereka berdua menyimpan acara "pengguna baru dengan nama pengguna X" di toko .

Kami sekarang telah memasuki negara global yang tidak konsisten karena aturan bisnis dilanggar (ada dua pengguna berbeda dengan nama pengguna yang sama).

Dalam N server tradisional <-> 1 sistem gaya RDBMS, database digunakan sebagai titik sentral sinkronisasi yang membantu mencegah ketidakkonsistenan tersebut.

Pertanyaan saya adalah: bagaimana sistem bersumber peristiwa biasanya mendekati masalah ini? Apakah mereka hanya memproses setiap perintah secara berurutan (misalnya membatasi jumlah proses yang dapat menulis ke toko ke 1)?

Olivier Lalonde
sumber
1
Apakah pembatasan seperti itu dikendalikan oleh kode atau apakah itu merupakan batasan db? N peristiwa mungkin atau tidak dapat dikirim-diproses secara berurutan ... N peristiwa dapat melalui validasi pada saat yang sama tidak ada saling discarting. Jika pesanan penting, Anda perlu menyinkronkan validasi. Atau untuk menggunakan antrian untuk mengantre acara, lakukan pengiriman secara berurutan
Laiv
@Laiv benar. Untuk kesederhanaan saya berasumsi bahwa tidak ada database, semua status disimpan dalam memori. Memproses jenis-jenis perintah tertentu secara berurutan melalui antrian akan menjadi pilihan, tetapi rasanya seperti itu bisa rumit menentukan perintah mana yang dapat mempengaruhi orang lain dan saya cenderung akan berakhir menempatkan semua perintah dalam antrian yang sama yang berarti memiliki satu proses pemrosesan perintah : / Sebagai contoh jika saya memiliki pengguna yang menambahkan komentar pada posting blog, "hapus pengguna", "tunda pengguna", "hapus posting blog", "nonaktifkan komentar posting blog", dll. Semuanya harus masuk dalam antrian yang sama.
Olivier Lalonde
1
Saya setuju dengan Anda, untuk bekerja dengan antrian atau semafor tidak sederhana. Baik untuk bekerja dengan pola konkurensi atau sumber acara. Tetapi pada dasarnya semua solusi berakhir dengan sistem mengatur lalu lintas acara. Namun itu paradigma yang menarik. Ada juga cache eksternal yang berorientasi pada tuple seperti Redis yang dapat membantu mengatur lalu lintas antar node, seperti men-cache keadaan terakhir suatu entitas atau jika entitas tersebut sedang diproses saat ini. Cache bersama cukup umum dalam perkembangan semacam ini. Ini mungkin terlihat rumit tetapi jangan menyerah ;-) ini cukup menarik
Laiv

Jawaban:

6

Dalam N server tradisional <-> 1 sistem gaya RDBMS, database digunakan sebagai titik sentral sinkronisasi yang membantu mencegah ketidakkonsistenan tersebut.

Dalam sistem bersumber acara, "toko acara" melayani peran yang sama. Untuk objek bersumber acara, tulisan Anda merupakan tambahan acara baru Anda ke versi tertentu dari aliran acara. Jadi, seperti halnya pemrograman bersamaan, Anda bisa memperoleh kunci pada riwayat itu saat memproses perintah. Ini lebih umum untuk sistem bersumber acara untuk mengambil pendekatan yang lebih optimis - memuat riwayat sebelumnya, menghitung riwayat baru, lalu membandingkan dan menukar. Jika beberapa perintah lain juga menulis ke aliran itu, maka perbandingan dan pertukaran Anda gagal. Dari sana, Anda menjalankan kembali perintah Anda, atau mengabaikan perintah Anda, atau mungkin bahkan menggabungkan hasil Anda ke dalam sejarah.

Pertarungan menjadi masalah besar jika semua server N dengan perintah M mereka mencoba menulis ke aliran tunggal. Jawaban yang biasa di sini adalah untuk mengalokasikan riwayat ke setiap entitas yang bersumber dari peristiwa dalam model Anda. Jadi Pengguna (Bob) akan memiliki riwayat yang berbeda dari Pengguna (Alice), dan menulis ke satu tidak akan memblokir menulis ke yang lain.

Pertanyaan saya adalah: bagaimana sistem bersumber peristiwa biasanya mendekati masalah ini? Apakah mereka hanya memproses setiap perintah secara berurutan?

Greg Young di Set Validasi

Apakah ada cara yang elegan untuk memeriksa batasan unik pada atribut objek domain tanpa memindahkan logika bisnis ke lapisan layanan?

Jawaban singkat, dalam banyak kasus, menyelidiki persyaratan yang lebih dalam mengungkapkan bahwa (a) itu adalah proxy yang kurang dipahami untuk beberapa persyaratan lain, atau (b) bahwa pelanggaran terhadap "aturan" dapat diterima jika mereka dapat dideteksi (laporan pengecualian) , dimitigasi dalam jendela waktu tertentu, atau frekuensi rendah (mis .: klien dapat memeriksa apakah nama tersedia sebelum mengirim perintah untuk menggunakannya).

Dalam beberapa kasus, ketika toko acara Anda sangat bagus dalam menetapkan validasi (yaitu: database relasional), maka Anda menerapkan persyaratan dengan menulis ke tabel "nama unik" dalam transaksi yang sama yang tetap ada di acara tersebut.

Dalam beberapa kasus, Anda hanya dapat menegakkan persyaratan dengan meminta semua nama pengguna dipublikasikan ke aliran yang sama (yang memungkinkan Anda untuk mengevaluasi serangkaian nama dalam memori, sebagai bagian dari model domain Anda). - Dalam kasus ini, dua proses akan memperbarui upaya untuk memperbarui "aliran" sejarah, tetapi salah satu operasi perbandingan-dan-swap akan gagal, dan coba lagi perintah itu akan dapat mendeteksi konflik.

VoiceOfUnreason
sumber
1) Terima kasih atas saran dan referensi. Ketika Anda mengatakan "bandingkan-dan-tukar" apakah maksud Anda bahwa proses akan, pada saat menyimpan suatu peristiwa, mendeteksi peristiwa baru telah tiba sejak ia mulai memproses perintah? Saya kira ini akan membutuhkan toko acara yang mendukung semantik "bandingkan-dan-tukar", benar? (mis. "hanya menulis acara ini dan hanya jika acara terakhir memiliki ID X")?
Olivier Lalonde
2) Saya juga menyukai gagasan menerima inkonsistensi sementara dan memperbaikinya pada akhirnya, tetapi saya tidak yakin bagaimana saya akan mengkode untuk itu dengan cara yang dapat diandalkan ... mungkin memiliki proses khusus yang memvalidasi peristiwa secara berurutan dan menciptakan peristiwa rollback ketika terdeteksi ada yang salah? Terima kasih!
Olivier Lalonde
(1) Saya akan mengatakan "versi baru dari sejarah" daripada "peristiwa baru", tetapi Anda memiliki ide; hanya ganti riwayat jika itu yang kita harapkan.
VoiceOfUnreason
(2) Yup. Ini sedikit logika yang membaca peristiwa dari toko dalam batch, dan pada akhir batch menyiarkan laporan pengecualian ("kami memiliki terlalu banyak pengguna bernama Bob"), atau mengirimkan perintah untuk mengkompensasi masalah (dengan asumsi respon yang tepat adalah dapat dihitung tanpa campur tangan manusia).
VoiceOfUnreason
2

Sepertinya Anda dapat menerapkan proses bisnis ( sagadalam konteks Domain Driven Design) untuk pendaftaran pengguna di mana pengguna diperlakukan seperti a CRDT.

Sumber daya

  1. https://doc.akka.io/docs/akka/current/distributed-data.html http://archive.is/t0QIx

  2. "CRDT dengan Data Terdistribusi Akka" https://www.slideshare.net/markusjura/crdts-with-akka-distributed-data untuk mempelajari tentang

    • CmRDTs - CRDT berbasis operasi
    • CvRDTs - sebutkan CRTD berbasis negara
  3. Contoh kode dalam Scala https://github.com/akka/akka-samples/tree/master/akka-sample-distributed-data-scala . Mungkin "keranjang belanja" paling cocok.

  4. Tur Akka Cluster - Akka Data Terdistribusi https://manuel.bernhardt.io/2018/01/03/tour-akka-cluster-akka-distributed-data/
SemanticBeeng
sumber