Bagaimana mencegah kondisi balapan di aplikasi web?

31

Pertimbangkan situs e-commerce, tempat Alice dan Bob mengedit daftar produk. Alice meningkatkan deskripsi, sementara Bob memperbarui harga. Mereka mulai mengedit Acme Wonder Widget secara bersamaan. Bob selesai lebih dulu dan menyimpan produk dengan harga baru. Alice membutuhkan waktu lebih lama untuk memperbarui deskripsi, dan ketika dia selesai, dia menyimpan produk dengan deskripsi barunya. Sayangnya, dia juga menimpa harga dengan harga lama, yang tidak dimaksudkan.

Dalam pengalaman saya, masalah ini sangat umum di aplikasi web. Beberapa perangkat lunak (mis. Perangkat lunak wiki) memang memiliki perlindungan terhadap ini - biasanya penyimpanan kedua gagal dengan "halaman diperbarui saat Anda mengedit". Tetapi sebagian besar situs web tidak memiliki perlindungan ini.

Perlu dicatat bahwa metode pengontrolnya aman untuk diri mereka sendiri. Biasanya mereka menggunakan transaksi basis data, yang membuat mereka aman dalam arti bahwa jika Alice dan Bob mencoba menyelamatkan pada saat yang sama, itu tidak akan menyebabkan korupsi. Kondisi balapan muncul dari Alice atau Bob yang memiliki data basi di browser mereka.

Bagaimana kita dapat mencegah kondisi balapan seperti itu? Secara khusus, saya ingin tahu:

  • Teknik apa yang bisa digunakan? mis. melacak waktu perubahan terakhir. Apa pro dan kontra dari masing-masing.
  • Apa pengalaman pengguna yang bermanfaat?
  • Kerangka apa yang memiliki perlindungan ini dibangun?
paj28
sumber
Anda telah memberikan jawabannya: dengan melacak tanggal perubahan objek dan membandingkannya dengan usia data yang coba diperbarui oleh perubahan lain. Apakah Anda ingin mengetahui sesuatu yang lain, misalnya bagaimana melakukannya secara efisien?
Kilian Foth
@KilianFoth - Saya telah menambahkan beberapa info tentang apa yang ingin saya ketahui
paj28
1
Pertanyaan Anda sama sekali tidak khusus untuk aplikasi web, aplikasi desktop dapat memiliki masalah yang sama persis. Strategi solusi khas dijelaskan di sini: stackoverflow.com/questions/129329/…
Doc Brown
2
FYI, bentuk penguncian yang Anda sebutkan dalam pertanyaan Anda dikenal sebagai " kontrol konkurensi optimis "
TehShrike
Beberapa diskusi terkait dengan Django di sini
paj28

Jawaban:

17

Anda perlu "membaca tulisan Anda", yang berarti sebelum Anda menuliskan perubahan, Anda perlu membaca catatan lagi dan memeriksa apakah ada perubahan di mana sejak Anda terakhir membacanya. Anda dapat melakukan bidang-per-bidang ini (berbutir halus) atau berdasarkan stempel waktu (berbutir kasar). Saat Anda melakukan pemeriksaan ini, Anda memerlukan kunci eksklusif pada catatan. Jika tidak ada perubahan yang dilakukan, Anda dapat menuliskan perubahan dan melepaskan kunci. Jika catatan telah berubah sementara itu, Anda membatalkan transaksi, lepaskan kunci dan beri tahu pengguna.

Phil
sumber
Ini kedengarannya seperti pendekatan yang paling praktis. Apakah Anda tahu ada kerangka kerja yang mengimplementasikan ini? Saya pikir masalah terbesar dengan skema ini adalah bahwa pesan "edit konflik" yang sederhana akan membuat pengguna frustrasi, tetapi berusaha menggabungkan perubahan (secara manual atau otomatis) itu sulit.
paj28 28
Sayangnya, saya tidak tahu kerangka kerja yang mendukung ini di luar kotak. Saya tidak berpikir pesan kesalahan edit edit akan dianggap sebagai frustasi, asalkan tidak sering muncul. Pada akhirnya, itu tergantung pada beban pengguna sistem apakah Anda hanya memeriksa stempel waktu atau menerapkan fungsi penggabungan yang lebih kompleks.
Phil
Saya memelihara produk database terdistribusi PC yang menggunakan pendekatan berbutir halus (terhadap salinan basis data lokalnya): jika satu pengguna mengubah harga dan yang lain mengubah deskripsi - tidak masalah! Sama seperti di kehidupan nyata. Jika dua pengguna mengubah harga - pengguna kedua mendapat permintaan maaf dan mencoba perubahannya lagi. Tidak masalah! Ini tidak memerlukan kunci kecuali saat data sedang ditulis ke database. Tidak masalah jika satu pengguna pergi makan siang saat perubahan mereka ada di layar dan mengirimkannya nanti. Untuk perubahan basis data jarak jauh, itu didasarkan pada catatan cap waktu.
1
Dataflex memiliki fungsi yang disebut "baca kembali ()" yang melakukan apa yang Anda gambarkan. Dalam versi yang lebih baru, itu aman di lingkungan multiuser. Dan, memang, itu satu-satunya cara untuk mendapatkan pembaruan yang disisipkan sedemikian rupa agar berfungsi.
dapatkah Anda memberikan contoh bagaimana melakukan ini dengan sql server? \
l --''''''--------- '' '' '' '' '' ''
10

Saya telah melihat 2 cara utama:

  1. Tambahkan stempel waktu pembaruan terakhir halaman yang sedang diedit dalam input tersembunyi. Ketika melakukan cap waktu diperiksa terhadap yang sekarang dan jika mereka tidak cocok itu telah diperbarui oleh orang lain dan mengembalikan kesalahan.

    • pro: banyak pengguna dapat mengedit bagian halaman yang berbeda. Halaman kesalahan dapat menyebabkan halaman berbeda di mana pengguna kedua dapat menggabungkan perubahannya di halaman baru.

    • con: kadang-kadang sebagian besar upaya terbuang sia-sia saat pengeditan besar-besaran.

  2. Ketika seorang pengguna mulai mengedit halaman kunci untuk waktu yang wajar, ketika pengguna lain kemudian mencoba untuk mengedit dia mendapatkan halaman kesalahan dan harus menunggu sampai kunci berakhir atau pengguna pertama telah berkomitmen.

    • pro: sunting upaya tidak sia-sia.

    • con: pengguna yang tidak bermoral dapat mengunci halaman tanpa batas. Halaman dengan kunci kedaluwarsa mungkin masih dapat melakukan kecuali dinyatakan sebaliknya (menggunakan teknik 1)

ratchet freak
sumber
7

Gunakan Kontrol Konkurensi Optimis .

Tambahkan kolom versionNumber atau versionTimestamp ke tabel yang dimaksud (integer paling aman).

Pengguna 1 membaca catatan:

{id:1, status:unimportant, version:5}

Pengguna 2 membaca catatan:

{id:1, status:unimportant, version:5}

Pengguna 1 menyimpan catatan, ini menambah versi:

save {id:1, status:important, version:5}
new value {id:1, status:important, version:6}

Pengguna 2 mencoba untuk menyimpan catatan yang mereka baca:

save {id:1, status:unimportant, version:5}
ERROR

Hibernate / JPA dapat melakukan ini secara otomatis dengan @Versionanotasi

Anda perlu mempertahankan status catatan baca di suatu tempat, umumnya dalam sesi (ini lebih aman daripada dalam variabel bentuk tersembunyi).

Neil McGuigan
sumber
Terima kasih ... sangat membantu untuk mengetahui tentang @Version. Satu pertanyaan: mengapa harus menyimpan negara dalam sesi? Kalau begitu, saya khawatir menggunakan tombol kembali bisa membingungkan.
paj28
Sesi lebih aman daripada elemen bentuk tersembunyi karena pengguna tidak dapat mengubah nilai versi. Jika itu bukan masalah, abaikan bagian tentang sesi
Neil McGuigan
Teknik ini disebut Optimis secara offline kunci dan dalam SQLAlchemy juga
paj28
@ paj28 - tautan itu untuk SQLAlchemytidak menunjukkan apa pun tentang kunci luring optimis, dan saya tidak dapat menemukannya di dokumen. Apakah Anda memiliki tautan yang lebih bermanfaat, atau hanya mengarahkan orang ke SQLAlchemy secara umum?
dwanderson
@dwanderson maksudku bagian penghitung versi dari tautan itu.
paj28
1

Beberapa sistem Object Relational Mapping (ORM) akan mendeteksi bidang objek mana yang telah berubah sejak dimuat dari database, dan akan membangun pernyataan pembaruan SQL untuk hanya menetapkan nilai-nilai tersebut. ActiveRecord untuk Ruby on Rails adalah salah satu ORM tersebut.

Efek bersihnya adalah bahwa bidang yang tidak diubah pengguna tidak termasuk dalam perintah UPDATE yang dikirim ke database. Orang yang memperbarui bidang yang berbeda pada saat yang sama tidak saling menimpa perubahan.

Bergantung pada bahasa pemrograman yang Anda gunakan, teliti ORM mana yang tersedia, dan lihat apakah ada di antara mereka yang hanya akan memperbarui kolom dalam basis data bertanda "kotor" di aplikasi Anda.

Greg Burghardt
sumber
Hai, Greg. Sayangnya, ini tidak membantu dengan kondisi lomba seperti ini. Jika Anda mempertimbangkan contoh asli saya, ketika Alice menyimpan, ORM akan melihat kolom harga kotor dan memperbaruinya - meskipun pembaruan tidak diinginkan.
paj28 28
1
@ paj28 Poin utama dalam jawaban Greg adalah " bidang yang tidak diubah pengguna ". Alice tidak mengubah harga, sehingga ORM tidak akan mencoba untuk menyimpan nilai "harga" ke database.
Ross Patterson
@RossPatterson - bagaimana ORM mengetahui perbedaan antara bidang yang diubah pengguna dan data basi dari browser? Tidak, setidaknya tanpa melakukan pelacakan tambahan. Jika Anda ingin mengedit jawaban Greg untuk memasukkan pelacakan seperti itu, atau mengirimkan jawaban lain, itu akan sangat membantu.
paj28
@ paj28 beberapa bagian dari sistem harus tahu apa yang dilakukan pengguna, dan hanya menyimpan perubahan yang dilakukan pengguna. Jika pengguna mengubah harga, lalu mengubahnya kembali, lalu dikirimkan, ini tidak boleh dianggap sebagai "sesuatu yang pengguna ubah", karena mereka tidak. Jika Anda memiliki sistem yang memerlukan tingkat kontrol konkurensi ini, Anda harus membangunnya dengan cara ini. Jika tidak, tidak.
@nocomprende - Sebagian, pasti - tetapi bukan ORM seperti jawaban ini mengatakan
paj28