4 poin oleh GN⁺ 2024-10-21 | 1 komentar | Bagikan ke WhatsApp
  • Redlock berbasis Redis bertujuan menyediakan distributed lock yang toleran terhadap kegagalan, tetapi kurang aman untuk pekerjaan yang bergantung pada korektness dan terlalu rumit bila hanya dipakai untuk optimasi efisiensi
  • Dalam distributed lock, tujuan efisiensi untuk mengurangi pekerjaan duplikat dan tujuan korektness untuk melindungi state bersama harus dibedakan lebih dulu; patokannya adalah apakah saat gagal hanya menambah biaya atau justru merusak data
  • Bahkan jika ada layanan lock yang sempurna, penghentian GC yang lama, proses yang terhenti sementara, dan latensi jaringan dapat membuat write lama tetap dieksekusi setelah lease kedaluwarsa, sehingga diperlukan fencing token
  • Redlock tidak dapat membuat token yang meningkat secara monoton setiap kali lock diperoleh, dan kedaluwarsa key Redis bergantung pada jam sistem berbasis gettimeofday, sehingga keamanan dapat rusak saat terjadi loncatan jam atau penundaan
  • Untuk lock yang membutuhkan korektness, gunakan sistem konsensus seperti ZooKeeper beserta pemeriksaan fencing token, dan batasi lock Redis node tunggal hanya untuk penggunaan aproksimatif dan nonkritis

Titik awal meninjau Redlock

  • Redlock adalah algoritme untuk mengimplementasikan distributed lock yang toleran terhadap kegagalan di atas Redis, atau lebih tepatnya lease
  • Sudah ada lebih dari 10 implementasi independen, dan karena tidak diketahui siapa saja yang bergantung pada algoritme ini, peninjauan publik layak dilakukan
  • Redis sendiri sangat cocok untuk berbagi data antarpeladen yang bersifat sementara, aproksimatif, dan cepat berubah
    • Contoh: penghitung request per alamat IP, himpunan IP unik per ID pengguna
  • Kekhawatirannya adalah Redis makin sering dipakai untuk pengelolaan data yang mengharapkan konsistensi dan durabilitas yang lebih kuat, dan distributed lock adalah salah satu area tersebut

Tujuan lock: efisiensi atau korektness?

  • Dalam aplikasi terdistribusi, lock adalah mekanisme agar saat beberapa node mencoba pekerjaan yang sama, hanya satu yang menjalankannya pada satu waktu
  • Alasan memakai lock pada dasarnya terbagi menjadi dua
    • Efisiensi: optimasi agar komputasi mahal tidak dilakukan dua kali; jika gagal, dampaknya mungkin hanya biaya AWS sedikit naik atau notifikasi email yang sama terkirim dua kali
    • Korektness: mekanisme untuk mencegah proses konkuren merusak state yang sama; jika gagal, dapat timbul masalah serius seperti file rusak, data hilang, inkonsistensi permanen, atau pemberian obat yang salah
  • Untuk lock dengan tujuan efisiensi, biaya dan kompleksitas Redlock yang memakai 5 server Redis dan verifikasi mayoritas tidak diperlukan
    • Instance Redis tunggal dengan replikasi asinkron bila perlu lebih cocok dipakai
    • Dalam kasus ini, sebagian lock bisa hilang karena mati listrik atau masalah pada node Redis, tetapi itu masih kegagalan yang dapat ditoleransi untuk optimasi nonkritis
  • Karena memakai 5 replika dan mayoritas, Redlock tampak cocok untuk lock yang penting bagi korektness, tetapi pada praktiknya tidak sesuai untuk tujuan itu

Lease saja tidak cukup untuk melindungi resource dengan aman

  • Lock dalam sistem terdistribusi berbeda dari mutex di aplikasi multithread; node dan jaringan bisa gagal secara independen sehingga jauh lebih kompleks
  • Alur tipikal untuk memperbarui file di shared storage adalah memperoleh lock, membaca file, memodifikasi, menulis ulang, lalu melepas lock
    • Lock dipakai untuk mencegah dua klien melakukan read-modify-write secara bersamaan dan menyebabkan update hilang
  • Jika klien berhenti terlalu lama saat memegang lock, lease bisa kedaluwarsa
    • GC bisa ikut campur dan menghentikan klien dalam waktu lama
    • Lease adalah desain yang baik agar klien yang crash tidak memegang lock selamanya, tetapi jika waktu berhenti lebih lama daripada masa kedaluwarsa, klien bisa tetap menjalankan write berbahaya tanpa sadar lease-nya sudah habis
  • Masalah ini bukan kasus teoretis; HBase juga pernah mengalami masalah serupa di masa lalu
    • Ada kasus penghentian GC “stop-the-world” yang berlangsung beberapa menit
    • Bahkan GC “concurrent” seperti CMS di HotSpot JVM tetap kadang harus menghentikan aplikasi
  • Memeriksa apakah lock sudah kedaluwarsa tepat sebelum menulis tidak menyelesaikan masalah
    • GC bisa menghentikan thread yang sedang berjalan di titik mana pun, termasuk di antara pemeriksaan terakhir dan operasi write

Penghentian proses dan latensi jaringan adalah model ancaman yang umum

  • Sekalipun memakai runtime tanpa penghentian GC yang panjang, proses tetap bisa berhenti karena berbagai alasan
    • Membaca alamat yang tidak ada di memori bisa memicu page fault
    • Jika disk adalah EBS, membaca variabel bisa berubah menjadi request sinkron melalui jaringan Amazon
    • Perebutan CPU, penundaan scheduler, atau SIGSTOP yang terkirim secara tidak sengaja juga bisa menghentikan proses
  • Latensi jaringan menimbulkan masalah yang sama
    • Aplikasi mengirim request write, tetapi paket bisa tertunda dan baru tiba di storage server setelah lease kedaluwarsa
    • Dalam satu insiden GitHub, paket jaringan tertunda sekitar 90 detik
  • Jaringan paket seperti Ethernet dan IP dapat menunda paket secara arbitrer, dan hal itu benar-benar terjadi di dunia nyata
  • Karena itu, bahkan pada jaringan yang dikelola dengan baik pun kita tidak bisa berasumsi soal timing, dan kode berbasis lease yang sederhana pada dasarnya tidak aman apa pun layanan lock yang dipakai

Write lama harus diblokir dengan fencing token

  • Solusinya adalah menyertakan fencing token pada setiap request write ke storage
  • Fencing token adalah angka yang bertambah setiap kali klien memperoleh lock
    • Contoh: klien 1 memperoleh lease dengan token 33 lalu berhenti lama hingga lease kedaluwarsa
    • Klien 2 memperoleh lease baru dengan token 34 dan mengirim request write ke storage
    • Ketika klien 1 bangun kemudian dan mengirim write dengan token 33, storage menolaknya karena sudah memproses token 34 yang lebih tinggi
  • Agar aman, server storage harus secara aktif memeriksa token dan menolak write dengan nilai token yang mundur
  • Jika layanan lock dapat menghasilkan token yang meningkat monoton secara ketat, lock bisa dibuat aman
    • Jika ZooKeeper dipakai sebagai layanan lock, zxid atau nomor versi znode dapat digunakan sebagai fencing token
  • Masalah besar Redlock adalah tidak memiliki kemampuan menghasilkan fencing token
    • Nilai acak unik Redlock tidak memberikan sifat meningkat monoton yang dibutuhkan
    • Counter pada satu node Redis tidak cukup karena node itu bisa gagal
    • Counter dari beberapa node bisa saling tidak sinkron
    • Bahkan untuk menghasilkan fencing token pun kemungkinan dibutuhkan algoritme konsensus

Redlock menggantungkan keamanan pada asumsi waktu

  • Dalam algoritme terdistribusi, model praktisnya adalah model asinkron dengan failure detector yang tidak andal
    • Proses bisa berhenti selama durasi arbitrer
    • Paket bisa tertunda secara arbitrer di jaringan
    • Jam bisa salah secara arbitrer
    • Meski begitu, algoritme tetap harus membuat keputusan yang benar
  • Jam hanya boleh dipakai untuk membuat timeout agar tidak menunggu selamanya ketika sebuah node down
    • Timeout tidak perlu akurat, dan request yang timeout tidak berarti node lawan pasti down
    • Bisa jadi penyebabnya latensi jaringan atau kesalahan jam lokal
  • Redis memakai gettimeofday, bukan monotonic clock, untuk menentukan kedaluwarsa key
    • gettimeofday memungkinkan waktu sistem melompat secara tidak kontinu
    • Jika NTP menyesuaikan jam atau administrator mengubah waktu secara manual, key Redis bisa kedaluwarsa jauh lebih cepat atau lebih lambat daripada yang diperkirakan
  • Algoritme dalam model asinkron umumnya menjaga safety tanpa asumsi timing, sementara failure detector seperti timeout hanya memengaruhi liveness
    • Jika timing kacau, performa boleh memburuk, tetapi algoritme tidak boleh mengambil keputusan yang salah
  • Redlock berbeda karena safety-nya bergantung pada berbagai asumsi timing
    • Semua node Redis harus mempertahankan key kira-kira selama durasi yang benar
    • Latensi jaringan harus cukup kecil dibandingkan waktu kedaluwarsa
    • Penghentian proses harus jauh lebih singkat daripada waktu kedaluwarsa

Contoh Redlock rusak dalam timing yang buruk

  • Dengan 5 node Redis A, B, C, D, E dan klien 1 serta 2, jika jam salah satu node melompat ke depan, kedua klien bisa sama-sama percaya bahwa mereka memegang lock
    • Klien 1 memperoleh lock di A, B, C dan tidak bisa menjangkau D, E karena masalah jaringan
    • Jam C melompat ke depan sehingga lock kedaluwarsa
    • Klien 2 memperoleh lock di C, D, E dan tidak bisa menjangkau A, B karena masalah jaringan
    • Akibatnya, klien 1 dan 2 sama-sama menyimpulkan bahwa mereka adalah pemegang lock
  • Masalah serupa juga bisa terjadi jika C crash sebelum mempersistenkan lock ke disk lalu segera restart
    • Dokumentasi Redlock menyarankan agar restart node yang crash ditunda setidaknya selama TTL lock terpanjang
    • Penundaan restart ini juga bergantung pada pengukuran waktu yang cukup akurat, dan bisa gagal jika jam melompat
  • Penghentian proses klien juga bisa merusak Redlock
    • Klien 1 mengirim request lock ke A, B, C, D, E
    • Saat respons sedang dalam perjalanan, klien 1 masuk ke GC stop-the-world
    • Lock di semua node Redis kedaluwarsa
    • Klien 2 memperoleh lock di A, B, C, D, E
    • Klien 1 selesai GC dan menerima respons sukses yang sempat tertahan di kernel network buffer
    • Kedua klien sama-sama yakin bahwa mereka memegang lock
  • Fakta bahwa Redis ditulis dalam C dan tidak memiliki GC tidak membantu
    • Masalahnya muncul pada sistem tempat klien bisa mengalami penghentian GC
    • Agar aman, setelah klien 2 memperoleh lock, pekerjaan klien 1 harus diblokir dengan mekanisme seperti fencing token
  • Latensi jaringan yang panjang juga dapat menghasilkan efek yang sama seperti penghentian proses
    • Jika TCP user timeout disetel jauh lebih pendek daripada TTL Redis, paket yang terlambat mungkin akan diabaikan, tetapi itu tetap bergantung pada detail implementasi TCP tertentu
    • Dalam kasus ini pun kita kembali ke persoalan akurasi pengukuran waktu

Asumsi sistem sinkron yang dibutuhkan Redlock

  • Redlock hanya bekerja benar dalam model sistem sinkron dengan sifat berikut
    • Ada batas atas yang dijamin untuk latensi jaringan
    • Durasi penghentian proses dibatasi
    • Error jam dibatasi
  • Model sinkron bukan berarti jam tersinkronisasi secara sempurna, melainkan bahwa ada batas tetap yang diketahui untuk latensi jaringan, penghentian, dan drift jam
  • Redlock berasumsi bahwa latensi, penghentian, dan drift semuanya kecil dibandingkan TTL lock
    • Jika masalah timing membesar hingga mendekati TTL, algoritme akan gagal
  • Di lingkungan data center umum, asumsi timing seperti ini mungkin terpenuhi pada sebagian besar waktu; ini disebut sistem partially synchronous
  • Jika korektness bergantung pada lock, “sebagian besar waktu” tidaklah cukup
    • Saat asumsi timing runtuh, Redlock dapat melanggar safety, misalnya memberi lease ke klien lain sebelum lease klien pertama berakhir
    • Kasus penundaan paket 90 detik di GitHub adalah bukti bahwa sulit mengasumsikan model sistem sinkron dalam lingkungan nyata
  • Raft, Viewstamped Replication, Zab, dan Paxos termasuk kategori algoritme konsensus yang dirancang untuk model sistem partially synchronous atau model asinkron dengan failure detector
    • Algoritme semacam ini harus membuang asumsi timing dan berhati-hati agar tidak menganggap jaringan, proses, dan jam dalam sistem terdistribusi lebih andal daripada kenyataannya

Kesimpulan dan pilihan yang direkomendasikan

  • Redlock terlalu berat dan mahal untuk lock optimasi efisiensi, dan tidak cukup aman untuk lock yang menyangkut korektness
  • Secara khusus, Redlock pada dasarnya mengasumsikan sistem sinkron dengan batas atas pada latensi jaringan dan waktu eksekusi komputasi, dan bila asumsi itu gagal, safety dapat dilanggar
  • Redlock juga tidak punya kemampuan menghasilkan fencing token untuk melindungi sistem dari latensi jaringan panjang atau proses yang berhenti
  • Jika yang dibutuhkan adalah lock optimasi efisiensi berbasis best effort, lebih baik gunakan algoritme lock Redis node tunggal
    • Peroleh lock dengan conditional set-if-not-exists
    • Lepaskan lock dengan delete atomik hanya jika nilainya cocok
    • Kode harus mendokumentasikan dengan jelas bahwa lock ini bersifat aproksimatif dan kadang bisa gagal
    • Tidak perlu membangun klaster 5 node Redis
  • Untuk lock yang membutuhkan korektness, jangan gunakan Redlock; pakailah sistem konsensus seperti ZooKeeper
    • Jika memungkinkan, gunakan Curator recipes yang mengimplementasikan lock
    • Minimal, bisa juga memakai database seperti PostgreSQL yang memberikan jaminan transaksi yang masuk akal
    • Pemeriksaan fencing token harus dipaksakan pada semua akses resource di bawah lock
  • Redis tetap alat yang berguna jika dipakai sesuai tujuan awalnya, dan seperti semua alat, ia punya batasan yang perlu dipahami dan direncanakan
  • Dalam pembaruan 9 Februari 2016, penulis asli Redlock, Salvatore, menerbitkan tulisan bantahan, tetapi kesimpulannya tetap sama

1 komentar

 
GN⁺ 2024-10-21
Opini Hacker News
  • Di tempat kerja kami memakai Temporal, dan mengimplementasikan distributed lock dengan workflow serta signal khusus.
    Sejauh ini berjalan baik, dan karena bagian pemrosesan terdistribusi dari lock diserahkan ke fitur Temporal, implementasinya juga cukup sederhana.

    • Saya baru tahu tentang Temporal, dan ini terlihat seperti cawan suci workflow yang menyediakan manajemen tugas tingkat tinggi dengan sangat jelas di atas infrastruktur yang kompleks.
      Saya penasaran apakah Temporal memang tak tertandingi di bidang ini, atau ada alternatif dengan level serupa.
      Karena dipisahkan dari Uber dan dipakai vendor-vendor besar, kedengarannya sudah cukup teruji di dunia nyata.
    • Menarik. Bisa jelaskan sedikit lebih detail? Saya ingin mencoba membuat sesuatu yang mirip dengan Temporal.
    • Saya ingin mencoba Temporal, tetapi pernah dengar bahwa itu bisa tidak stabil. Dalam praktiknya apakah berjalan baik?
  • Untuk distributed lock biasanya saya menggunakan PostgreSQL advisory lock.
    Walaupun pekerjaannya tidak terkait database, jika memulai transaksi dan mengambil advisory lock, lock akan dipertahankan sampai aplikasi melepaskannya sendiri atau transaksi berakhir karena crash dan sebagainya.
    Sejauh ini saya merasa ini cukup aman, tetapi baru saja sadar bahwa saya belum pernah memeriksa apakah koneksi database masih normal.
    Kalau itu pekerjaan terkait database, query akan gagal dan pekerjaannya juga gagal, tetapi kalau bukan, kita bisa saja tidak sadar bahwa lock sudah hilang.
    Jika menginginkan kebenaran absolut tanpa fencing token atau operasi atomik, pada akhirnya sepertinya semua pekerjaan membutuhkan two-phase commit.

    • Advisory lock punya banyak jebakan. Lihat [0].
      Mungkin untuk melakukan hal yang dimaksud dengan benar, perlu memakai “EXCLUSIVE” atau “ACCESS EXCLUSIVE”, atau memastikan two-phase commit atau idempotensi untuk pekerjaannya.
      [0] https://www.postgresql.org/docs/current/explicit-locking.htm...
    • Hal yang perlu diperhatikan pada lock, sejauh yang saya tahu, adalah lock itu per koneksi.
      Kebanyakan library biasanya memakai connection pool, jadi harus mengamankan koneksi khusus untuk lock, dan pemeriksaan lock berkala juga wajib dilakukan lewat koneksi tersebut.
  • Sebaiknya baca komentar saya yang dulu saya tinggalkan di komentar blog ini, serta balasan yang saya tulis di blog saya.
    Kalau diurutkan secara acak, penulis melewatkan poin inti tentang bagaimana algoritma itu bekerja, lalu menolak algoritma tersebut dengan dasar-dasar yang tersisa dan lebih lemah.
    Pernyataan bahwa pada komputer dan API modern mustahil menunggu kira-kira selama waktu yang benar juga tidak benar. Jeda GC itu bounded dan monotonic clock juga berfungsi, jadi itu asumsi yang dapat diterima.
    Mengkritik mekanisme pelepasan otomatis itu sendiri karena membuka potensi race condition berbeda dengan mengkritik di dalam tujuan algoritma dan model sistemnya.
    Redlock telah berhasil dipakai selama bertahun-tahun dalam banyak use case, dan jika timeout dibuat jauh lebih besar daripada waktu penyelesaian pekerjaan serta jeda arbitrer yang bisa terjadi pada sistem operasi umum, sangat sulit untuk memicu race condition.
    Tentu saja, kalau timeout pelepasan otomatis dibuat terlalu kecil dan pekerjaan bisa dengan mudah memakan waktu selama itu, itu kesalahan desain, tetapi bukan masalah Redlock itu sendiri.

    • Jujur saja, sejak dulu saya kurang memahami tulisan balasan di blog itu. Mungkin pertanyaan ini bisa membantu menemukan titik temu.
      Jika timeout cukup pendek (misalnya 1–2 detik), pekerjaan biasanya memakai sekitar 90% dari timeout itu, dan pekerjaan yang dilakukan saat memegang lock RedLock sama sekali tidak boleh berjalan bersamaan dengan pemegang lock lain, apakah Anda akan memakai RedLock?
      Menurut saya jawaban yang benar di sini selalu “tidak”. Karena risiko lease kedaluwarsa sebelum klien selesai bekerja sangat besar.
      Karena RedLock tidak bisa menjamin mutual exclusion dalam semua situasi, pekerjaan harus diubah menjadi idempoten, dan tipe seperti ini lebih baik diimplementasikan dengan optimistic locking.
    • Bisa beri tautannya?
  • Saya sedang memperkuat kembali pengetahuan level rendah dan algoritma; buku bagus untuk topik ini apa? Saya sudah punya buku yang ditulis penulisnya.
    Saya ingin membuat sesuatu untuk bersenang-senang, tetapi materi yang ada hanya level mainan atau terlalu kompleks.

    • Saya merekomendasikan System Design Interview I, II karya Alex Xu.
      Pilih satu topik lalu coba implementasikan sungguhan.
  • Dulu saya menulis artikel blog tentang distributed lock berdasarkan materi ini: https://medium.com/sahibinden-technology/an-easy-integration...

  • Penjelasan “lock punya timeout (artinya lease)” terdengar aneh.
    Pertama, jika klien crash, bahkan tanpa timed lease pun OS atau supervisor seharusnya melepaskan lock, dan kalau keduanya mati pun koneksi akhirnya terputus; sistem jaringan seharusnya mendeteksinya lewat reset, timeout, tidak adanya heartbeat, dan sebagainya, lalu menginvalidasi koneksi dan melepas lock.
    Kedua, jika masalahnya adalah klien terlalu lama memegang lock tanpa crash karena bug, bukankah semestinya ada supervisor yang mendeteksi itu lalu membunuh klien sebelum melepas lock untuk pihak lain?
    Ketiga, jika lock dengan timeout disediakan untuk menangani corner case seperti ini, bukankah program nyata harus diberi tahu melalui exception, signal, termination, atau semacamnya? Dan bukankah harus menunggu verifikasi bahwa program sudah menerima notifikasi sebelum lock dilepas?
    Gagasan membiarkan program terus menjalankan alur kontrol normal meski sudah timeout tampak seperti akar masalahnya, tetapi saya tidak mengerti mengapa semua orang melewatinya begitu saja. Apakah saya melewatkan alasan yang jelas?

    • Ini bukan mutex, melainkan versi distributed system-nya.
      Pihak yang menginvalidasi lock di sisinya adalah layanan penyimpanan, dan tanpa jaminan tambahan yang tidak disediakan Redlock, klien tidak bisa mendeteksi sendiri masalahnya.
    • Asumsi bahwa server akan selalu menerima RST atau FIN dari klien itu salah.
      Dalam beberapa kasus paket-paket ini di-drop, dan klien di mesin jarak jauh sudah mati tetapi koneksi terbuka bisa tetap ada di server.
      Sebagai tambahan, bukan saya yang menekan downvote.
  • Saya mengimplementasikan distributed lock dengan Deno KV yang di-host oleh Deno dan Deno Deploy
    Secara internal menggunakan FoundationDB, sebuah basis data terdistribusi, dan instance Deno yang berjalan di perangkat lokal terhubung ke Deno KV yang sama untuk memperoleh lock
    Dengan PostgreSQL pun bisa bekerja menggunakan SELECT FOR UPDATE, tetapi basis datanya sendiri bukan terdistribusi

  • Pada 2018 kami mengevaluasi Redis untuk kasus penggunaan kami, tetapi akhirnya memilih solusi yang kurang mencolok, dan benar-benar tidak pernah gagal sekalipun
    Kasus penggunaannya adalah membagikan tiket beridentitas satu per satu dari sekumpulan tiket terbatas untuk sebuah kampanye, mirip seperti Ticketmaster menetapkan kursi di venue pertunjukan
    Saat ada permintaan masuk, kami harus memberikan tiket yang tersedia, menempelkan metadata permintaan ke tiket yang dialokasikan, lalu mengecualikannya dari target permintaan berikutnya
    Akurasi adalah hal utama karena di masa lalu ada kampanye yang gagal seperti alokasi berlebih, alokasi kurang, dan alokasi ganda
    Kami juga mencoba implementasi sederhana dengan Redis untuk memperoleh lock, memeriksa lock, menjalankan pekerjaan, lalu melepas lock, tetapi saat itu beban operasionalnya terlalu besar bagi kami, dan untungnya kami tidak menempuh jalan itu
    Pilihan akhirnya adalah Postgres. “Distributed lock” kami lebih mirip pernyataan UPDATE gabungan yang memakai fitur bawaan Postgres, mengubah permintaan menjadi semacam operasi himpunan sehingga basis data mengembalikan record sukses atau penanda gagal. Transaksi ACID yang menang
    Setelah menyelesaikan soal akurasi, kami melihat skala dan performa; kami tidak membutuhkan jutaan permintaan per detik, tetapi punya tolok ukur untuk lonjakan sesaat
    Kami mengoptimalkan instance basis data baca/tulis di dalam klaster, menempatkan kampanye yang lebih besar atau permintaannya tinggi secara strategis pada sistem yang ditentukan, dan terus mengoptimalkan selama 2 tahun, tetapi tidak pernah ada satu pun kampanye distribusi tiket yang gagal
    Saya bukan pakar teknologi distributed lock; saya hanya fokus pada masalah yang perlu diselesaikan, mencoba beberapa hal, lalu menemukan solusi yang tepat

    • Benar bahwa jika batasnya hingga 50 ribu transaksi atomik singkat per detik, Postgres saja sudah cukup
      Karena transaksi UPDATE hanya berlangsung beberapa mikrodetik, masalahnya bisa dipusatkan, dan itu lebih sederhana, cepat, serta aman
      Namun seperti dijelaskan dalam tulisan tersebut, ini bukan masalah terdistribusi
      Lock dalam sistem terdistribusi berbeda dari mutex pada aplikasi multi-thread; ini lebih rumit karena banyak node dan jaringan bisa gagal secara independen dengan berbagai cara
      Distributed lock diperlukan ketika transaksi bisa memakan waktu dari beberapa detik hingga beberapa jam, dan mesin terkait bisa gagal saat sedang memegang lock
    • Hal penting yang ditunjukkan kasus ini adalah bahwa yang dibutuhkan bukan lock, melainkan constraint
      Dalam kasus ini constraint-nya adalah “jangan jual lebih dari N tiket”, dan sebagian besar skala trafik realistis untuk masalah seperti ini bisa diselesaikan dengan perilaku transaksi basis data relasional tradisional, sementara pengelolaan lock internal diserahkan ke basis data
      Saya berharap developer tidak terlalu cepat melompat ke “mari membuat distributed lock”. Hampir selalu ada jawaban yang lebih baik, tetapi jawaban itu berbeda-beda untuk tiap aplikasi
    • Pada akhirnya jawabannya adalah, berbeda dari dugaan, dalam sebagian besar kasus distributed lock tidak diperlukan :)
    • Ini tampak seperti masalah yang sangat mudah diparalelkan sampai memalukan, karena bisa di-sharding ke instance berbeda untuk tiap konser
      Mungkin cocok untuk sesuatu seperti SQLite baru dari Cloudflare
    • Ini adalah cara terbaik, dan sebenarnya satu-satunya cara yang masuk akal untuk mendekati masalahnya
      Tempat pertama kali saya membaca hal ini adalah di sini: https://code.flickr.net/2010/02/08/ticket-servers-distribute...
  • Banyak engineer tidak sungguh-sungguh peduli pada masalah correctness sampai sudah terlambat. Mirip dengan keamanan
    Bahkan ketika peduli, banyak yang tidak memverifikasi apakah yang mereka lakukan sudah benar
    Misalnya di bidang saya, microservice, actor, dan proses saling mengirim pesan lewat jaringan, dan lebih dari 95% implementasi yang saya lihat memiliki edge case di mana pesan bisa hilang atau diproses dengan urutan yang berubah
    Namun insentif tidak cukup selaras untuk memperbaiki masalah ini. Struktur kompensasi eksekutif dan engineer tidak selaras dengan hasil terbaik bagi pelanggan dan pemegang saham

    • Microservice sendiri sering kali merupakan gejala masalah ini
      Orang ingin memasang batas jaringan di antara pemanggilan fungsi tanpa alasan kuat, lalu tanpa henti membuat server dan client HTTP, serialisasi dan deserialisasi JSON untuk pemanggilan fungsi itu, memakai gRPC kalau beruntung, dan mencoba mengimplementasikan ulang hal-hal seperti transaksi terdistribusi di seberang batas jaringan tersebut
      Pada akhirnya yang muncul hanyalah pekerjaan sibuk untuk menangani “interaksi menyeramkan dari jarak jauh” yang tak terhindarkan
    • Untuk memperbaikinya, pertama-tama harus mengukur dan memantau, lalu menetapkan service level objective yang merepresentasikan pengalaman pelanggan
      Tim produk dan tim engineering harus menyepakatinya, dan jika SLO dilanggar, fokus harus dialihkan ke stabilitas sistem
      Sulit meyakinkan semua orang, sehingga dibutuhkan kepemimpinan yang baik
      Ketika bug bermunculan, fitur baru lambat atau nyaris tidak ada, dan pelanggan mulai pergi, alasan bahwa kualitas harus dijadikan bagian dari proses menjadi sangat mudah diterima
      Pemimpin yang matang bergerak mendahului tahap itu sedini mungkin
    • Masalah seperti itu bahkan bisa membuat orang tak bersalah dipenjara atau meninggal
      [0] https://en.wikipedia.org/wiki/British_Post_Office_scandal
    • Saya rasa ada tingkat penyelarasan insentif tertentu. Edge case seperti ini rumit, sehingga kemungkinan besar developer harus menangani banyak tiket dukungan, dan itu tidak baik bagi siapa pun
      Namun saya tidak melihat cara yang jelas untuk meyakinkan para manajer kemarin agar memberi waktu untuk membangunnya dengan benar
  • Ini membuat pekerjaan menjadi terlalu rumit
    Jika ada sesuatu seperti fencing token yang dibahas dalam tulisan itu, lock tidak diperlukan
    Token tidak harus meningkat secara monoton; cukup berupa nilai unik pasif yang dimiliki bersama oleh klien dan penyimpanan
    Jika disebut token versi, nilainya boleh meningkat secara monoton, dan UUID yang biasanya lebih mudah dibuat juga berfungsi. Secara teknis hash dari seluruh data di penyimpanan juga memungkinkan, tetapi tidak praktis
    Alurnya seperti ini. Klien mengambil token versi saat ini beserta data yang akan diubah dari penyimpanan, dan penyimpanan membaca data dan token secara atomik untuk menjamin bahwa token itu milik versi data tersebut
    Setelah itu klien mengirim kembali token versi bersama perubahan, dan penyimpanan hanya menerima perubahan jika token saat ini cocok dengan token yang dikirim, lalu membuat token versi baru secara atomik
    Lock bisa saja diperkenalkan karena alasan lain, tetapi dalam sistem terdistribusi ia harus independen dari integritas penyimpanan
    Saya juga kurang suka istilah “lock”. Karena sifatnya sementara dan tidak terjamin, lease atau reservasi mungkin lebih tepat menyampaikan maknanya

    • Yang dijelaskan adalah compare-and-swap (CAS), dan itu solusi yang baik
      Ini adalah cara memindahkan kompleksitas ke sisi database, tetapi perlu diingat bahwa di sini konteksnya adalah lock terdistribusi
      Jika hanya satu database, semuanya sederhana sampai database crash dan kita tidak tahu penulisan CAS mana yang benar-benar diterapkan
      Pada sistem besar yang membutuhkan ketersediaan tinggi dan backup multi-datacenter, skenario seputar kegagalan node membuat pendekatan ini juga bisa rusak dan menjadi cukup kompleks
      Biasanya digunakan log transaksi berbentuk Paxos. Jangan berasumsi ada solusi mudah untuk sistem terdistribusi. Selalu merepotkan
    • Kamu salah menafsirkan masalahnya, dan sedang mengusulkan solusi untuk masalah lain
    • Ini dikenal sebagai optimistic locking. Namun saya tidak akan menyebutnya mekanisme lock terdistribusi
    • Penjelasan ini melewatkan alasan pertama penggunaan lock dalam tulisan tersebut
      Dari sudut pandang efisiensi, memegang lock dapat menghindari pengerjaan tugas yang sama dua kali secara tidak perlu. Misalnya komputasi yang mahal
      Jika lock gagal dan dua node mengerjakan tugas yang sama, dampaknya mungkin sepele bila hanya berupa sedikit kenaikan biaya atau duplikasi notifikasi email yang sama
      Namun menurut saya, beberapa node mengerjakan tugas yang sama bisa jauh lebih buruk daripada contoh yang disebutkan. Karena itu dapat menghambat pemrosesan terdistribusi yang scalable itu sendiri
    • Jika tidak memakai token yang meningkat secara monoton, bukankah itu bisa berujung pada keadaan tidak konsisten?
      Misalkan sistem penyimpanan memiliki dua node, dan ada dua proses baca-ubah-tulis yang berjalan. Proses 1 dan 2 sama-sama mendapatkan token awal abc
      Proses 1 melakukan commit sehingga token berubah menjadi cde, dan perubahan itu di-stream ke node 2, tetapi karena latensi jaringan, perubahan tersebut terlambat tiba di node 2
      Sementara itu, jika proses 2 melakukan commit ke node 2 dengan token abc, node 2 belum menerima pesan dari node 1, sehingga ia menerima perubahan tersebut dan sistem menjadi tidak konsisten
      Dengan fencing token yang meningkat secara monoton, hal seperti ini tidak terjadi. Sebab persyaratan itu memaksa node-node menyepakati urutan keseluruhan operasi sebelum memberikan token