- OpenRun, dahili araçlara yönelik bir web uygulaması dağıtım platformu olarak statik dosyaları, uygulama kodunu ve yapılandırma dosyalarını dosya sistemi yerine SQLite içinde saklayarak dağıtım durumunu veritabanı merkezli yönetiyor
- Birlikte değişen birden çok dosyayı içeren uygulama güncellemelerini tek bir transaction olarak işlemek, sürüm geçişi sırasında bozuk web sayfalarının sunulmasını önlemeyi amaçlıyor
- Sıkıştırma öncesi SHA256 hash değerini birincil anahtar olarak kullanarak uygulama sürümleri arasında ve staging, preview, production uygulamaları arasında yinelenen dosya depolamasını azaltıyor
- SQLite tabanlı depolama; rollback, yedekleme, ETag için hash saklama ve Brotli sıkıştırmalı depolamayı basitleştiriyor; gerekirse GZip ve sıkıştırılmamış veriler de sütun eklenerek birlikte yönetilebiliyor
- Şu anda tek node üzerinde çalışıyor; çok node desteği geldiğinde gecikmeyi azaltmak için paylaşımlı Postgres ile yerel SQLite dosya önbelleğini birlikte kullanmayı planlıyor
OpenRun'ın dosya depolama yöntemi
- OpenRun, code-first dahili araçlar için açık kaynaklı bir dağıtım platformudur; web uygulamalarını tek bir node'a veya Kubernetes kümesine GitOps yöntemiyle dağıtır
- OpenRun, tipik web sunucuları gibi statik içeriği dosya sisteminde tutmak yerine statik dosyalar, uygulama kodu ve yapılandırma dosyaları gibi uygulama verilerini SQLite içinde saklar
- Uygulama metadatası dinamik olarak üretildiği için veritabanında saklama doğal bir tercihtir; dosyaları da aynı depolama katmanında yönetmek dağıtım durumunu birlikte takip etmeyi kolaylaştırır
- Uygulama oluşturma ve güncelleme sırasında dosyalar GitHub'dan veya yerel diskten SQLite veritabanına yüklenir
- Yerel dosya sistemi yalnızca geliştirme modunda kullanılır
SQLite'ın seçilme nedeni
- En büyük avantaj transaction tabanlı güncellemelerdir
- Birden çok dosya değişikliği tek bir transaction içinde gruplanıp işlenebilir
- İzolasyon sayesinde güncelleme sırasında bozuk bir web uygulaması sunulmaz
- Dağıtım hatası olursa veritabanı transaction'ı düzeyinde rollback yapılabilir
- Birden çok uygulama aynı anda güncelleniyor olsa bile hepsi tek seferde geri alınabilir
- Bu, dosya sisteminde değişen dosyaları bulup temizleme yönteminden daha basittir
- OpenRun tüm güncellemeleri otomatik olarak sürümlendirir ve dosya verileri aşağıdaki şemaya sahip tabloda saklanır
CREATE TABLE files (sha text, compression_type text, content blob, create_time datetime, PRIMARY KEY(sha));
- Sıkıştırma öncesi içeriğin SHA256 hash değeri birincil anahtar olarak kullanıldığı için aynı dosya içeriği birden çok sürümde yalnızca bir kez saklanır
- Her production uygulamasının bir staging app'i olabilir ve birden çok preview apps'e sahip olabilir; bu da dosya tekrarına yol açabilir
- SQLite tabanlı depolama, aynı içeriğe sahip dosyaların uygulamalar arasında da yinelenerek saklanmamasını sağlar
Yedekleme, önbellekleme ve sıkıştırma işleme
- Tüm sistem durumu, metadata ve dosyalar SQLite yedekleme aracı Litestream gibi araçlarla yedeklenebilir
- Tarayıcı önbelleklemesi için ETag başlığında gereken içerik SHA'sı dosya yükleme sırasında bir kez saklanırsa daha sonra yeniden hesaplanması gerekmez
- Dosya içeriği SQLite tablosunda Brotli sıkıştırmalı biçimde saklanır
- Veritabanı yaklaşımında
files tablosuna sütun eklenerek GZip sıkıştırmalı veri veya sıkıştırılmamış veri de saklanabilir
Performans ve çok node planı
- OpenRun'da SQLite veritabanı yaklaşımı iyi performans sağlar
- Dosya sistemi tabanlı eşdeğer bir uygulama olmadığı için doğrudan karşılaştırmalı benchmark yapılmamıştır
- SQLite ekibinin benchmark sonuçlarına göre bazı workload'larda SQLite, dosya sistemini doğrudan kullanmaktan daha iyi performans gösterebilir
- OpenRun şu anda tek node üzerinde çalışır
- Gelecekte çok node desteği eklendiğinde, metadata ve dosya verilerinin saklanması için yerel SQLite yerine paylaşımlı Postgres veritabanı kullanmayı planlıyor
- Bu yaklaşım gecikme sorunlarına yol açabilir
- Postgres erişim gecikmesinden kaçınmak için yerel SQLite veritabanını dosya önbelleği olarak kullanmayı planlıyor
Dosya sistemi yaklaşımı neden daha yaygın?
- Çoğu web sunucusunun dosya sistemi kullanmasının nedenlerinden biri kolaylıktır
- rsync, tar gibi mevcut dosya sistemi araçlarıyla dosyalar kopyalanabilir ve güncellenebilir
- Bir diğer neden tarihsel arka plandır
- İyi in-process ilişkisel veritabanları ortaya çıkmadan önce dosya sistemleri kullanılmaya başlanmıştı
- Veritabanını dosya deposu olarak kullanmak için dosya yüklemeye yönelik bir API arayüzü gerekir ve bu her zaman uygulanabilir bir yöntem değildir
1 yorum
Hacker News yorumları
Birkaç yıl önce bu fikri denemiştim; kısmen “35% Faster Than The Filesystem” yazısından esinlenmiştim: https://www.sqlite.org/fasterthanfs.html
O zamanki notlarım burada: https://simonwillison.net/2020/Jul/30/fun-binary-data-and-sq...
Datasette’te SQLite’tan statik dosya sunan bir eklenti olarak https://datasette.io/plugins/datasette-media’yı yaptım ve iyi çalışıyor; ama açıkçası yaptıktan sonra pek kullanmadım
İlgili bir kavram olarak SQLite’tan harita tile’ları sunma yöntemi de var; https://datasette.io/plugins/datasette-tiles bu işi yapıyor. MBTiles biçiminin meğer içi PNG dolu bir SQLite veritabanı olduğu ortaya çıktı
Dosya sunumu için SQLite’ı denemek istiyorsanız, veritabanını ilk kurarken “sqlite-utils insert-files” CLI aracı işe yarayabilir: https://sqlite-utils.datasette.io/en/stable/cli.html#inserti...
İçerik hash’i dosya yüklenirken yalnızca bir kez oluşturulur; web sunucusu her yeniden başlatıldığında üretmeye ya da gerçek dosya adlarını değiştiren bir build adımı koymaya gerek yoktur. Dosya sistemi dosyalarına da dinamik olarak uygulanabilir (https://github.com/benbjohnson/hashfs içindeki embedFS implementasyonuna bakın), ancak veritabanı bunu biraz daha kolaylaştırıyor
requests-cache, yanlış hatırlamıyorsam istekleri SQLite’ta
(date, URI)ile cache’liyor: https://github.com/requests-cache/requests-cache/blob/main/r...pyfilesystem SQLite araması: https://www.google.com/search?q=pyfilesystem+sqlite
sendfile mmap SQLite araması: https://www.google.com/search?q=sendfile+mmap+sqlite
https://github.com/adamobeng/wddbfs “sqlite veritabanı içeriğini okuyabilen bir webdavfs sağlayıcısı”dır
Unix dosya izinlerini ve xattrs genişletilmiş dosya öznitelik izinlerini SQLite üzerine koyarak bir dosya sistemi uygulamanın iyi bir yolu da olabilir
SQLite, örneğin ngx_http_memcached_module.c’den daha hızlı ya da daha kullanışlı olur mu? SQLite’ta hücre düzeyinde ACL de var mı merak ediyorum
Statik dosya okurken her istekte dosyayı açmak, okumak ve kapatmak gerekir; dosya sistemi katmanı dosya içeriğini cache’lemiş olsa bile bu daha fazla context switch demektir. Bunu hızlandırmak istiyorsanız her şeyi veritabanına çevirmek yerine bir caching frontend eklemek daha uygun olur. SQLite’tan daha hızlıdır; bakım ve sorun giderme de daha kolaydır
Tamamen kullanıcı alanında çalışan dosya sistemleri de buna dahil. FUSE, çağrılar çekirdekten geçtiği için hariç
“İşlemsel güncellemeler”in ana fayda olduğu söyleminin sınırları var. Sunucu ister SQLite ister dosya sistemi kullansın, bu tek başına güncelleme sırasında bozulan bir web uygulamasını engelleyemez.
Tarayıcıdaki her sayfa, ayrı HTTP istekleriyle alınan kaynaklardan oluşan bir ağaç olduğundan, sunucu tarafındaki işlem/atomik güncelleme sisteminin hedefi değildir. Sunucuda tüm kaynakları bir işlem içinde değiştirseniz bile tarayıcı eski ve yeni kaynakların karıştığı bir kombinasyon görebilir.
Yaygın çözüm, sayfanın tüm alt kaynaklarını (JavaScript bundle’ları, stil dosyaları, medya vb.) içerik hash’i ya da sürüm içeren adlarla (URL’lerle) tutmaktır. Kök HTML belgesi X sürümünü yüklüyorsa tüm alt kaynaklar da buna karşılık gelen X sürümünü yüklemelidir.
Ayrıca X’ten Y’ye güncellerken, Y sayfasını sunmaya başladıktan sonra bir süre X alt kaynaklarını sunmaya devam etmek gerekir. Hâlâ X sayfasını yüklemekte olan makul ölçüde hiçbir tarayıcı kalmadığından emin olana kadar bunları tutmazsanız X sayfası bozulabilir.
Bu yüzden kök HTML ile alt kaynakları atomik olarak değiştirilen tek bir bundle’a koymak istemek aslında ters teper. Çünkü hâlâ referans verilebilecek eski alt kaynakları kaldırmış olursunuz.
Duruma göre medya dosyaları gibi bazı alt kaynakları HTML belgesinden ayrı sürümlemek de isteyebilirsiniz. JavaScript parçaları ya da stil dosyaları gibi uygulama yapısı öğelerinin önbelleğini tamamen geçersiz kılmadan güncellemek istiyorsanız sayfa derleme sisteminin de bunu dikkate alması gerekebilir.
Büyük bir şirkette bunu denediklerinde (o dönemde web’in hatırı sayılır bir bölümünü görmüşlerdi), kullanıcıların çoğu (%80’den fazlası) web uygulamasında yaklaşık 2–3 gün kalıyordu. Hafta sonu boyunca sekmeleri açık bırakan insanlar olduğu için muhtemelen yanlıydı.
%95 noktası yaklaşık 2 haftaydı, %100 ise yaklaşık 600 gündü. Yani sekmeyi neredeyse 2 yıl açık tutan bir kullanıcı vardı.
%100’ü hedeflerseniz epey uzun beklemeniz gerekir. Bu sayıların tamamı hafızaya dayanıyor ve artık o şirkette çalışmıyorum.
Kullanıcının bir sayfada uzun süre kalıp bozuk bir bağlantı alması senaryosu daha çok SPA tarafının problemine benzer.
Genel olarak katılıyorum, ancak işlemsel güncellemelerin engellediği şey, güncellemeyle ilgili sorunların yalnızca bir kategorisidir. Uygulama düzeyindeki başka sorunlar da bozuk bir deneyime yol açabilir.
İçerik hash’iyle referans verilen statik içeriğin eski sürümlerini sunmaya devam etmek mümkündür, ancak Clace’te şu anda uygulanmış değil.
Temel püf noktası, henüz var olmayan dosyalara referans verilmemesi için HTML olmayan değişiklikleri HTML değişikliklerinden önce yüklemektir. Uygulamayı olabildiğince karmaşık hâle getirmek istiyorsanız yüklemeye derinlik öncelikli arama uygulayabilirsiniz. Ama ruh sağlığınızı önemsiyorsanız problemi yumuşatıp uygulamada önce varlıkları yüklemeyi seçmek daha iyidir.
2011/2012’de küçük bir oyun geliştirme şirketinde çalışırken, önerimle 100 KB altındaki varlıkların tamamını sqlite3 DB’ye taşıdık ve “pak dosyaları” oluşturup bu dosyaların ofsetlerini sqlite3 DB içinde sakladık.
Bu tercih, Richard Hipp’in bir postmortem sunumunda geriye dönüp baktığında BLOB’ları inode gibi ele alıp veritabanının daha ilerideki ofsetlerine koymanın ve BLOB’ları dosyaya eklemenin daha iyi olacağını söylemesinden etkilenmişti.
Varlık yükleme inanılmaz hızlıydı. Mobil oyun olduğu için DB’de olmayan varlık sayısı çok azdı. Sonrasında insanların bu yaklaşımı daha fazla benimsediğini görmek de ilginç.
Kolay gözden kaçan bir başka avantaj da içeriğin yanına neredeyse sınırsız metadata ekleyebilmeniz ve “benzer” dosyaları veritabanı sorgularıyla bulabilmenizdir.
DB’ye çok fazla metadata koymuştuk; sanırım son pak dosyası 200 MB, veritabanı ise yaklaşık 20 MB’tı. Tekrar söyleyeyim, bu bir mobil oyundu.
İstemci tarafında en kötü şey, sunucu tarafındaki karmaşıklık yüzünden azaltamadığımız tek bir çift iç join’di. Sunucu implementasyonunu bizim yapamamamız sinir bozucuydu; birlikte çalıştığımız taraf yazılım geliştirmede çok kötüydü ve backend spesifikasyonunun tamamını haber vermeden değiştirip build’in aniden bozulmasına neden oluyordu.
Oyun tekrarları için de ayrı bir sqlite3 veritabanı kullandık; maç bittikten sonra tüm oyunu yeniden oynatıp her rakibin ne yaptığını görebiliyordunuz. Otomasyon testleri için de çok iyiydi.
lix değişiklik kontrol sisteminde de dosya sistemi ve git ile uğraşmak yerine dosyaları SQLite’a koyma yoluna gittik. Bu yazı yaşadığımız sorunu ele alıyor: https://opral.substack.com/i/150054233/breaking-git-compatib...
Dosya kilitleme, eşzamanlılık gibi sorunları SQLite çözüyor.
SQLite kullanınca platforma özgü dosya sistemi API’leri yerine dosyaları SQL ile sorgulayabiliyorsunuz.
SQL sorguları Kysely https://kysely.dev/ ile ORM olmadan da tip güvenli yazılabiliyor.
Ancak SQLite veritabanının vacuum yapılmadıkça küçülmediğine dikkat etmek gerekir. Temelde verileri ayrı bir dosyaya kopyalayıp özgün dosyayı silen bir işlemdir.
Uygulama içinde mantıklı bir zamanda elle yapılması gereken bir iş olduğu için, ikili verileri yazıp silen bir kullanımda disk kullanımına dikkat etmek gerekir.
İlginçtir, benim yaptığım statik site oluşturucu CMS buradaki yöntemin tam tersi çalışıyor.
Web sitesini geliştirirken/güncellerken tüm sayfalar ve yazılar SQLite veritabanındaki girdiler; düzenlenebilir web sitesi sürümünü gösteren bir web arayüzüyle yönetiliyor.
Ardından web sitesini statik sayfalar olarak dosya sistemine döküp doğrudan dağıtıyor ya da zip olarak indirip tamamen statik hosting hizmetleri dâhil başka bir yere yükleyerek dağıtıyorsunuz.
SQLite’in “Appropriate Uses For SQLite” https://www.sqlite.org/whentouse.html sayfasına göre, SQLite’ın kaldırabileceği web trafiği, sitenin veritabanını ne kadar yoğun kullandığına bağlıdır.
Genel olarak günde 100K hit altındaki siteler SQLite ile sorunsuz çalışmalıdır. 100K/gün muhafazakâr bir tahmindir, katı bir üst sınır değildir. SQLite’ın bunun 10 katı trafiği de işlediği örnekler vardır.
SQLite web sitesi (https://www.sqlite.org/) de doğal olarak SQLite kullanır; 2015 itibarıyla günde yaklaşık 400K~500K HTTP isteğini işliyordu ve bunların %15~20’si veritabanına dokunan dinamik sayfalardı. Dinamik içerik, web sayfası başına yaklaşık 200 SQL ifadesi kullanır.
Bu yapı, fiziksel sunucuyu 23 başka VM ile paylaşan tek bir VM üzerinde çalışmasına rağmen çoğu zaman yük ortalamasını 0,1’in altında tutar. Kaynak: https://news.ycombinator.com/item?id=33975635
Statik dosya sunumu gibi okuma ağırlıklı iş yüklerinde SQLite çok daha fazlasını kaldırabilir. İçerik önbellekleme başlıkları ayarlıysa tarayıcı içeriği önbelleğe alır; böylece sunucu isteği yalnızca yeni istemciler için gerekir.
Çoğu kullanım senaryosunda SQLite’ın darboğaz olacağını sanmıyorum.
Yalnızca 2017 tarihli “35% Faster Than The Filesystem” sayfasına dayanarak SQLite ile statik içerik sunma fikri, en iyi ihtimalle olgunlaşmamış görünüyor.
Nginx gibi modern web sunucuları statik dosya işleme için en uygun stratejileri kullanır. sendfile’dan başlayıp io_uring ve splice işlemlerine kadar uzanır; epoll, kqueue, eventport arasından ihtiyaca uygun temel üzerinde iyi tasarlanmış bir thread pool içinde çalışır.
Buna karşılık SQLite’ın varsayılan olarak sunabileceği en iyi şey, bellek eşlemeli I/O desteği (https://www.sqlite.org/mmap.html) civarındadır.
Bu yaklaşım, yerel olarak barındırılan web uygulaması gibi tek istemcili servisler için iyi uyabilir (https://github.com/electron/asar da bkz.). Ancak büyük web sitelerinde, diğer yorumlarda dendiği gibi, var olmayan bir sorunu çözmeye çalışmak olur.
Yüksek performanslı bilimsel hesaplama çok yapıyorum; özellikle verilere paralel erişirken en esnek ve hızlı yöntemin çoğu zaman ramdisk üzerindeki salt okunur SQLite veritabanı olduğu oldu.
Oldukça hacky hissettiriyor ama şimdiye kadar bulduğum tüm yöntemlerden daha kolay kuruluyor ve daha hızlı.
Astronomi alanından bir arkadaşımın, bilim dünyasındaki birçok kişinin veritabanlarına alışması gerektiğini söylediğini görmüştüm. Aksi halde, farkına bile varmadan berbat bir kendi veritabanlarını yapmak için muazzam çaba harcıyorlar.
Bu yaklaşımın neden daha yaygın olmadığına gelince: dosya sistemleri dosya işlemede iyi olduğu için.
Atomik güncelleme gerekiyorsa yeni bir dizine checkout yapıp sembolik bağlantıyı değiştirmek yeterli.
Veritabanını dosya sistemi gibi kullanan çeşitli sürümler gördüm; iyi yanları var ama işler ters gidince kâbus gibi yanları da var.
Böylece btrfs gibi bir şey kullanıp dosya sistemi katmanında tekilleştirme de yapabilirsiniz.
Uygulama güncellemesi sırasında çok sayıda dosya değişebileceği için veritabanı kullanınca tüm değişiklikleri transaction ile atomik olarak işleyebilir ve sürüm değişirken bozuk web sayfası sunulmasını engeller iddiasında sorun var.
Bunun nedeni, SQLite dosyasının serileştirilebilir izolasyon sağlamak için yazma sırasında okumalara karşı kilitlenmesidir. O zaman veritabanı işlemlerini çevrimdışı dosya üzerinde yapmak, ardından prodüksiyondaki mevcut dosyayla yeni dosyayı değiştirmek daha mantıklı olur.
Bu da sonuçta tar dosyası kullanmak ya da yeni içerikle değiştirilecek ayrı bir dizin kullanmak anlamına daha yakın.
Statik dosyaları statik olarak sunmak çok daha kolaydır. Canlı SQLite bağlantılarını yöneten ve tuhaf bir “güncelleme eşzamanlılığı” sihri başarmaya çalışan bir programdan sunmaya gerek yok. Bu problem hiç zorlanmadan çözülebilir.
CMS’i SQLite veritabanı olarak yönetmek sorun değil; ama içerik statikse ve canlı olarak sunuluyorsa statik dosyalar kullanmak daha iyidir.