4 puan yazan GN⁺ 2024-10-21 | 1 yorum | WhatsApp'ta paylaş
  • Redis tabanlı Redlock, hataya dayanıklı dağıtık kilitleri hedefler; ancak doğruluğun söz konusu olduğu işler için yeterince güvenli değildir, verimlilik optimizasyonu amacıyla da gereğinden karmaşıktır
  • Dağıtık kilitlerde önce yinelenen işleri azaltmaya yönelik verimlilik amacı ile paylaşılan durumu korumaya yönelik doğruluk amacı ayrılmalıdır; ölçüt, başarısızlık durumunda maliyet artışı mı yoksa veri bozulması mı olduğudur
  • Kusursuz bir kilit servisi olsa bile uzun GC duraklamaları, proses askıya alınmaları ve ağ gecikmeleri nedeniyle lease süresi dolduktan sonra eski yazmalar çalışabilir; bu yüzden fencing token gerekir
  • Redlock, her kilit ediniminde monoton artan bir token üretemez; Redis anahtar süresinin dolması gettimeofday tabanlı sistem saatine bağlı olduğu için saat sıçramaları veya gecikme durumlarında güvenlik bozulabilir
  • Doğruluk gerektiren kilitlerde ZooKeeper gibi konsensüs sistemleri ve fencing token denetimi kullanılmalı; Redis tek düğüm kilitleri ise yaklaşık ve kritik olmayan kullanımlarla sınırlandırılmalıdır

Redlock'u incelemeye çıkış noktası

  • Redlock, Redis üzerinde hataya dayanıklı dağıtık kilitleri, daha doğru ifadeyle lease'leri uygulayan bir algoritmadır
  • Zaten 10'dan fazla bağımsız uygulaması var ve bu algoritmaya kimin güvendiği bilinmediği için herkese açık biçimde incelenmeye değerdir
  • Redis'in kendisi, sunucular arasında geçici, yaklaşık ve hızlı değişen verileri paylaşma kullanımına çok uygundur
    • Örn: IP adresine göre istek sayaçları, kullanıcı ID'sine göre benzersiz IP kümeleri
  • Kaygı uyandıran nokta, Redis'in giderek daha güçlü tutarlılık ve dayanıklılık beklenen veri yönetimi alanlarında kullanılmasıdır; dağıtık kilitler de bu alanlardan biridir

Kilidin amacı: verimlilik mi doğruluk mu?

  • Dağıtık uygulamalarda kilit, birden fazla düğüm aynı işi denediğinde işin aynı anda yalnızca biri tarafından yapılmasını sağlayan bir mekanizmadır
  • Kilit kullanma nedenleri genel olarak ikiye ayrılır
    • Verimlilik: Aynı pahalı hesaplamayı iki kez yapmamak için bir optimizasyondur; başarısız olsa bile AWS maliyetinin biraz artması veya aynı e-posta bildiriminin iki kez gitmesiyle sınırlı kalır
    • Doğruluk: Eşzamanlı proseslerin aynı durumu bozmasını engelleyen bir mekanizmadır; başarısız olursa dosya bozulması, veri kaybı, kalıcı tutarsızlık veya yanlış ilaç verilmesi gibi ciddi sorunlar doğabilir
  • Verimlilik amaçlı kilitlerde, 5 Redis sunucusu ve çoğunluk doğrulaması kullanan Redlock'un maliyeti ve karmaşıklığı gereksizdir
    • Tek bir Redis instance'ı ve gerekirse asenkron replikasyon kullanmak daha uygundur
    • Bu durumda elektrik kesintisi veya Redis düğümü sorunu nedeniyle bazı kilitler kaybedilebilir; ancak kritik olmayan bir optimizasyonsa bu kabul edilebilir bir hatadır
  • Redlock, 5 replika ve çoğunluk nedeniyle doğruluğun önemli olduğu kilitlere uygun görünür; ama gerçekte bu amaç için uygun değildir

Yalnızca lease ile kaynaklar güvenli biçimde korunamaz

  • Dağıtık sistemlerdeki kilitler, çok iş parçacıklı uygulamalardaki mutex'lerden farklıdır; düğümler ve ağ birbirinden bağımsız biçimde arızalanabildiği için daha karmaşıktır
  • Paylaşılan depolamadaki bir dosyayı güncelleyen tipik akış; kilidi edinme, dosyayı okuma, değiştirme, yeniden yazma ve kilidi bırakma şeklindedir
    • Kilit, iki istemcinin read-modify-write işlemini aynı anda yapıp güncellemelerin kaybolmasını önlemek içindir
  • İstemci kilidi elinde tutarken uzun süre durursa lease'in süresi dolabilir
    • GC devreye girip istemciyi uzun süre durdurabilir
    • Lease, çöken bir istemcinin kilidi sonsuza kadar tutmasını önleyen iyi bir tasarımdır; ancak durma süresi sona erme süresinden uzunsa istemci sürenin dolduğunu bilmeden tehlikeli bir yazma işlemi yapabilir
  • Bu sorun teorik bir örnek değildir; geçmişte HBase'te de benzer bir sorun yaşanmıştır
    • “stop-the-world” GC duraklamalarının birkaç dakika sürdüğü örnekler vardır
    • HotSpot JVM'in CMS gibi “concurrent” GC'leri bile zaman zaman uygulamayı durdurmak zorundadır
  • Yazmadan hemen önce kilidin süresinin dolup dolmadığını kontrol etmek sorunu çözmez
    • GC, son kontrol ile yazma işlemi arası dahil, herhangi bir noktada çalışan iş parçacığını durdurabilir

Proses durması ve ağ gecikmesi olağan tehdit modelidir

  • Uzun GC duraklamaları olmayan bir runtime kullanılsa bile proses çeşitli nedenlerle durabilir
    • Bellekte olmayan bir adres okununca page fault oluşabilir
    • Disk EBS ise bir değişkeni okumak, Amazon ağı üzerinden senkron bir isteğe dönüşebilir
    • CPU çekişmesi, zamanlayıcı gecikmesi veya yanlışlıkla gönderilen SIGSTOP da prosesi durdurabilir
  • Ağ gecikmesi de aynı sorunu yaratır
    • Uygulama bir yazma isteği göndermiştir, ancak paket gecikip lease süresi dolduktan sonra depolama sunucusuna ulaşabilir
    • GitHub'daki bir kesintide ağ paketleri yaklaşık 90 saniye gecikmiştir
  • Ethernet ve IP gibi paket ağları paketleri keyfi olarak geciktirebilir ve pratikte bu olur
  • Bu nedenle iyi yönetilen ağlarda bile zamanlama varsayılamaz; basit lease tabanlı kod, hangi kilit servisi kullanılırsa kullanılsın temelde güvenli değildir

Eski yazmalar fencing token ile engellenmelidir

  • Çözüm, tüm depolama yazma isteklerine fencing token eklemektir
  • Fencing token, istemcinin kilidi her edindiğinde artan bir sayıdır
    • Örn: İstemci 1, token 33 ile lease'i aldıktan sonra uzun süre durur ve lease'in süresi dolar
    • İstemci 2, token 34 ile yeni bir lease alır ve depolamaya yazma isteği gönderir
    • Daha sonra istemci 1 uyanıp token 33 ile yazma gönderirse, depolama zaten daha yüksek token 34'ü işlediği için token 33 isteğini reddeder
  • Güvenlik için depolama sunucusu token'ı etkin biçimde kontrol etmeli ve token değeri geriye giden yazmaları reddetmelidir
  • Kilit servisi katı biçimde monoton artan token'lar üretirse kilit güvenli hale getirilebilir
    • ZooKeeper kilit servisi olarak kullanıldığında zxid veya znode sürüm numarası fencing token olarak kullanılabilir
  • Redlock'un büyük sorunu, fencing token üretme işlevinin olmamasıdır
    • Redlock'un benzersiz rastgele değeri, gerekli monoton artışı sağlamaz
    • Tek bir Redis düğümündeki sayaç, o düğüm arızalanabileceği için yeterli değildir
    • Birden fazla düğümdeki sayaçlar birbirinden sapabilir
    • Fencing token üretmek için de muhtemelen bir konsensüs algoritması gerekir

Redlock güvenliğini zaman varsayımlarına dayandırır

  • Dağıtık algoritmalarda pratik model asenkron model ve güvenilmez hata algılayıcısıdır
    • Prosesler keyfi uzunlukta durabilir
    • Paketler ağda keyfi biçimde gecikebilir
    • Saatler keyfi biçimde yanlış olabilir
    • Buna rağmen algoritma doğru kararlar vermelidir
  • Saatler yalnızca bir düğüm çöktüğünde sonsuza kadar beklememek için timeout üretiminde kullanılabilir
    • Timeout'un doğru olması gerekmez; bir isteğin timeout'a düşmesi karşı düğümün mutlaka çöktüğü anlamına gelmez
    • Ağ gecikmesi veya yerel saat hatası da olabilir
  • Redis, anahtar süresinin dolmasına karar verirken monotonic clock değil gettimeofday kullanır
    • gettimeofday sistem zamanının kesintili biçimde sıçrayabileceği anlamına gelir
    • NTP saati ayarlarsa veya yönetici zamanı elle değiştirirse Redis anahtarlarının süresi beklenenden çok daha hızlı ya da yavaş dolabilir
  • Asenkron modeldeki algoritmalar genellikle zamanlama varsayımı olmadan güvenliği korur; timeout gibi hata algılayıcıları yalnızca canlılığı etkiler
    • Zamanlama bozulursa performans düşebilir, ancak yanlış karar verilmemelidir
  • Redlock ise bundan farklı olarak güvenliğini birden fazla zamanlama varsayımına dayandırır
    • Tüm Redis düğümleri anahtarı yaklaşık doğru süre boyunca tutmalıdır
    • Ağ gecikmesi, sona erme süresinden yeterince küçük olmalıdır
    • Proses duraklaması, sona erme süresinden çok daha kısa olmalıdır

Kötü zamanlamada Redlock'un bozulduğu örnekler

  • 5 Redis düğümü A, B, C, D, E ve istemci 1, 2 varken bir düğümün saati ileri sıçrarsa iki istemci de kilide sahip olduğuna inanabilir
    • İstemci 1, A, B, C'de kilidi alır; ağ sorunu nedeniyle D ve E'ye ulaşamaz
    • C'nin saati ileri sıçrar ve kilidin süresi dolar
    • İstemci 2, C, D, E'de kilidi alır; ağ sorunu nedeniyle A ve B'ye ulaşamaz
    • Sonuçta istemci 1 ve 2 kendisini kilit sahibi olarak görür
  • C kilidi diske kalıcılaştırmadan çöker ve hemen yeniden başlarsa benzer bir sorun oluşabilir
    • Redlock dokümanı, çöken düğümün yeniden başlatılmasının en uzun kilit TTL'inden daha uzun süre geciktirilmesini önerir
    • Bu yeniden başlatma gecikmesi de makul derecede doğru zaman ölçümüne dayanır; saat sıçrarsa başarısız olabilir
  • İstemci prosesinin durması da Redlock'u bozabilir
    • İstemci 1, A, B, C, D, E'ye kilit isteği gönderir
    • Yanıtlar yoldayken istemci 1 stop-the-world GC'ye girer
    • Tüm Redis düğümlerindeki kilitlerin süresi dolar
    • İstemci 2, A, B, C, D, E'de kilidi alır
    • İstemci 1 GC'yi bitirir ve kernel ağ tamponunda bekleyen başarılı yanıtları alır
    • İki istemci de kilide sahip olduğuna inanır
  • Redis'in C ile yazılmış olması ve GC'ye sahip olmaması yardımcı olmaz
    • Sorun, istemcinin GC duraklaması yaşayabildiği sistemlerde ortaya çıkar
    • Güvenlik için fencing token gibi bir yöntemle istemci 2 kilidi aldıktan sonra istemci 1'in çalışması engellenmelidir
  • Uzun ağ gecikmesi de proses durmasıyla aynı etkiyi yaratabilir
    • TCP user timeout, Redis TTL'inden çok daha kısa ayarlanırsa geciken paketlerin yok sayılma olasılığı vardır; ancak emin olmak için somut TCP implementasyonuna bakmak gerekir
    • Bu durumda da yine zaman ölçümünün doğruluğu sorununa dönülür

Redlock'un gerektirdiği senkron sistem varsayımları

  • Redlock yalnızca aşağıdaki özelliklere sahip senkron sistem modelinde doğru çalışır
    • Ağ gecikmesinin üst sınırı garanti edilir
    • Proses durma süresi sınırlıdır
    • Saat hatası sınırlıdır
  • Senkron model, saatlerin tam olarak senkronize olduğu anlamına gelmez; ağ gecikmesi, durma ve saat drift'i için bilinen sabit üst sınırlar olduğu anlamına gelir
  • Redlock; gecikme, durma ve drift'in hepsinin kilit TTL'ine göre küçük olduğunu varsayar
    • Zamanlama sorunları TTL kadar büyürse algoritma başarısız olur
  • Tipik veri merkezi ortamlarında bu zamanlama varsayımları çoğu zaman sağlanabilir; buna kısmi senkron sistem denir
  • Doğruluk kilide bağlıysa “çoğu zaman” yeterli değildir
    • Zamanlama varsayımı bozulduğu anda Redlock, bir istemcinin lease'i dolmadan başka bir istemciye lease vermek gibi güvenliği ihlal eden durumlara yol açabilir
    • GitHub'daki 90 saniyelik paket gecikmesi örneği, gerçek ortamlarda senkron sistem modelini varsaymanın zor olduğuna kanıttır
  • Raft, Viewstamped Replication, Zab ve Paxos; kısmi senkron sistem modeli veya hata algılayıcılı asenkron model için tasarlanmış konsensüs algoritmaları sınıfına girer
    • Bu tür algoritmalar zamanlama varsayımlarını terk etmelidir; dağıtık sistemin ağı, prosesleri ve saatleri gerçekte olduğundan daha güvenilir varsayılmamalıdır

Sonuç ve önerilen seçenekler

  • Redlock, verimlilik optimizasyonu amaçlı kilitler için gereksiz yere ağır ve pahalıdır; doğruluğun söz konusu olduğu kilitler için yeterince güvenli değildir
  • Özellikle ağ gecikmesi ve işlem yürütme süresi için üst sınır bulunan senkron bir sistemi fiilen varsayar; bu varsayım bozulursa güvenliği ihlal edebilir
  • Sistemi uzun ağ gecikmelerinden veya durmuş proseslerden koruyacak fencing token üretme işlevi de yoktur
  • En iyi çaba temelli verimlilik optimizasyonu kilidine ihtiyaç varsa Redis'in tek düğümlü kilit algoritmasını kullanmak daha iyidir
    • Koşullu set-if-not-exists ile kilit edinilir
    • Kilit, yalnızca değer eşleştiğinde atomik olarak silinerek bırakılır
    • Kodda kilidin yaklaşık olduğu ve zaman zaman başarısız olabileceği açıkça belgelenmelidir
    • 5 Redis düğümlü bir küme kurmaya gerek yoktur
  • Doğruluk gerektiren kilitlerde Redlock kullanmak yerine ZooKeeper gibi bir konsensüs sistemi kullanılmalıdır
    • Mümkünse kilitleri uygulayan Curator recipes kullanılabilir
    • En azından makul işlem garantileri sunan PostgreSQL gibi bir veritabanı kullanılabilir
    • Kilit altındaki tüm kaynak erişimlerinde fencing token denetimi zorunlu tutulmalıdır
  • Redis, amaçlanan kullanımına uygun kullanıldığında yararlı bir araçtır; her aracın sınırları vardır, bu yüzden bu sınırları bilip plan yapmak gerekir
  • 9 Şubat 2016 güncellemesinde Redlock'un özgün yazarı Salvatore bir karşı yazı yayımladı, ancak sonuç değişmiyor

1 yorum

 
GN⁺ 2024-10-21
Hacker News yorumları
  • İş yerinde Temporal kullanıyoruz ve özel bir workflow ile signal’lar üzerinden dağıtık lock uyguladık.
    Şu ana kadar iyi çalışıyor; lock’un dağıtık işleme kısmını Temporal’ın özelliklerine bırakınca uygulama da epey basitleşiyor.

    • Temporal’ı az önce öğrendim; karmaşık altyapı üzerinde üst seviye iş yönetimini çok net biçimde sunan workflow’ların kutsal kâsesi gibi görünüyor.
      Bu alanda Temporal’ın benzersiz olup olmadığını, yoksa benzer seviyede alternatifler bulunup bulunmadığını merak ediyorum.
      Uber’den ayrılmış olması ve büyük tedarikçiler tarafından kullanılması, yeterince sahada doğrulanmış gibi geliyor.
    • İlginç. Biraz daha ayrıntı verebilir misin? Temporal ile benzer bir şey yapmak istiyorum.
    • Temporal’ı denemek istiyorum ama kararsız olabileceğine dair şeyler duydum. Gerçekte iyi çalıştı mı?
  • Dağıtık lock için genelde PostgreSQL advisory lock kullanıyorum.
    İş veritabanıyla ilgisiz olsa bile bir transaction başlatıp advisory lock alınca, uygulama kendisi serbest bırakana ya da crash gibi bir nedenle transaction bitene kadar lock tutuluyor.
    Şimdiye kadar bunu oldukça güvenli buluyordum, ama az önce veritabanı bağlantısının hâlâ sağlıklı olup olmadığını hiç kontrol etmediğimi fark ettim.
    Veritabanıyla ilgili bir işse sorgu başarısız olur ve iş de başarısız olur; ama değilse lock’u çoktan kaybetmiş olduğumu fark etmeyebilirim.
    Fencing token veya atomik işlemler gibi şeyler olmadan mutlak doğruluk istiyorsak, sonunda tüm işler için iki aşamalı commit mi gerekiyor diye düşünüyorum.

    • Advisory lock’larda çok fazla tuzak var. [0]’a bakın.
      Muhtemelen amaçladığınız şeyi doğru yapmak için “EXCLUSIVE” ya da “ACCESS EXCLUSIVE” kullanmanız veya iş için iki aşamalı commit ya da idempotency sağlamanız gerekir.
      [0] https://www.postgresql.org/docs/current/explicit-locking.htm...
    • Lock’larda dikkat edilmesi gereken şey, bildiğim kadarıyla lock’un bağlantı başına olması.
      Çoğu kütüphane genelde connection pool kullandığı için lock’a özel bir bağlantı ayırmalı ve periyodik lock kontrolünü de mutlaka o bağlantı üzerinden yapmalısınız.
  • Daha önce bu blogun yorumlarında bıraktığım yorumumu ve kendi blogumda yazdığım yanıtı okumanızı öneririm.
    Rastgele sırayla değinirsem, yazar algoritmanın nasıl çalıştığına dair kritik noktayı kaçırmış ve ardından geriye kalan daha zayıf gerekçelerle algoritmayı reddetmiş.
    Modern bilgisayarlarda ve API’lerde yaklaşık olarak doğru süre kadar beklemenin imkânsız olduğu iddiası da doğru değil. GC duraklamaları bounded’dır ve monotonic clock’lar da çalışır; bu yüzden kabul edilebilir bir varsayımdır.
    Otomatik serbest bırakma mekanizmasının doğası gereği potansiyel bir race condition açığa çıkardığını söyleyerek eleştirmek ile algoritmanın hedefleri ve sistem modeli içinde eleştirmek farklı şeylerdir.
    Redlock yıllardır pek çok kullanım senaryosunda başarıyla kullanılıyor; timeout’u işin tamamlanma süresinden ve sıradan işletim sistemlerinde yaşanabilecek keyfi duraklamalardan çok daha büyük ayarlarsanız race condition oluşturmak çok zordur.
    Elbette otomatik serbest bırakma timeout’unu çok küçük ayarlayıp işin o kadar sürebilmesi kolaysa bu bir tasarım hatasıdır; ama bu Redlock’un kendisinin sorunu değildir.

    • Açıkçası o eski yanıt blog yazısını baştan beri pek anlayamamıştım. Belki şu soruyla ortak bir zemin bulabiliriz.
      Timeout oldukça kısa olduğunda (ör. 1–2 saniye), iş genelde bu timeout’un yaklaşık %90’ını kullandığında ve RedLock lock’u tutulurken yapılan işin başka bir lock sahibiyle asla eşzamanlı çalışmaması gereken bir durumda RedLock kullanır mıydınız?
      Burada doğru cevabın her zaman “hayır” olduğunu düşünüyorum. Çünkü client’ın işi bitirmeden lease’in süresinin dolma riski çok yüksektir.
      RedLock her durumda mutual exclusion garanti edemediği için işi idempotent hâle getirmek gerekir; bu tür işler de optimistic lock ile uygulanmaya daha uygundur.
    • Link verebilir misin?
  • Alt seviye ve algoritma bilgimi yeniden güçlendiriyorum; bu konu için iyi kitaplar neler olabilir? Yazarın yazdığı kitap bende var.
    Eğlence için bir şeyler yapmak istiyorum ama kaynaklar ya oyuncak seviyesinde ya da aşırı karmaşık.

    • Alex Xu’nun System Design Interview I, II kitaplarını öneririm.
      Bir konu seçip gerçekten uygulayabilirsiniz.
  • Daha önce bu kaynağı temel alarak dağıtık lock üzerine bir blog yazısı yazmıştım: https://medium.com/sahibinden-technology/an-easy-integration...

  • “Lock’un timeout’u vardır (yani lease’tir)” açıklaması tuhaf geliyor.
    Birincisi, client crash olursa timed lease olmasa bile OS ya da supervisor zaten lock’u serbest bırakmalı; ikisi de ölse bile bağlantı eninde sonunda kopar ve ağ sistemi reset, timeout, heartbeat yokluğu gibi yollarla bunu tespit edip bağlantıyı geçersiz kıldıktan sonra lock’u bırakmalı.
    İkincisi, sorun client’ın bir bug yüzünden crash olmadan lock’u çok uzun süre tutmasıysa, bir supervisor bunu tespit edip başkaları için lock’u bırakmadan önce client’ı öldürmeli değil mi?
    Üçüncüsü, bu tür köşe durumları ele almak için timeout’lu lock kullanılıyorsa, gerçek programa exception, signal, termination gibi bir yolla bildirmek gerekmez mi? Ve lock’u bırakmadan önce programın bildirimi aldığının doğrulanmasını beklemek gerekmez mi?
    Timeout gerçekleşmişken programın normal control flow’u çalıştırmaya devam etmesine izin verme fikri, sorunun temel nedeni gibi görünüyor; herkesin bunu neden geçiştirdiğini bilmiyorum. Çok bariz bir şeyi mi kaçırıyorum?

    • Bu bir mutex değil, onun dağıtık sistem sürümü.
      Lock’u kendi tarafında geçersiz kılan taraf storage service’tir ve Redlock’un sağlamadığı ek garantiler olmadan client kendi sorununu kendi başına algılayamaz.
    • Sunucunun client’tan her zaman RST veya FIN alacağı varsayımı yanlıştır.
      Bazı durumlarda bu paketler drop edilir; uzak makinedeki client çoktan ölmüşken sunucuda açık bağlantı kalabilir.
      Bu arada, downvote veren ben değilim.
  • Deno ve Deno Deploy tarafından barındırılan Deno KV ile dağıtık kilit uyguladık
    İçeride dağıtık bir veritabanı olan FoundationDB kullanıyor; yerel cihazlarda çalışan Deno örnekleri aynı Deno KV'ye bağlanıp kilidi alıyor
    PostgreSQL kullanılsa da SELECT FOR UPDATE ile çalışır, ancak veritabanının kendisi dağıtık değildir

  • 2018'de kendi kullanım senaryomuz için Redis'i değerlendirdik ama sonunda daha az gösterişli bir çözümü seçtik ve gerçekten bir kez bile başarısız olmadı
    Kullanım senaryosu, bir kampanyanın sonlu bilet kümesinden kimlikli biletleri tek tek dağıtmaktı; Ticketmaster'ın bir etkinlik mekânındaki koltukları atamasına benziyordu
    Bir istek geldiğinde uygun bir bileti vermemiz, isteğin metadatasını atanan bilete iliştirmemiz ve sonra o bileti sonraki isteklerin hedefinden çıkarmamız gerekiyordu
    Geçmişte fazla tahsis, eksik tahsis, mükerrer tahsis gibi başarısız kampanyalar olduğu için doğruluk kilit önemdeydi
    Redis ile kilit alma, kilidi kontrol etme, işi yapma ve kilidi bırakma şeklinde basit bir uygulamayı da denedik; ama o dönem bizim için operasyonel yükü büyüktü ve o yoldan gitmediğimize sevindim
    Son seçim Postgres oldu. Bizim “dağıtık kilidimiz”, Postgres'e özgü özellikleri kullanan birleşik bir UPDATE ifadesine daha yakındı; isteği bir tür küme işlemine dönüştürüp veritabanının başarılı kayıt ya da başarısızlık göstergesi döndürmesini sağladık. ACID işlemleri kazandı
    Doğruluk sorununu çözdükten sonra ölçek ve performansa baktık; saniyede milyonlarca isteğe ihtiyacımız yoktu ama ani sıçramalar için hedeflerimiz vardı
    Küme içinde okuma-yazma veritabanı örneklerini optimize ettik, daha büyük veya talebi yüksek kampanyaları belirlenmiş sistemlere stratejik olarak yerleştirdik ve 2 yıl boyunca optimizasyonu sürdürdük; buna rağmen bilet dağıtımı başarısız olan tek bir kampanya bile olmadı
    Dağıtık kilit teknolojisi uzmanı değilim; sadece çözülmesi gereken probleme odaklanıp birkaç şey denedikten sonra doğru çözümü bulduk

    • Saniyede en fazla 50 bin kısa atomik işlem için yalnızca Postgres kullanmanın yeterli olduğu doğru
      UPDATE işlemi yalnızca birkaç mikrosaniye sürdüğü için problemi merkezîleştirebilirsiniz; bu da daha basit, hızlı ve güvenlidir
      Ancak yazıda açıklandığı gibi bu dağıtık bir problem değildir
      Dağıtık sistemlerdeki kilitler, çok iş parçacıklı uygulamalardaki mutex'lerden farklıdır; birden çok düğüm ve ağ, her biri farklı şekillerde bağımsız olarak arızalanabildiği için daha karmaşıktır
      İşlemler birkaç saniyeden birkaç saate kadar sürebildiğinde ve ilgili makineler kilidi tutarken arızalanabildiğinde dağıtık kilide ihtiyaç duyulur
    • Bu örneğin gösterdiği önemli nokta, gereken şeyin kilit değil kısıt olduğudur
      Bu durumda kısıt “N adetten fazla bilet satma”dır; bu tür problemlerin gerçekçi trafik ölçeklerinin çoğu, geleneksel ilişkisel veritabanlarının transaction davranışıyla çözülebilir ve iç kilit yönetimi veritabanına bırakılabilir
      Geliştiricilerin “dağıtık kilit yapacağım” diye çok hızlı atlamamasını isterim. Neredeyse her zaman daha iyi bir yanıt vardır, ama o yanıt uygulamadan uygulamaya değişir
    • Sonuçta yanıt, sanılanın aksine çoğu durumda dağıtık kilide ihtiyaç olmadığıydı :)
    • Bu, konser bazında farklı instance'lara sharding yapılabildiği için utandıracak kadar kolay paralelleştirilebilen bir problem gibi görünüyor
      Yeni çıkan Cloudflare SQLite gibi bir şeye uygun bir iş de olabilir
    • En iyi yöntem bu ve pratikte probleme yaklaşmanın tek makul yolu da bu
      Bunu ilk okuduğum yer şurasıydı: https://code.flickr.net/2010/02/08/ticket-servers-distribute...
  • Pek çok mühendis, çok geç olana kadar doğruluk sorunlarını gerçekten ciddiye almıyor. Güvenliğe benziyor
    Ciddiye alsalar bile yaptıkları şeyin doğru olduğundan emin olmadıkları durumlar da çok
    Örneğin benim alanımda mikroservisler, aktörler ve süreçler ağ üzerinden mesajlaşıyor; gördüğüm uygulamaların %95'inden fazlasında mesajların kaybolabileceği veya sırası değişerek işlenebileceği edge case'ler var
    Ancak teşvikler bu sorunu düzeltecek kadar hizalanmış değil. Yöneticilerin ve mühendislerin ödül yapısı, müşteriler ve hissedarlar için en iyi sonuçlarla örtüşmüyor

    • Mikroservislerin kendisi çoğu zaman bu sorunun bir belirtisi
      İnsanlar durduk yere fonksiyon çağrılarının arasına ağ sınırı koymak istiyor; sonra da o fonksiyon çağrısı için durmadan HTTP sunucuları ve istemcileri, JSON serileştirme/ters serileştirme katmanları kuruyor, şansları varsa gRPC kullanıyor ve o ağ sınırının ötesinde dağıtık transaction gibi şeyleri yeniden uygulamaya çalışıyorlar
      Sonunda kaçınılmaz “uzakta gerçekleşen ürkütücü etkileşimleri” ele almak gibi yoğun işlerden başka bir şey kalmıyor
    • Bunu düzeltmek için önce ölçmek ve izlemek, ardından müşteri deneyimini temsil eden hizmet seviyesi hedefleri belirlemek gerekir
      Ürün ve mühendislik ekiplerinin bunun üzerinde anlaşması gerekir; SLO ihlal edildiğinde odak sistem güvenilirliğine kaydırılmalıdır
      Herkesi ikna etmek zor olduğu için iyi liderlik gerekir
      Hatalar ortaya çıkıp yeni özellikler yavaşladığında ya da neredeyse hiç gelmediğinde ve müşteriler ayrılmaya başladığında, kaliteyi sürecin bir parçası hâline getirmek gerektiğine dair gerekçe çok kolaylaşır
      Olgun liderler mümkün olduğunca erken davranıp o aşamanın önüne geçer
    • Bu tür problemler masum bir insanın hapse girmesine veya ölmesine bile yol açabilir
      [0] https://en.wikipedia.org/wiki/British_Post_Office_scandal
    • Bir dereceye kadar teşvik hizalaması olduğunu düşünüyorum. Bu edge case'ler çetrefilli olduğundan geliştiricilerin çok sayıda destek talebiyle uğraşması muhtemel, bu da kimse için iyi değil
      Ama dünkü yöneticileri bunu doğru yapmak için zaman istemeye nasıl ikna edeceğimiz konusunda net bir yol göremiyorum
  • Bu, işi gereğinden fazla karmaşık hâle getiriyor
    Yazıda bahsedilen fencing token gibi bir şey varsa kilide gerek yok
    Token’ın monoton artması da gerekmez; istemci ile depolamanın birlikte sahip olduğu pasif bir benzersiz değer olması yeterli
    Buna sürüm token’ı dersek, monoton artan bir değer de olabilir; genelde üretmesi daha kolay olan UUID de çalışır. Teknik olarak tüm depolama verisinin hash’i de mümkün ama pratik değil
    Akış şöyle. İstemci, depolamadan mevcut sürüm token’ını ve değiştireceği veriyi birlikte alır; depolama da veriyi ve token’ı atomik olarak sorgulayarak o token’ın ilgili veri sürümüne ait olduğunu garanti eder
    Sonra istemci değişikliklerle birlikte sürüm token’ını geri gönderir; depolama yalnızca mevcut token ile gönderilen token eşleştiğinde değişikliği kabul eder ve yeni sürüm token’ını atomik olarak üretir
    Başka nedenlerle kilit devreye alınabilir, ama dağıtık sistemlerde depolama bütünlüğünden bağımsız olmalıdır
    “Kilit” terimini de pek sevmiyorum. Geçici ve garantisiz olduğu için lease ya da rezervasyon anlamı daha iyi aktarabilir

    • Anlattığın şey compare-and-swap (CAS) ve iyi bir çözüm
      Karmaşıklığı veritabanı tarafına iten bir yaklaşım; ama burada konunun dağıtık kilit olduğunu unutmamak gerekir
      Tek bir veritabanı söz konusuysa, veritabanı çöküp hangi CAS yazmasının gerçekten yansıdığını bilemediğin duruma gelene kadar basittir
      Yüksek erişilebilirlik ve çoklu veri merkezi yedekliliği gereken büyük sistemlerde, düğüm arızaları etrafındaki senaryolar yüzünden bu yöntem de bozulabilir ve oldukça karmaşık hâle gelir
      Genellikle Paxos biçiminde bir transaction log kullanılır. Dağıtık sistemlerde kolay çözüm olduğunu varsaymamak gerekir. Her zaman baş ağrıtır
    • Sorunu yanlış yorumlamışsın ve başka bir probleme çözüm öneriyorsun
    • Bu, iyimser kilitleme olarak bilinir. Ama buna dağıtık kilit mekanizması demezdim
    • Bu açıklama, yazıda kilit kullanmanın ilk nedenini kaçırıyor
      Verimlilik açısından kilit almak, aynı işi gereksiz yere iki kez yapmayı önleyebilir. Örneğin pahalı bir hesaplama gibi
      Kilit başarısız olup iki düğüm aynı işi yapsa bile sonuç yalnızca biraz maliyet artışı ya da aynı e-posta bildiriminin tekrarlanmasıysa bu önemsiz olabilir
      Ama birden fazla düğümün aynı işi yapmasının, örnekte yazılandan çok daha kötü olduğunu düşünüyorum. Çünkü ölçeklenebilir dağıtık işlemenin kendisini engelleyebilir
    • Monoton artan token kullanmazsan tutarsız duruma yol açmaz mı?
      Depolama sisteminde iki düğüm olduğunu ve iki okuma-değiştirme-yazma sürecinin çalıştığını varsayalım. Süreç 1 ve 2 ikisi de ilk token olan abcyi alır
      Süreç 1 commit eder, token cdeye değişir ve değişiklik düğüm 2’ye stream edilir; ancak ağ gecikmesi yüzünden düğüm 2’ye geç ulaşır
      Bu sırada süreç 2, abc token’ıyla düğüm 2’ye commit ederse, düğüm 2 henüz düğüm 1’in mesajını almadığı için değişikliği kabul eder ve sistem tutarsız duruma düşer
      Monoton artan fencing token varsa bu yaşanmaz. Çünkü bu gereksinim, token sağlamadan önce düğümlerin toplam işlem sırası üzerinde anlaşmasını zorunlu kılar