Tüm Durable Object’lerde sıfır gecikmeli SQLite depolama
(simonwillison.net)- Cloudflare’ın Durable Object’i anahtar/değer deposundan SQLite tabanlı ilişkisel depolamaya dönüşüyor; uygulama mantığı ile verilerin aynı fiziksel host üzerinde çalıştığı bir yapı haline geliyor
- Ölçekleme, tek bir nesneyi büyütmek yerine belge, kullanıcı, veritabanı shard’ı gibi mantıksal durum birimleri bazında Durable Object’leri ayırma yöntemiyle yapılıyor; tek bir nesne, tek makine ve tek iş parçacığı sınırlarına sahip
- Uçuş rezervasyonu gibi durum biriminin net olduğu sistemlerde her uçuş için özel bir Durable Object ve kendi SQLite veritabanı bulunabilir; bu da havayolu başına günde binlerce yeni veritabanı oluşabileceği anlamına gelir
- Dayanıklılık; WAL girdilerinin stream edilmesi, nesne depolamada saklama ve yakındaki veri merkezlerine replikasyonla sağlanıyor; 16 MB veya 10 saniyede bir batch yapılıyor ve 30 güne kadar belirli bir ana geri yükleme destekleniyor
- JavaScript API, hızlı tek iş parçacıklı kalıcı depolama işlemleri için bloklayıcı yaklaşımı seçti; Durable Object’in konumu oluşturulduktan sonra sabit kalıyor, ancak gelecekte dinamik yeniden konumlandırma planlanıyor
SQLite’a geçen Durable Object tasarımı
- Cloudflare’ın Durable Object ürünü, anahtar/değer deposundan SQLite tabanlı ilişkisel sisteme yükseltildi
- Temel fikir, uygulama mantığını bu mantığın işlediği verilerle aynı konuma yerleştirmek
- Tek bir Durable Object, kullandığı SQLite veritabanıyla aynı fiziksel host üzerinde çalışan koddan oluşur
- Okuma ve yazmaları ağ gidiş-dönüşü olmadan işleme yönünde olduğu için gecikmeyi azaltmak daha kolaydır
- Büyük ölçekli işlem, tek bir nesnenin throughput’unu artırmaktan ziyade daha fazla nesne oluşturma yaklaşımıyla ele alınıyor
- Tek bir nesne, tek bir makinenin tek iş parçacığında çalıştığından throughput açısından yapısal bir sınıra sahiptir
- Belge, kullanıcı, veritabanı shard’ı gibi farklı mantıksal durum birimleri ayrı nesneler tarafından üstlenildiğinde ölçeklemek daha kolaydır
- Bir uçuş rezervasyon sisteminde her uçuş özel bir Durable Object’e ve kendi SQLite veritabanına eşlenebilir
- Bu yapıda havayolu başına günde binlerce yeni veritabanı oluşturulabilir
- Her Durable Object’in benzersiz bir adı vardır ve Cloudflare ağı, istekleri dünya çapındaki ağ içinde ilgili nesnenin bulunduğu konuma yönlendirir
WAL stream’i ve replikasyonla sağlanan dayanıklılık
- Durable Object tabanlı sistem, Litestream’den esinlenerek her nesnenin WAL girdileri dizisini sürekli olarak nesne depolamaya stream eder
- Batch işlemi 16 MB’de bir veya 10 saniyede bir yapılır
- Kaydedilen transaction’lar yeniden oynatılarak 30 güne kadar belirli bir ana geri yükleme mümkündür
- 10 saniyelik pencere içindeki dayanıklılığı tamamlamak için yazmalar, commit’in hemen ardından yakındaki ayrı bir veri merkezindeki 5 replikaya iletilir
- Yazma yanıtının onaylanması için bunlardan 3’ünün doğrulaması gerekir
- Durable Objects’in temel sistemi Storage Relay Service’tir ve Cloudflare’ın ayrı D1 SQLite system ürününü bir yıldan uzun süredir çalıştırmaktadır
API ve nesne konumu davranışı
- JavaScript API async değil, bloklayıcı yaklaşım kullanır
- Tasarım amacı hızlı tek iş parçacıklı kalıcı depolama işlemleri sağlamaktır
- Örnek kod, belge listesini sorguladıktan sonra her belgenin yazar adını ayrı bir sorguyla alan N+1 sorgu desenini bilerek kullanır
- SQLite, bu deseni işlemeye uygun bir örnek olarak bağlanır
- Durable Object şu anda oluşturulduktan sonra konumunu değiştirmez
- Varsayılan olarak ilk
get()isteğinin oluşturulduğu yere yakın bir veri merkezinde instantiate edilir - Başka bir konumda manuel oluşturmak için
get()çağrısına isteğe bağlılocationHintparametresi verilebilir - Mevcut Durable Object’lerin dinamik yeniden konumlandırılması gelecekte planlanıyor
- Varsayılan olarak ilk
- where.durableobjects.live, Cloudflare ağında yeni bir Durable Object’in nerede oluşturulduğunu izleyen bir sitedir
- Örnek ziyarette, sayfa Half Moon Bay’den yüklendiğinde San Jose’deki worker’ın San Jose’de bir Durable Object oluşturduğu gösteriliyor
1 yorum
Hacker News yorumları
Birkaç ilginç nokta daha var: Yazma API’si senkron, ama gizli bir asenkron bekleme içeriyor; bir sonraki yanıt çıktısı sırasında yazma başarısız olursa çalışma zamanı yanıtı HTTP hatasına dönüştürüyor.
Böylece kullanıcı hataları ya da beklemeyi açıkça yönetmek zorunda kalmadan çalışma zamanı yazmaları otomatik olarak toplu işleyebiliyor ve başarıyı iyimser biçimde varsayabiliyor.
Okuma transaction’ları yok; oysa belirli bir andaki snapshot işaretçisini almak için faydalı olabilirdi.
Her çalışma zamanı instance’ı 128MB RAM ile sınırlı.
WebSocket’ler hazırda bekletme durumuna geçebiliyor ve uykuda oldukları süre için ücretlendirilmiyor; böylece DO uyurken bile istemcileri bağlı tutabiliyor.
Diğer DO’ları veya Worker’ları normal JS çağrıları gibi ele alabilen otomatik RPC benzeri bir özellik de var; gerçekte başka bir veri merkezini çağırabiliyor ve serileştirme ile ayrıştırmayı çalışma zamanı üstleniyor.
Yazıda adı geçen Litestream de benzer bir tekniği öneriyor.
Standart SQLite’ta birden fazla süreç aynı veritabanına eşzamanlı erişebildiği için okuma transaction’ları faydalıdır.
Burada veritabanına yalnızca tek bir süreç erişebildiğinden, tüm okumaları tek bir senkron fonksiyon içinde yapmak ya da süreç düzeyinde kilitlemeyi kendiniz uygulamak aynı etkiyi sağlayabilir.
WAL kayıtlarının nesne depolamaya her 16MB’de bir veya her 10 saniyede bir toplu olarak stream edilmesi, yazmaların küresel ölçekte güvenilir biçimde okunabilir hale gelmesinin 10 saniyeye kadar sürebileceği anlamına geliyor gibi görünüyor.
Bunun kıta ölçeğinde milisaniyelik yanıt veren bölgesel veritabanı kümelerinin yerini nasıl alabileceğini pek anlamıyorum.
Stream kullandıklarını biliyorum, ama hedef yalnızca 5 follower ve Cloudflare’ın yüzlerce veri merkezi var.
Tüm SQLite instance’ları her zaman bağlı değilse saniye mertebesinde okumaları fiziksel olarak garanti edemezsiniz; bağlı olsalar bile paket gecikmesi sorun çıkarabilir.
Yazıp hemen okursanız anında yansır. Çünkü yazma ve okuma, mevcut sürecin bellek durumunu da birlikte günceller.
Başka bir süreç, örneğin başka bir DO veya Worker, veriye erişmek isterse veriyi barındıran DO üzerinden geçmek zorunda olduğundan RPC ya da HTTP isteği gönderir ve en güncel bilgiyi alır.
Hazırda bekletme belirli bir süre hareketsizlikten sonra gerçekleştiği için, beklentinin aksine yazmanın unavailable hale gelmesi ancak DO veya Worker’ın yazmadan hemen sonra çökmesi gibi durumlarda olur gibi görünüyor.
Cloudflare blog yazısı bu kısmı daha ayrıntılı ele alıyor. Bu sayede yazmalar hızlı kalırken dayanıklılık da sağlanıyor.
O makine, kendi SQLite veritabanına dair her zaman tutarlı bir görünüme sahiptir.
Milyarlarca nesne oluşturabilirsiniz, ancak her nesnenin ayrı bir veritabanı vardır.
DO’nun çalıştığı makineden farklı bir makinede o veritabanını doğrudan okumanın bir yolu yoktur.
Her DO küresel olarak benzersizdir; belirli bir ID’ye sahip DO bir yerde yalnızca tek bir tane çalışır ve o veri merkezinin yerel depolamasında SQLite çalıştırır.
Durable Objects’ta hâlâ anlamadığım kısım fiziksel konum
İlk oluşturulmasına neden olan API çağrısını barındıran bölgede mi bulunduğunu merak ediyorum
Öyleyse Kuzey Amerika’da oluşturulmuş ama sonradan tüm okuma/yazma trafiğinin Avustralya’dan geldiği ortaya çıkarsa, DO’yu otomatik olarak başka bir konuma taşıyan bir mekanizma var mı, onu da merak ediyorum
locationHintbelirtebilirsiniz. Avustralya için"oc"kullanılır: https://developers.cloudflare.com/durable-objects/reference/...“Mevcut Durable Objects’in dinamik olarak yeniden konumlandırılması gelecek planları arasında” olduğunu da not etmek gerekir
Cloudflare PoP’larının yalnızca yaklaşık %10-11’i Durable Objects barındırıyor
DO oluşturmak için başka bir PoP’a gelen istek, onu barındıran yakın PoP’lardan birine iletiliyor
Hatırladığım kadarıyla Orleans(https://www.microsoft.com/en-us/research/wp-content/uploads/...) aktörleri makineler arasında taşıyabiliyor; bu model DO’ları konumlar arasında taşıma biçimine iyi uyacak gibi görünüyor
Ancak Durable Object’i taşıyan bir API çağrısı yok
Bağlantı olmaması gerekir ve bir sonraki ya da ilk bağlantıda en yakın veri merkezinde yeniden oluşturulur
Bu sırada bellek kaybolur ama depolama kalır
Yakın zamanda hibernation ile ilgili ayrıntılı özellikler geldiği için bu açıklama biraz eskimiş olabilir
Bu tür yeni bulut teknolojilerini anlamakta zorlanan başka biri var mı merak ediyorum
15 yıldan fazla web geliştirme deneyimim var ve Laravel / Postgres / Redis stack’iyle çalıştım; böyle yazıları okuyunca sadece bana göre değil diye düşünüyorum
Aklıma hemen gelen gerçek zamanlı iş birliği uygulamaları Google Docs/Sheets, Notion, Miro, Figma gibi şeyler
Hepsi küresel ölçekte iş birliği uygulamaları ve Laravel stack’inin bu kullanım senaryolarını destekleyip destekleyemeyeceğinden pek emin değilim
Google muhtemelen çoğunu kendi başına gerçekleştirmek zorunda kaldı ve CRDT kullanımına öncülük etmiş olabilir
Ama kalıplar ortaya çıktıkça ve bileşenler SaaS’laştıkça, büyük ölçekli gerçek zamanlı iş birliği artık devasa bir mühendislik problemi olmaktan çıkıyor ve daha ilginç ürünler mümkün hâle geliyor
Durable Object tasarımını gerçekten beğeniyorum. Özellikle içeride nasıl çalıştığını anlamanın kolay olması hoşuma gidiyor
Gerçek zamanlı veri için tasarlanmış pek çok başka çözümün aksine, Durable Objects’te Redis ve İtalyan yemekleri gibi bir sadelik var. Malzemelerin hepsi görünüyor
Yeterli zaman, kaynak ve veri merkezleri varsa, yetkin bir programcının DO belgelerini okuyup benzer bir şeyi yeniden uygulayabileceğini düşünüyorum
Bu yüzden ilgili ödünleşimleri değerlendirmek kolay
Ancak DO’nun hızlı ve düşük overhead’li gerçek zamanlı deneyimler, örneğin beş kişinin bir belgeyi eş zamanlı düzenlemesi gibi özellikler için iyi olsa da, analiz ve genel görünümler çıkarmayı çok zorlaştırabileceğinden endişeliyim
Geçen hafta hangi grubun hangi belgeyi düzenlediği gibi sorular buna örnek; verileri SQLite içine koyarsanız daha da zorlaşabilir
Sayısız küçük SQLite instance’ını bir şekilde sorgulayıp sonuçları birleştirmeniz gerekecek; DO’da bunun için bir şey var mı merak ediyorum
Sonunda sürekli Postgres’e dönmemin nedeni de bu. Hem temel uygulama özelliklerinde hem de genel görünüm ve BI gibi işlerde kullanılabiliyor
Gerçekten ilginç bir tasarım, ama bu tür akıllı sistemler benim için hep tekinsiz vadide duruyor
Gerektiği durumlar tam olarak iki tane: çok yüksek yüklü bir sistemi akıllıca ölçeklemeniz gerektiğinde ya da eğlencesine bir oyuncak proje yaptığınızda
Oyuncak projeyse ne kullanırsanız kullanın sorun değil
Ama prodüksiyon ya da iş amaçlıysa kanıtlanmış bir şeye ihtiyaç var
Buna ihtiyacınız olduğunu bilmiyorsanız ihtiyacınız yoktur; sıkıcı bir Postgres veritabanı ve VM gibi şeyler kullanmanız yeterli
Tersine, ihtiyacınız olduğunu biliyorsanız iş zorlaşıyor. Hâlâ yeni ve yeterince olgun olmadığı için garip edge case’lerle çok karşılaşmanız olası; bunları debug etmek ya da kabullenmek istemezsiniz
Sonuçta böyle bir sistemin kimin için olduğunu bilmiyorum
O kadar niş ki çok sayıda ciddi oyuncunun kullanıp kolayca olgunlaştırması zor; şirketlerin %99,9’u içinse fazla karmaşık ve çok fazla ödün içeriyor
Net hedef kitlesi, parlak bir şey görüp şirket kuran ya da daha kötüsü başkasının şirketini bunun üzerine kurup kısa süre sonra pişman olarak daha sıkıcı bir şeye geçen geliştiriciler gibi görünüyor
Başka bir Figma ya da Google Docs benzeri bir şey yapmak istiyorsanız Durable Objects’in programlama modeli çok kullanışlı
Bu yazı daha ayrıntılı ele alıyor: https://digest.browsertech.com/archive/browsertech-digest-cl...
Bu eski yazı da oldukça ilgili: http://ithare.com/scaling-stateful-objects/
Figma’nın multiplayer yazısını okuyup “ihtiyacım olan şey kabaca bu” diye düşünen biri için Durable Objects iyi uyacak gibi: https://www.figma.com/blog/rust-in-production-at-figma/
Başka yaklaşımlar da var. Eskiden WebRTC üzerinde CRDT denemiştim; gerçekten gelecek teknolojisi gibi hissettirmişti
Ama bulutun bir yerindeki WebSocket ve tek bir sınıf instance’ı temelinden çok daha karmaşık
Veriyle davranışı aynı konuma koymak gerçekten karmaşıklığı nicel olarak azaltır
Gecikme ve bant genişliği endişelerini ortadan kaldırdığı için hem operasyonel hem de geliştirme tarafındaki kaygıları azaltır. Meşhur N+1 probleminin etkisi de ciddi ölçüde azalır
Ağ üzerinden bağlanılan Postgres’in başka nedenlerle daha iyi olduğunu savunabilirsiniz ve gerçekten haklı da olabilirsiniz
Ama SQLite sıkıcı ve öngörülebilir olmakta en önde gelenlerden biridir; bilinen güçlü yanları da nettir
Bu yüzden sunucu tarafında da popülerliği artıyor
Yine de Durable Objects’in önerdiği gibi çok sayıda küçük veritabanı oluşturma yaklaşımını pek sevmiyorum
Bana NoSQL kâbusunu hatırlatıyor ve ilişkisel veritabanlarının önemli invariant’larını bozabilir
SQLite’ın, Cloudflare’in D1 ürünü gibi tekil veritabanı olarak kullanılmasının çok daha iyi olduğunu düşünüyorum
Cloudflare’in iyi müşteri başarı hikâyelerine ihtiyacı var; iyi mühendislerinin de bir başarı hikâyesi elde etme karşılığında bunun nasıl çalıştığını, nasıl yardımcı olacağını ve bug’ların nasıl ele alınacağını sizinle birlikte inceleme ihtimali yüksek
Kanıtlandıktan sonra bu bir hizmet ilişkisine dönüşür, ama başta daha çok ortaklık gibidir
İç araçlar gibi performans tuning’i ya da derin iç gözlem gerektirmeyen çok sayıda servis var
Böyle yerlerde serverless framework’lerin dağıtıma harcanan zamanı ciddi azaltması nedeniyle iyi uyduğunu düşünüyorum
Hızlı olması iyi, ama nadiren temel gereksinimdir
Genellikle temel gereksinim hızlı geliştirilebilmesi ve az bakım istemesidir
Cloudflare’in burada iyi bir geliştirici deneyimi sunma ihtimali var, ancak bu onların ana tanıtım noktası değil; bu tür geliştirmeyi hızlandırdığını iddia eden çok sayıda rakip servis de var
Öte yandan yüksek debug edilebilirlik ve iç gözlemlenebilirlik gerektiğinde bu tür servislerin pek uygun olmadığını düşünüyorum
Hangi metrikleri alabileceğiniz, bazı Durable Objects yavaşsa bunun nedenini anlamaya yetecek bilgi olup olmadığı, düzeltebilip düzeltemeyeceğiniz, logging’in nasıl olduğu ve maliyetin ne kadar olduğu önemli
Bu tür servisler, bir startup’ın ilk günden akıllı bir dağıtık sistem kurup ölçeklenme kaygısını ertelemesi için iyi olabilir
Ama scale-up boyutundaki bir şirketin daha derinine bakabileceği bir şeye geçmek isteyeceğini düşünüyorum; bu geçiş de zor olacaktır
Başkalarının da söylediği gibi kullanım alanı multiplayer; çünkü uygulamanın iyi hissettirmesi için herkesin değişiklikleri mümkün olduğunca hızlı görmesi gerekiyor
Daha geniş bakarsak depolama sektörü uzun zamandır tutarlı, düşük gecikmeli ve çok kullanıcılı kullanıma uygun şeyler üretmeye çalışıyor
Fiziksel olarak da tutarlılık ile gecikme arasında genellikle bir ödünleşim olduğundan bu çok zor
Bu yüzden çeşitli modeller deneniyor ve bu deneylerin önemli bir kısmı SQLite etrafında gerçekleşiyor. Hepsi değil; Yugabyte ya da Cockroach gibi istisnalar da var
DO tasarımına hayran kalmaya devam ediyorum
Bu yaklaşımda bir şeylerin yanlış olduğu yönünde refleksif bir tepki vermek kolay, ama aslında birçok gerçek ürünün örtük olarak böyle yapılandığını düşünüyorum
Transaction tutarlılığı gereken her atomik birimde, çok düşük ölçekte çok sayıda karmaşık işin yapıldığı bir yapı
Geriye dönüp bakınca Framer’da multiplayer desteği projesi için yaptığımız şey de bugün DO’nun yaptığının daha uygulamaya özgü bir versiyonuydu
Düzenlemeler 60 FPS ile çoğaltılıyor ve tüm client’lara doğru sırayla uygulanıyordu; sonunda JSON nesnesi düzenlemeleri için WAL benzeri bir şey de yapmıştık
Böylece proje instance’ı çökse bile yedek hiçbir şey olmamış gibi devralabiliyordu
Dev proje veri nesnesine JSON patch commit edecek zaman olmasa bile, burada açıklandığı gibi N güncellemede bir ya da M saniyede bir işleme şeklindeydi
Aptalca bir soru olabilir ama böyle bir yapılandırmada şema migrasyonlarının nasıl ele alındığını merak ediyorum
Anladığım kadarıyla hedef, kiracı başına veritabanı ya da ondan da daha ince parçalanmış bir yapı gibi görünüyor
Şema migrasyonlarını aklı başında bir şekilde yönetmenin bir yolu var mı, yoksa bu tür veritabanlarının daha kısa ömürlü olduğu ve silinene kadar birden fazla DB/DO sürümünü desteklemenin beklendiği mi varsayılıyor, merak ediyorum
Kafamda kullanıcı başına DO olan bir yer imi servisi yapmak eğlenceli olur gibi geliyor
Ama mevcut bir tabloya yeni bir alan eklemeye çalıştığınız anda, bu değişikliği her bir DO'ya tek tek uygulamak gibi epey zorlu bir sorunla karşılaşıyorsunuz
Belki de bu örnek çok uzun ömürlü veri içerdiği için, bu tasarım daha geçici kullanımlar içindir
Bunu gerçekten denemiş biri varsa nasıl yaptıklarını bilmek isterim
SQLite için Python ile yazdığım bir sürümü var ama Durable Objects üzerinde çalışıp çalışmayacağını bilmiyorum. WASM ve PyOdide ile mümkün olabilir
Ya da JavaScript'e portlamak gerekir
https://github.com/simonw/sqlite-migrate
Son zamanlarda Cloudflare geliştiricilere Worker'dan çok DO'yu her yerde kullanmayı teşvik ediyor gibi görünüyor
Worker'ın WebSocket bağlantıları da yaklaşık 30 saniye sonra timeout oluyor ve önerilen yöntem DO kullanmak
İlk chat demosu da 2020'den beri DO ve WebSocket kullanıyordu: https://github.com/cloudflare/workers-chat-demo
Bu, DO için SQLite'ın, DO'nun başarısız olduğu durumlarda en fazla 10 saniyelik veriyi kaybedebileceği anlamına mı geliyor?
Burada Simon'ın “beyond” değil within demek istediğini düşünüyorum