1 puan yazan GN⁺ 2024-08-11 | 1 yorum | WhatsApp'ta paylaş
  • Screenshotbot, ayrı bir DB olmadan web sunucusu sürecinin RAM durumunu veri deposu olarak kullanıyor ve arıza sonrası durumu snapshot'lar ile transaction log'lar üzerinden geri yüklüyor
  • SQL serileştirmesini, DB round-trip'lerini ve ayrı arka plan iş servislerini azaltarak tek süreçli geliştirme deneyimini sadeleştiriyor; bellek içi indeksler ve thread tabanlı işlemeyi kullanıyor
  • Yüksek erişilebilirlik, Raft mutabakat algoritması ile transaction log'un 3 sunucuya çoğaltılmasıyla sağlanıyor; lider arızasında birkaç saniye içinde yeni lider istekleri devralıyor
  • Gerçek uygulama, Common Lisp, bknr.datastore, açık kaynak bknr.cluster, Baidu Braft ve EFS'yi birleştiriyor; sunucu yeniden başlatmaları genelde 1–2 ayda bir gerçekleşiyor
  • Tanınmış kurumsal müşterilerin CI sistemleri her commit ve Pull Request için API'yi yüzlerce kez çağırsa da, 4 çekirdekli 16 GB makinelerde CPU kullanımı en fazla %20 seviyesinde kalıyor; yükün çoğu görüntü işlemeden geliyor

RAM durumunu veritabanı gibi ele alan yapı

  • Geleneksel web hizmetlerinde genellikle Rails, Django, Node gibi web framework'leri ile MySQL, PostgreSQL, MongoDB gibi veritabanlarının birlikte seçildiği bir yaklaşım yaygındı
  • Son 10 yılda bu yaklaşımı yeniden değerlendirmeyi gerektiren bazı çevresel değişimler oldu
    • Diskler NVMe ile daha da hızlandı
    • EBS/EFS gibi diskler daha sağlam hale geldi
    • RAM ucuzladı; bu yüzden çoğu startup verisi RAM'e sığabilir hale geldi
    • Yüzlerce çekirdekli makineler kiralanabilir oldu
    • 2014'te Raft mutabakat algoritması yayımlandı ve sağlam uygulamaları çoğaldı
  • Bu mimari, web hizmeti ile veritabanı örneğini birleştirerek sürecin bellek durumunu veritabanı gibi kullanıyor
  • SQLite gibi veriyi serileştirip saklamak yerine, RAM içindeki nesne ve alanlar doğrudan uygulama durumu olarak ele alınıyor

Explore: İlk ürün iterasyonlarını sadeleştiren yaklaşım

  • Tüm veri RAM'de olduğunda SQL sorgularına serileştirme gerekmez ve birden fazla frontend sunucusunun tek bir DB'ye bağlandığı yapı da azalır
  • Yük arttığında daha fazla RAM ve CPU'ya sahip daha büyük sunucular kullanılarak ölçeklenilebilir
  • İndeksler, disk gecikmesine göre tasarlanmış B-tree yerine bellek içi hash table gibi yapılarla kurulabilir
    • Screenshotbot'ta functional collections kullanan indeksler ölçeklenebilirlik açısından önemliydi
  • Veri okuma RAM üzerinden yapıldığı için DB round-trip'lerini azaltacak özel yapılar ya da Async-IO gerekmez
  • Arka plan işleri, aynı büyük sürecin içinde çalışan thread'lere dönüşür; eşzamanlılık da çoğunlukla bellek içi mutex ve condition variable ile çözülebilir

Arıza kurtarma: snapshot'lar ve transaction log'lar

  • Süreç çökmesine karşı RAM'in tüm durumu düzenli olarak snapshot şeklinde kaydedilir
  • Son snapshot'tan sonraki değişiklikler, bellek durumunu değiştirmeden önce diske transaction log olarak yazılır
    • foo.setBar(2), önce foo nesnesinin bar alanının 2 olarak değiştirildiğini kaydeder, ardından gerçek alanı değiştirir
    • new Foo(), önce bir Foo nesnesi oluşturulduğu transaction'ını kaydeder, sonra yeni nesneyi döndürür
  • Yeniden başlatmada önce snapshot okunur, ardından transaction log yeniden oynatılarak durum geri yüklenir
  • İndeks değişikliklerini transaction log'a koymak gerekmez
    • Örneğin Foo nesnesinin bar alanında bir indeks varsa, setBar bu indeksi günceller ve bu güncelleme snapshot yükleme ya da transaction replay sırasında da yeniden gerçekleşir

Tek süreci mümkün kılan kod yapısı

  • Tüm istekler aynı süreçte işlendiği için, sunucu belleğinde closure'lar saklanıp sayfa isteği işlemede kullanılabilir
  • Screenshotbot'taki https://screenshotbot.io/n/nnnnnnn biçimindeki URL'ler iç closure'lara eşlenir
  • Closure'lar nesne referanslarını tuttuğundan, her sayfa geçişinde nesne ID'si taşımaya veya nesneleri serileştirmeye daha az ihtiyaç olur
  • Debugging, profiling ve monitoring kapsamı da tek bir hizmete indirgenir
    • MySQL slow query log gibi ayrı veritabanı gözlem noktaları ortadan kalkar
    • Tek bir hizmet çökerse site de çöker, ancak hizmet ve sunucu tekil olduğunda arıza olasılığının da daha düşük olduğu düşünülür
    • Sunucu ölürse AWS birkaç dakika içinde yeni bir sunucu ayağa kaldırabilir
  • Test kodu da veritabanını mock'lamayı gerektirmediği için daha kolay olur

Expand: Raft ile yüksek erişilebilirlik sağlamak

  • Yüksek riskli müşteriler %99.999 erişilebilirlik istediğinde tek sunuculu yapı yeterli olmaz
    • Sunucu arızasında AWS'nin tekrar ayağa kaldırması birkaç dakika sürebilir
    • Sürecin disk snapshot'ını geri yüklemesi de birkaç dakika alabilir
    • Yeniden dağıtım sırasında hizmetin yeniden başlaması nedeniyle sunucu birkaç dakika kapalı kalabilir
  • Raft mutabakat algoritması, sonlu durum makinesi olan web sunucusu/veritabanının transaction log'unu çoğaltır
  • 3 makineye çoğaltıldığında lider arızasında birkaç saniye içinde yeni lider seçilir ve istekleri işlemeye devam eder
  • Geliştiricinin kod yazma biçimini büyük ölçüde değiştirmeden, basit bir hizmet yüksek erişilebilirlikli bir veritabanı gibi çalıştırılabilir
  • Raft tabanlı yapıda sunucuyu kapatmadan rolling deployment da mümkündür

Extract: Sharding ve beklenen darboğazlar

  • Daha fazla büyük müşteri desteklenecek aşamada, büyük şirketlerin veritabanlarında kullandığı gibi sharding uygulanabilir
  • Her shard kendi kümesi olabilir
  • Screenshotbot zaten her kurumsal müşteri için özel küme sağlıyor
  • Meta, MySQL küme çoğaltmasını yönetmek için Raft'a geçti; Screenshotbot da ayrı bir veritabanı olmadan benzer bir yaklaşım kullanıyor
  • Başlıca olası darboğaz commit-thread'in ölçeklenmesi
    • Okuma thread'leri iyi paralelleşir
    • Tek bir commit-thread transaction'ları teker teker uygular
    • Raft birden fazla transaction'ı birlikte diske commit ettiği için disk gecikmesi kritik değildir
    • Asıl endişe, transaction uygulamanın CPU maliyetinin tek çekirdek performansını aşmasıdır
    • Böyle bir durumda commit maliyeti profillenip işin bir kısmı transaction thread'inin dışına taşınabilir ya da sharding değerlendirilebilir

Screenshotbot'un gerçek stack'i

  • Screenshotbot Common Lisp kullanıyor
  • İlk uygulama MySQL kullanıyordu ancak MySQL ile eşzamanlılığı yönetmek zor olduğu için bknr.datastore ile değiştirildi
  • bknr.datastore, Explore aşamasında anlatılan yapıyı Common Lisp için sunan bir kütüphane
  • Bu yapıda web isteklerini tek süreç içindeki thread'ler işlediğinden, güçlü multithreading önem taşıyor
    • Bu gereksinim nedeniyle Ruby ve Python'ın uygun olmadığı düşünülüyor
  • Closure'lar sunucu belleğinde tutulduğu için sunucuyu sık yeniden başlatmak zor
    • Yeniden başlatma closure'ların kaybı anlamına gelir
    • Bunun yerine çalışan süreçte hot reloading ile kod güncellenir
    • Common Lisp, sınıf tanımı değiştiğinde mevcut nesneleri güncellemek için reinitialize-instance gibi standart özellikler sunar

Küme, dosya depolama ve operasyon ölçeği

  • Sunucu yeniden başlatmaları şu anda genelde 1–2 ayda bir oluyor
  • Yeniden başlatma gerektiğinde Raft kümesinde rolling restart uygulanıyor
  • Her kurulumda 3 sunuculu bir küme kullanılıyor ve bu yapı bir sunucunun devre dışı kalmasını tolere ediyor
  • Şu anda Kubernetes kullanılmıyor ve henüz gerekli görülmüyor
  • Raft uygulaması, bknr.datastore üzerine kurulan özel bir kütüphane
    • bknr.cluster açık kaynak olarak yayımlandı
    • İçeride Baidu'nun Braft projesi kullanılıyor
    • Braft, arka planda snapshot almayı yöneterek snapshot oluşturulurken sunucunun istek işlemeye devam etmesini sağlıyor
  • Görüntü dosyaları veya datastore içine konmaması gereken blob'lar, üç sunucunun paylaştığı EFS üzerinde saklanıyor
    • EFS, yüksek erişilebilirlikli bir NFS'tir
    • S3'e kıyasla hata koşullarını ayrıca ele almayı gerektirmediği için onunla çalışmak daha kolay görülüyor
    • Dış sunucularla etkileşmeden doğrudan diske yazdığı için test edilebilirlik de daha yüksek

Mevcut performans ve kullanım kapsamı

  • Screenshotbot birkaç büyük kurumsal müşteriyi ve özellikle çok tanınmış bir müşteriyi destekliyor
  • Bu müşterinin CI ortamında çalışıyor ve her commit ile Pull Request başına API'ye yüzlerce istek geliyor
  • Bu yük altında bile istek işleme için 4 çekirdekli 16 GB bir makine yeterli oluyor
    • Replikasyon sunucuları da benzer makineler kullanıyor ve çoğu zaman boştalar
    • CPU kullanımı en fazla %20 seviyesinde
    • CPU kullanımının büyük kısmı görüntü işlemeden kaynaklanıyor
  • Gereken ölçeğin ötesini varsayarak tasarım yapmamak önemli
  • Common Lisp seçilirse ilgili yapı Screenshotbot OSS üzerinden kullanılabilir

1 yorum

 
GN⁺ 2024-08-11
Hacker News yorumları
  • Bu mimari, HashiCorp’un Nomad, Consul ve Vault’unun inşa edilme biçimine kabaca benziyor (Nomad’ın bakımcılarından biriyim). Kesinlikle alışılmadık bir mimari, ama alışınca geliştirici deneyimi oldukça iyi
    Bellek içi durumu istediğiniz biçimde tutabildiğiniz için, uygulamanıza göre uyarlanmış indeksleme ve sorgu fonksiyonlarını kendiniz oluşturabilirsiniz. Raft FSM’de :memory: SQLite da kullanabilirsiniz; ama bellek içi bir transaction deposu oluşturabilir ya da bulabilirseniz (biz kendi go-memdb’mizi kullanıyoruz), durum okumaları sadece fonksiyon çağrısına dönüşür. Eski okumaları veya yazma sapmasını önlemek de basittir. Yazdığınız her nesnede bir Raft indeksi olduğundan, “follower’a foo nesnesini sorgula ama en az 123 indeksine kadar bekle” gibi API’ler oluşturabilirsiniz. Genelde RDBMS’ye veya harici depolara yüklenen pek çok “sihri” ortadan kaldırır
    Ancak “altyapı” alanı dışındaki yeni bir startup için böyle bir mimari seçerken temkinli olurdum. Çünkü fiilen kendi veritabanınızı yapmış oluyorsunuz. Düğümler arası RPC, disk kalıcılığı, bellek içi transaction durum deposu gibi temel parçaları iyi seçmeniz ya da kendiniz yazmanız gerekir. Yükseltmeler özellikle zordur; yeni kod, eski sürüm düğümlerin anlamadığı entity’leri Raft log’una yazmaya çalışabilir. Daha da kötüsü, işleme biçimi değişip eski düğümlerin bunu yanlış anlamasına yol açabilir. Bedava öğle yemeği yok

    • “Raft FSM’de :memory: SQLite da kullanabilirsiniz” kısmı, rqlite[1]’ın ilk yaklaşık 7 yıl boyunca kullandığı temel tasarımdı. Ancak rqlite disk tabanlı SQLite’a geçti ve WAL modu ile PRAGMA synchronous=OFF[2] kullanıldığında RAM’e yazmak kadar hızlı ya da yeterince yakın olduğu görüldü. Bu sayede :memory: SQLite veritabanının kısıtlarından da kaçınılabildi; bunlardan biri maksimum 2GB boyut sınırıydı. En baştan disk modunu kullanmalıymışım, bunu ancak şimdi anladım
      rqlite, Nomad ile aynı Raft kütüphanesini[3] kullandığı için bunların bir kısmını zaten biliyor olabilirsiniz
      Yükseltme sorunu gerçekten var. Nomad’da sahada bunun sık yaşanıp yaşanmadığını merak ediyorum. rqlite’ın 10 yıllık geliştirme sürecinde yeni bir Raft Entry tipi tanıtmak çok nadir oldu; gerçek kullanıcıların karşılaştığı yalnızca bir kezdi. Yöntemlerden biri, yeni tipi anlayan ama asla yazmayan bir sürümü önce dağıtmak, o sürüm tamamen kurulduktan sonra da yeni tipi gerçekten yazan sürüme yükseltmektir. Ancak bunu pratikte hiç denemedim ve son kullanıcıdan da disiplin gerektirir
      [1] https://www.rqlite.io
      [2] Kulağa tehlikeli gelebilir, ancak mevcut rqlite tasarımında başlangıçta alttaki SQLite veritabanı Raft log’undan tamamen yeniden oluşturulur (Raft log’u her yazmada fsync edilir). Bu yüzden elektrik kesintisi vb. nedeniyle SQLite veritabanı bozulsa bile, SQLite veritabanı rqlite’ın yetkili veri deposu olmadığından bunun pek bir önemi yoktur
      [3] https://github.com/hashicorp/raft
    • Buradaki diğer yanıtlara kıyasla ben de veriyi bellekte tutma fikrine daha açığım. Yazıda Common Lisp ve hot reload kullandıklarını görünce, “O ekipse istediklerini yapabilirler. Ama herkes o ekipte çalışmıyor” diye düşündüm
    • “Yükseltmeler özellikle zordur” sözü kesinlikle doğru. Ama replikasyona ihtiyaç duymayan Explore aşamasındaki startup’lar için geçerli değil; biz de uzun süre böyle yaptık. Bu mimarinin ürün iterasyonu için en faydalı olduğu aşama tam da bu
      Ancak Expand aşamasında replikasyon kullanmaya başlayınca mühendislik görevleri ortaya çıktığı doğru. Yine de bunların hepsi çözülebilir sorunlar. Common Lisp’te kodu hot reload edebilmek, bazı migration’ları çok daha kolaylaştırdığı için de yardımcı oluyor
  • On yıllar önce PG, Viaweb’de veritabanı kullanmadığını; masaüstü uygulamaları böyle değilken web uygulamalarının veritabanlarının ön yüzü gibi hâle gelmesinin tuhaf göründüğünü yazmıştı[0]. HN de veritabanı kullanmıyor
    Ancak modern masaüstü ve mobil uygulamalar yaygın olarak veritabanı, çoğunlukla da SQLite kullanır hâle geldiği için bu artık aynen geçerli bir söz değil. Çünkü ilişkisel veri saklama ve sorgulamanın çok geniş bir uygulama yelpazesinde oldukça yararlı olduğu ortaya çıktı
    [0] https://www.paulgraham.com/vwfaq.html

    • Bağlantıyı okuyunca, “veritabanı”nın herkes için aynı anlama gelmediği anlaşılıyor
      vwfaq’te hâlâ verilerin diskten okunduğundan bahsediliyor ve “HTTP isteğine yanıt verecek süreci başlatmak” gibi bir ifade de var. Burada “veritabanı” ile veriyi kalıcı hâle getiren ayrı bir sunucu ve o veriyi almak için başka bir sunucuyla iletişim kurmayı gerektiren bir mimari kastedilmiş gibi görünüyor
      Bu tanıma göre SQLite elbette veritabanı kapsamı dışında kalır. Ayrıca zaten diskten veri okuyorsanız, ya veritabanı kullanıyorsunuzdur ya da geçici olarak kendi kalıcılık katmanınızı uygulamışsınızdır. Sorun, uygulama başlarken SQLite’tan veri okumayı hâlâ veritabanı kullanmak sayıp saymayacağınız
      Bu düşünme biçiminin sorunu, veritabanının özünün düşük seviyeli ayrıntıları düşünmeden veriyi rahat bir şekilde saklayıp geri almak olduğu gerçeğini göz ardı etmesi. Veriyi veritabanında saklamak, bir yerlerde Postgres instance’ı ayağa kaldırıp veriyi web üzerinden almak anlamına gelmez. Tüm veriyi bellekte tutup, log yapılı veri yapısıyla anlık görüntüleri diske kaydeden bir süreç kullanıyorsanız tebrikler: az önce kendi veritabanınızı yaptınız
    • PG’nin yazısından etkilendiği açık. Biz de Common Lisp kullanıyoruz; bu alanda PG’den kaçınmak zor. Ancak Viaweb’in bknr.datastore gibi transaction log kullandığını sanmıyorum; bu da geliştirme sürecini çok daha pürüzsüz hâle getiriyor
    • PG Viaweb’i yazdığında SQLite’ın kendisi de bugünkü kadar yaygın değildi. Düzeltmek gerekirse, o dönemde SQLite diye bir şey yoktu. SQLite yoksa ve seçenekler fiilen anahtar-değer depolarıyla sınırlıysa, çoğu durumda dosya sistemi de iş görürdü
      İkincisi, son 20 yılda RDBMS sorguları çok daha basitleşti. Tekrarlayan kodu azaltan her türden ORM ve row mapper ortaya çıktı
      Masaüstü ve mobil uygulamalar için yararlı tam metin arama gibi gelişmiş özellikler de geldi. Günümüzde masaüstü uygulamasında RDBMS kullanmak iyi bir tercih
    • HN veritabanı kullanmıyor mu? Biraz daha açıklayabilir misin? Oldukça şaşırtıcı
    • O zamanlar dönem farklıydı. Bildiğim kadarıyla Viaweb birden çok Common Lisp instance’ından oluşuyordu ve kullanıcı oturumlarının tüm durumu tek tek makinelerin belleğindeydi. Bir yerlerde, production’da kullanıcıyla telefonda konuşurken hataları canlı olarak patch’lediklerine dair bir yazı okuduğumu hatırlıyorum
      Web çok daha büyüdü ve bu pratiklerin önemli bir kısmı bugün işe yaramaz. Bir müşteriyle telefondayken bugünkü test yükü altında production makinesine canlı düzeltme göndersem, buradaki birçok kişi akıl sağlığımdan şüphe ederdi
  • İlginç bir şeyi deneme isteğini anlıyorum ama MySQL ya da Postgres’in en temel kısımlarını öğrenmemek için bunu yapmak muazzam bir zaman israfı gibi görünüyor. Sadece onun üzerine inşa edersin, biter; özellikle de public cloud’da çalıştırıyorsan
    Gidiş-dönüş gecikmesinin artması ya da eşzamanlılık sorunları argümanı bana ikna edici gelmiyor. İkincisi için temel tuning veya gürültülü müşterileri ayırmak gibi basit çözümler var. Bloglarındaki başka bir yazıda günde 10 milyon satır ekleme olasılığından ve indeksleme zorluklarından bahsediyorlar; bu kelimenin tam anlamıyla hiçbir şey değil. Bunun 10 katı bile özel çözüm mühendisliği yapmak için gerekçe değil bence
    Gerçekten ihtiyaç duyulana kadar “kötü olan daha iyidir” yaklaşımı doğru; o an geldiğinde de bunu kesin olarak anlarsın. O noktada darboğazı da bilirsin ve en baştan aşırı mühendislik yapmak yerine daha akıllıca yanıt verebilirsin

    • Sunucu tabanlı bir veritabanı motoru söz konusuysa, ağ çağrılarını azaltma argümanı hâlâ kurulabilir. Şüpheli olsa da mümkün
      Ama blog yazarının SQLite’ı seçmeme gerekçesi olarak ihtiyaç duyulmayan özellikler olabileceğini öne sürmesi şaşırtıcı. Bu saçma ve hiçbir şeyi meşrulaştırmıyor
      Yazı, varsayımsal bir soruna kötü bir çözümle başlayıp, bariz çözümü reddetmek için zorlama gerekçeler sıralayan umutsuz bir çaba gibi okunuyor
    • Merak ettiğim şey, tekerleği yeniden icat etmeye zaman harcamasalardı bu iş başarılı olur muydu. Açık şekilde geliştirmek ve blogda yazmak bile ürünü tanıtıyor, teknik yetkinliği gösteriyor. Birbirine bağlayınca gayet çalışan sıkıcı teknolojileri kullansalardı anlatacakları şey azalır, dolayısıyla tanıtım etkisi de azalır mıydı?
      Ben mi yanlış düşünüyorum, yoksa tartışmalı ama gereksiz görünen bu ek çaba ürünün bir parçası ve bu alanda başarılı olmanın yolu mu, merak ediyorum
  • “SQLite gibi verilerin hâlâ serileştirildiği bir şeyden bahsetmiyorum” diye başlayıp sonunda yine serileştirme gerektiren ve kopyalanması gereken kendi transaction log’una varması tuhaf. Veritabanı replikasyonu zaten temelde böyle yapılır.
    Yükün tamamı tek bir sunucuya geliyorsa veritabanını o sunucuda çalıştırın; “veritabanına gidiş gelişleri azaltan özel mimari” gibi şeyleri unutun. Verinin tamamı RAM’e sığıyorsa, isterseniz veritabanı için ramdisk kullanır ve standart araçlarla kalıcı depolamaya replike edersiniz. Gerçekten basit olan bu.

    • Genel olarak “kendi SQLite + Raft replikasyonunu yapmış” diye özetlenebilecek gibi duruyor. Ancak SQLite’ın kanıtlanmış güvenilirliği ya da belleği diske verimli biçimde aktarma yeteneği yok.
      Bu yüzden temelde https://litestream.io/ gibi. Açık bir Raft yapılandırması sayesinde failover daha hızlı olabilir. Litestream kullanıcısı olmadığım için ince farkları bilmiyorum ama kulağa çok benzer geliyor.
      Bu aşırı basitleştirmeden bağımsız olarak fikrin kendisini epey beğendim; yazının da kavramı oldukça iyi ikna ettiğini düşünüyorum. Pek çok sistemde, anormal derecede başarılı olsalar bile işin büyük kısmını ya da tamamını taşıyacak kadar ölçeklenir; performansı da neredeyse her alternatifle kıyaslandığında saçma derecede iyi olur.
    • Ramdisk’e de gerek olmadığını düşünüyorum. Veritabanları zaten her şeyi bellekte cache’ler; yalnızca yazmalar diske ulaşır.
      Veritabanını cold start ettikten sonra epey büyük bir select sorgusunu iki kez çalıştırmanız yeterli.
    • Her startup’ın Explore, Expand, Extract olmak üzere üç aşamadan geçtiğini anlamak önemli. Bir aşamada basit olan şey başka bir aşamada basit değildir.
      Transaction veritabanları Expand ve Extract aşamalarında basittir; ama Explore aşamasında ürüne değil altyapı sorunlarına odaklanmanıza yol açarak ek yük getirir. Explore aşamasında müşteriniz olmadığı için veriniz de yoktur; veri güvenilirliği de önemli değildir.
      bknr.datastore ile her şeyi bellekte tutma yaklaşımı (replikasyon yok) Explore aşamasında basittir. Ancak Expand aşamasına girince veri tutarlılığını garanti etmek için operasyonel yük ortaya çıkar.
      Yine de Expand aşamasına gelindiğinde ürün zaten doğrulanmış ve epey kod yazılmış olur. Bunu transaction veritabanıyla baştan yazmak mantıklı değildir; üstüne Raft replikasyonu eklemek daha kolaydır.
    • Katılıyorum. WAL’ı yeniden yapmak, onunla gelen tüm baş ağrılarını da yeniden yapmak ya da görmezden gelmek demek. Log’dan toparlanmanın epey uzun sürdüğü anlaşılıyor; muhtemelen log checkpointing noktasına bile henüz düzgün gelememişler.
    • Trading sistemleri açıkça her şeyi RAM’de önceden ayrılmış yapılarda tutar. Mesele hangi ödünleşimi seçtiğinize bağlı.
  • Bu yazının mantığı şaşırtıcı. Bu, stateful uygulamaları daha basit ve hızlı yapmanın yolu mu yani?
    Varsayımlar zayıf, iddialar aşırı. Yazar, serileştirmenin zorluğunu abartarak zayıf bir iddiayı daha güçlü gösteriyor.

    • Blog yazısından güçlü bir “ne kadar zeki olduğumuza bakın” havası kalıyor.
      Böyle insanlarla çalışmak genelde zordur. Benim uğraşmak zorunda kalmayacağım şekilde batıracakları bir startup bulmuş olmalarına sevindim.
    • Sonra da transaction’ları log’a yazmak ve başka node’lara replike etmek için serileştirme uyguluyorlar.
  • Yeni bir projeye başladığınızda veri yapısı genelde “özellikleri olan öğeler listesi” olur. Örneğin şu anda bir fitness uygulaması yazıyorum; veri, egzersiz listesi ve her egzersizin başlığı, açıklaması, video URL’si ve başka özellikleri var.
    Genelde bu öğeleri data dizinindeki YAML dosyalarına koyarak başlarım. Aslında bu, orijinal YAML’ın tuhaflıkları çıkarılmış özel bir YAML lehçesi. Her değer string’dir; sihirli tip dönüşümleri yoktur. Yeni öğe oluşturmak sadece vim crunches.yaml ile veriyi girmek demektir; düzenlemek ve silmek de bu veri yapısında çok kolaydır.
    Proje büyüyünce genellikle bir DB şeması oluşturup öğeleri MariaDB ya da SQLite’a taşırım.
    Bu kez öğeleri (egzersizleri) SQLite DB’deki JSON kolonlarına taşımayı düşünüyorum. Bir öğenin tüm özelliklerini tek bir JSON alanında saklayıp, JSON alanlarını YAML gibi düzenlemeyi sağlayan küçük bir DB gezgini yazmak istiyorum. İnsan tarafından okunabilir veriyi düzenleme kolaylığını korumaya çalışıyorum.
    DB gezgini yazmak oldukça doğrusal görünüyor. Biraz ncurses ile tablolar arasında gezinmek, birini seçmek, satırlarda gezinmek, satır ekleyip silmek. Bir alanı düzenlerken Vim’i açacağım. Alan JSON ise Vim’e göndermeden önce YAML’a çevirecek, kullanıcı Vim’den çıktığında tekrar JSON’a döndüreceğim.

  • Yazının başında anlatılan şey temelde NUMA makinelerinin çalışma biçimine benziyordu (ör. SGI Altix veya UV). Ayrıca öne sürdükleri avantajlar düşük gecikme ve büyük RAM üzerinde multithreading ile işi paralelleştirebilmekti. Clustering, 1 milyon doları aşan makinelere düşük maliyetli bir alternatif olarak ortaya çıkmıştı. Uygulamanın sadece belleğe yazdığı ve bunun şeffaf biçimde diske map edildiği AS/400’ün kalıcılığıyla da benzerlikler var.
    Şimdi ucuz donanımla, kümelenmiş NUMA makinelerinin avantajlarına zamanda geri dönmüş oluyoruz. Bu süreçte iyileştirmeler de olmuş; yazı da keyifliydi.
    Geçmişteki başka bir teknik, ilgili sorunları ortadan kaldırmak için cluster içinde TCP/IP stack’ini kaldırmaktı. Active Messages gibi çözümler donanımın üzerinde ince bir katmandı. Güçlü tutarlılığın yerleşik olduğu ağ router tasarımları da var. Yapabilecekleri epey şey var.
    Büyüdüğünde donanım fırsatları da var. CPU tarafında SGI iki şey yaptı. NUMA makineleri, tek bir sistemdeki CPU ve RAM sayısını ölçekledi; FPGA’leri doğrudan bellek veriyoluna takıp özel hızlandırıcılar olarak kullanmayı sağladı. Son olarak bazı bilgisayar bilimi makaleleri, multithreading darboğazlarını ortadan kaldırmak ya da azaltmak için işlemci komut setini, çip içi ağı vb. değiştirdi. OpenPiton gibi çipler, açık ve özelleştirilebilir çekirdeklerle çekirdek sayısını artırıyor (ör. 32 adet).

  • “Veriyi SQL sorgularına serileştirmek zorunda olmasaydınız ne kadar harika şeyler yapabileceğinizi hayal edin” ifadesi, yeterince olgun actor model[0] uygulamalarında, örneğin Akka Event Sourcing[1] içinde zaten var. Şu sorunu da ele alıyor:
    “Ama önemli kısım, süreç çöktüğünde nasıl kurtarılacağıdır. Cevap kolay: periyodik olarak tüm RAM’in anlık görüntüsünü almak.”
    “Web geliştirme için yeni bir mimari” oluşturmaya gerek kalmadan, özünde çözülmüş durumda. Actor’larla RAFT protokolünü araştıran açık kaynak denemeler de burada[2] ve burada[3] var.
    0 - https://en.wikipedia.org/wiki/History_of_the_Actor_model
    1 - https://doc.akka.io/docs/akka/current/typed/persistence.html
    2 - https://github.com/Michael-Dratch/RAFT_Implementation
    3 - https://github.com/invkrh/akka-raft

  • İlk aklıma gelen “Ah, Common Lisp ile yazarken benim yaptığım yöntem buydu; birinin bu tekniği yeniden keşfetmesi ilginç” oldu.
    Ama öyle değilmiş; sadece daha fazla Lisp insanı varmış.

  • “RAM çok ucuz” geliştiricilerin en büyük yanılgılarından biri olabilir.
    SSD’ler throughput veya IOPS açısından 100~10000 kat iyileşti, vCPU’nun dolar başına performansı da 20~50 kat arttı. 45/32nm’den artık 5nm/3nm’ye geldik ve saat döngüsü başına komut işleme de çok daha yüksek.
    Ama RAM fiyatı CPU veya SSD kadar neredeyse hiç düşmedi. Çok daha hızlı olmuş olabilir; daha yüksek yoğunluklu çiplerle daha fazla bellek takılabiliyor ve kanal sayısı dual’dan 8 ya da 12’ye çıkmış olabilir. Ama 2008~2022 DRAM spot fiyatlarına bakıldığında en düşük DRAM fiyatı üç kez yaklaşık aynı seviyede, 2,8 dolar/GB civarında oldu. Aynı dönemde döngü içinde 6~8 dolar/GB seviyelerine kadar inip çıktı. Yani son yaklaşık 15 yılda DRAM’i dipte ya da tepede aldıysanız, enflasyonu yok sayınca fiyat kabaca ±%10~20 aralığında benzer kalmış olurdu.
    Ancak 2022 ortasında 2,8 dolar/GB bariyeri kırılıp 1 dolar/GB civarına kadar çöktü, sonra DDR5 için yaklaşık 2 dolar/GB seviyesine yerleşti.
    Artık tek bir sunucuya 4 TB RAM koyabilirsiniz. Bu, DRAM’in aşırı ucuz olduğu anlamına gelmez. Ortalama geliştirici ya da büyük teknoloji şirketlerindeki geliştiriciler 2010’a göre çok daha fazla kazanıyor; bu yüzden RAM çok daha karşılanabilir gelmeye başladı. Gerçekte ise son 15 yılın dip seviyelerinde bile DRAM fiyatındaki düşüş en fazla 2 katın biraz üzerindeydi. Üstelik 1~2 yıl içinde DRAM fiyatlarının yeniden fırlama olasılığı yüksek.

    • Başka bir yorum da mümkün. Tekil düğümlerin maksimum RAM kapasitesi son birkaç on yılda keskin biçimde arttı.
      Basit bir örnek olarak, 20 yıl önce belirli bir düğüm 16 GB RAM ile sınırlı olsaydı, sistemde 4 TB RAM elde etmek için 256 düğüm gerekirdi (her bir OS overhead’i hariç).
      Bugün tek bir düğüm o 4 TB’ın tamamını tek bir kasaya sığdırabiliyor.
      RAM çiplerinin toplam maliyeti değişmemiş olabilir, ancak fiziksel bir sistemde o RAM’i fiilen kullanmanın maliyeti dramatik biçimde düştü.
    • Aynı zamanda pek çok geliştirici, sadece daha fazla RAM satın almanın yeterli olduğu durumlarda bile çok erken dağıtık sistemlere yöneliyor. Yazarın kastettiği de muhtemelen bu olabilir.
    • Veriler için teşekkürler. Diğer yanıta katılıyorum. “Ucuz” demek yerine yüzlerce GB RAM’li makineleri bulmanın kolaylaştığını söylemeliydim gibi görünüyor.