2 puan yazan GN⁺ 2023-09-19 | 1 yorum | WhatsApp'ta paylaş
  • OpenDocument Presentation (ODP), ZIP arşivi yerine SQLite konteyneri içine konursa belge kaydetme, başlatma ve kurtarma yöntemleri daha güvenli ve hızlı tasarlanabilir
  • Günümüzde ODP, XML ve görsel dosyalarını ZIP arşivi içinde paketleyen bir yapıya sahiptir; 49 slaytlık örnek sunum dosyası content.xml, styles.xml, meta.xml, settings.xml ve görseller dahil toplam 78 öğeden oluşur
  • ZIP tabanlı yapıda küçük bir değişiklikte bile tüm arşivi baştan yazmak kolay olduğundan artımlı güncelleme zordur; bu da File/Save gecikmesine ve SSD yazma miktarının artmasına yol açar
  • SQLite’a geçilirse dosyalar tablo satırları olarak saklanabilir; dahası slayt bazında içerik ve sürüm ayrılarak yalnızca ilk slaydı okuma veya yalnızca değişen slaytları kaydetme yöntemi mümkün olur
  • Bu, OpenDocument’ın kendisini eleştiren veya değiştirmeyi öneren bir metin değil; SQLite’ın uygulama dosya formatlarında atomik kaydetme, erişilebilirlik, sürüm yönetimi ve kurtarma özelliklerini kolaylaştırabildiğini gösteren bir örnektir

Düşünce deneyinin kapsamı ve konusu

  • Konu, OpenDocument içinde sunum belgesi formatı olan ODP (OpenDocument Presentation)’dir
  • Amaç OpenDocument’ı fiilen değiştirmek değil, gelecekteki dosya formatı tasarımlarında SQLite’ı konteyner olarak kullanma yaklaşımını değerlendirmektir
  • Beklenen faydalar daha küçük belgeler, daha hızlı File/Save, daha hızlı başlatma, daha az bellek kullanımı, belge sürüm yönetimi ve daha iyi kullanıcı deneyimidir

Mevcut ODP dosya yapısı

  • ODP dosyası, XML dosyaları ve görsel kaynaklar içeren bir ZIP arşividir
  • Örnek olarak, 2014 SouthEast LinuxFest’teki SQLite konulu 49 slaytlık sunum dosyası zip -l çıktısında toplam 78 öğeye sahiptir
    • content.xml, styles.xml, meta.xml, settings.xml adlı dört XML dosyası slayt düzenini, metin içeriğini ve stilleri tanımlar
    • Sunum dosyasında tam ekran fotoğraflardan küçük ikonlara kadar 62 görsel ayrı dosyalar halinde saklanır
    • mimetype dosyasında tek satır olarak application/vnd.oasis.opendocument.presentation bulunur
  • OpenDocument kelime işlemci ve elektronik tablo dosyaları da benzer yapıya sahiptir, ancak analiz konusu ODP’dir

ZIP tabanlı ODP’nin sınırlamaları

  • ZIP arşivi, bir kez yazılıp birçok kez okunmaya optimize edilmiş bir anahtar/değer veritabanına yakındır ve az sayıda anahtarın büyük BLOB değerleri tuttuğu yapılara uygundur
  • Tek tek öğeleri güncellemek zor olduğundan kullanıcı File/Save seçtiğinde genellikle tüm ZIP arşivi yeniden oluşturulur
    • Güç kesintisi veya çökme sırasında belgenin tamamını bozmadan tekil öğeleri güncellemek mümkündür, ancak yeterince zor olduğundan pratikte neredeyse hiç kullanılmaz
    • 50 MB’lık bir sunum dosyasında yalnızca tek bir karakter değişse bile 50 MB’ın tamamının yeniden yazıldığı bir durum oluşur
  • Başlatma süresi yavaşlayabilir
    • ODP, tüm slayt içeriğini content.xml adlı büyük bir XML dosyasında saklar
    • LibreOffice ilk slaydı göstermek için bu dosyanın tamamını okuyup ayrıştırır
    • Görünüşe göre görsellerin tamamı da belleğe okunur; bunun sonucunda dosyaya çift tıklandığında ilk slayt yerine bir ilerleme çubuğu görünür
  • Bellek kullanımı artar
    • ZIP yapısı, başlangıçta tüm belgeyi belleğe okuyan, düzenlemeyi bellekte yapan ve kaydetme sırasında tüm belgeyi diske yazan uygulama tarzını teşvik eder
    • 50 MB’lık bir sunum dosyası 200 MB’tan fazla RAM kullanabilir
    • Birden fazla sunum dosyası aynı anda açıkken tarayıcı ve masaüstü uygulamaları da birlikte kullanılırsa swapping gerçekleşebilir
  • Çökme kurtarma zahmetli hale gelir
    • OpenOffice türevleri çökmelere karşı bellekteki belgeyi düzenli aralıklarla yedekler
    • Yedekleme sırasında uygulama birkaç saniyeliğine donabilir ve yeniden başlatma sonrasında ayrı bir kurtarma iletişim kutusundan geçmek gerekir
  • İçeriğe erişilebilirlik düşüktür
    • ZIP araçlarıyla görseller çıkarılabilir, ancak slayt metnini genel araçlarla çıkarmak veya düzenlemek zordur
    • Örnek dosyadaki content.xml dosyasının ilk satırı XML bildirimi, ikinci satırı ise tek satır halinde 211.792 karakterlik XML içerir

İlk iyileştirme: ZIP’i SQLite ile değiştirmek

  • İlk adım, ZIP öğelerini SQLite tablo satırlarına dönüştüren basit bir yapıdır
CREATE TABLE OpenDocTree(
  filename TEXT PRIMARY KEY,
  filesize BIGINT,
  content BLOB
);
  • Bu aşamada dosya formatının geri kalan yapısı değiştirilmez
    • Hâlâ bir “dosya yığını” yapısıdır, ancak her dosya ZIP girdisi değil SQLite veritabanındaki bir satır olur
  • NeoOffice’in oluşturduğu self2014.odp ile aynı içeriğin SQLAR aracıyla yeniden paketlenmiş SQLite dosyası boyut karşılaştırması şöyledir
    • self2014.odp: 10.514.994 bayt
    • self2014.sqlar: 10.464.256 bayt
    • Komut satırındaki zip ile yeniden sıkıştırılmış zip.odp: 10.416.644 bayt
  • SQLite dosyası, NeoOffice’in ürettiği ODP’den yaklaşık %0,5 daha küçüktü
    • Komut satırındaki zip ile iyi sıkıştırılmış ZIP dosyası ise SQLite’tan tekrar yaklaşık %0,5 daha küçüktü
    • SQLite veritabanı, boyut açısından ZIP arşiviyle rekabet edebilir
  • SQLite atomik yazma sunduğu için, çökme veya güç kaybı sırasında bile belgeyi bozma riski olmadan artımlı değişiklikler kaydedilebilir
    • content.xml dosyasının tamamını yeniden yazma sınırlaması devam eder
    • Yine de diğer 77 dosya olduğu gibi bırakılabildiğinden File/Save hızlanır ve SSD’ye yazılan veri miktarı azalır

İkinci iyileştirme: İçeriği küçük parçalara bölmek

  • SQLite, büyük blokların yanı sıra çok sayıda küçük parçayı da verimli biçimde saklayabildiğinden slayt bazlı bir içerik tablosu kullanılabilir
CREATE TABLE slide(
  pageNumber INTEGER,
  slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
  • İlk ekranı gösterirken uygulamanın yalnızca ilk slaydı okuması yeterlidir
SELECT slideContent FROM slide WHERE pageNumber=1;
  • Bu yapıda yalnızca ilk slayt içeriği hızlıca alınıp ayrıştırılarak gösterilebilir; başlangıçta tüm content.xml dosyasını okumaya gerek kalmaz
  • Uygulama seçenekleri de artar
    • İlk slayt gösterildikten sonra kalan sayfalar arka plan iş parçacığında okunabilir
    • Bellekte yalnızca mevcut slayt tutulabilir
    • Hızlı geçiş için mevcut slayt ve sonraki slayt bellekte tutulabilir
  • Kaydetme de yalnızca değişen sayfaları yeniden yazmayı gerektirdiğinden File/Save daha hızlı hale gelir
  • Kısa metin parçalarında sıkıştırma verimliliği düşebilir ve belge boyutu artabilir
    • Ancak belge alanının büyük kısmını görseller kapladığından, metin sıkıştırma verimindeki azalma kullanıcı deneyimi iyileşmesine kıyasla küçük bir maliyet olarak görülebilir

Üçüncü iyileştirme: Sürüm yönetimi

  • Slaytlar tekil nesneler olarak saklandığında aynı belge içinde sürüm geçmişi tutulabilir
CREATE TABLE slide(
  slideId INTEGER PRIMARY KEY,
  derivedFrom INTEGER REFERENCES slide,
  content TEXT
);
CREATE TABLE version(
  versionId INTEGER PRIMARY KEY,
  priorVersion INTEGER REFERENCES version,
  checkinTime DATETIME,
  comment TEXT,
  manifest TEXT
);
  • Her slayt sayfa numarası yerine benzersiz bir slideId değerine sahip olur; sıra, version tablosundaki manifest içinde saklanan slideId listesiyle belirlenir
  • Başlangıçta uygulama önce gösterilecek sürümü seçer ve genellikle en yeni sürümü alabilir
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
  • checkinTime temelinde en yeni sürümü getiren sorgu da mümkündür
SELECT manifest, versionId, max(checkinTime) FROM version;
  • SQLite’ta yukarıdaki max(checkinTime) sorgusu tanımlı bir sonuç döndürür; ancak birçok başka SQL veritabanında tanımsız bir sonuç döndürebilir veya hata verebilir
  • Kullanıcı File/Save yaptığında yalnızca değiştirilmiş slaytlar slide tablosuna yeni satırlar olarak eklenebilir ve değiştirilmiş manifest değerine sahip yeni bir version satırı oluşturulabilir
  • version tablosu check-in zamanını, kullanıcı yorumunu ve üst sürümü kaydederek değişiklik geçmişini korur
  • Aynı belge içinde birden fazla sunum saklamak da mümkündür
  • Ayrı bir yedekleme dosyası yerine özel bir pending sürümü kullanılırsa kaydedilmemiş değişiklikler sık sık ve sessizce kaydedilebilir
    • Tüm belge yerine yalnızca değişiklikler yazılacağı için birkaç MB değil birkaç KB yazan bir işlem olur
    • Kaydetme süresi saniyeler değil milisaniyeler düzeyinde olabilir
    • Çökme sonrası yeniden başlatılsa bile kullanıcının çalışmasının büyük kısmı veya neredeyse tamamı korunabilir
    • Kullanıcı kaydedilmemiş değişiklikleri atmak isterse önceki sürüme dönebilir

SQLite dosya formatında mümkün olan diğer özellikler

  • SQLite konteyneri, yalnızca üç tabloyla bile uygulama dosya formatına önemli özellikler ekleyebilir
  • Ek olarak şema, indeksler, tetikleyiciler, görünümler ve kısıtlar kullanılarak performans, kullanım kolaylığı ve tutarlılık artırılabilir
  • Genişletme fikirleri şunlardır
    • Veritabanı tablolarında otomatik undo/redo yığını saklanarak önceki düzenleme oturumlarına kadar geri alma mümkün olur
    • Slayt destesi veya birden çok slayt destesine tam metin arama işlevi eklenir
    • settings.xml SQL tablolarına ayrıştırılarak başka uygulamalarda daha kolay görüntülenip düzenlenebilir
    • Her slaydın sunumcu notları ayrı bir tabloya ayrılarak üçüncü taraf uygulamalar veya script’ler tarafından kolayca erişilebilir hale gelir
    • Basit doğrusal slayt sırasının ötesinde, dinleyici tepkisine göre farklı yollar ve yan akışlar içeren sunum yapıları desteklenir

SQLite’a yönelik yaygın çekinceler ve yanıtlar

  • Kurumsal SQL veritabanı deneyimi nedeniyle SQLite’ı uygulama dosya formatı olarak kullanmaya karşı çekince duyulabilir
  • Birçok kurumsal veritabanı büyük string veya BLOB değerlerini veritabanına koymak yerine ayrı dosyalar olarak saklamayı önerir, ancak SQLite farklıdır
  • Tüm SQL şemalarının üçüncü normal formda (3NF) olması ve yalnızca küçük ilkel tipler saklaması gerektiği düşüncesi de kısıtlayıcı olabilir
    • İlişkisel teori önemlidir, ancak gerçek dosya formatlarında XML veya JSON gibi karmaşık bilgileri metin alanlarında saklamak da kabul edilebilir bir seçimdir

Uygulama dosya formatı olarak SQLite

  • SQLite veritabanı dosyası, aynı bilgileri içeren ZIP arşiviyle neredeyse aynı boyuttadır ve bazı durumlarda daha küçük olabilir
  • Atomik güncellemeler sayesinde küçük değişiklikler belgeye güvenli biçimde kaydedilerek disk I/O’su azaltılabilir ve File/Save performansı artırılabilir
  • Uygulama yalnızca ilk ekran için gereken içeriği okuyarak başlatma süresini kısaltabilir
  • Yalnızca mevcut görüntüyle ilgili içerik bellekte tutulup kalanı diskte bırakılabildiğinden bellek kullanımı ciddi ölçüde düşürülebilir
  • SQL şeması, ZIP gibi anahtar/değer yapılarından daha doğrudan ve yalın biçimde bilgi ifade edebilir
    • Üçüncü taraf uygulama ve script’lerin erişilebilirliği artar
    • Yerleşik belge sürüm yönetimi ve çökme sonrası çalışma kurtarma gibi gelişmiş özelliklerin uygulanması kolaylaşır
  • OpenDocument zaten yerleşik ve iyi tasarlanmış bir formattır; SQLite, OpenDocument’tan sonra çıktığı için mevcut tercihi eleştiren bir içerik değildir
  • Application File Format belgesi, SQLite’ı uygulama dosya formatı olarak kullanmaya yönelik ek fikirler sunar

1 yorum

 
GN⁺ 2023-09-19
Hacker News görüşleri
  • SQLite'ı dosya biçimi olarak kullanan bir uygulama geliştiriyorum
    Kullanıcının belgeyi düzenleyip yalnızca kaydettiğinde dosyanın değiştiği alışılmış akışı korumak istediğim için, dosya açılırken onu :memory: veritabanına kopyalıyorum: https://www.sqlite.org/inmemorydb.html
    Kullanıcı istediği gibi işlem yapıyor, uygulama da ayrı bir belge modeli olmadan değişiklikleri doğrudan veritabanı biçimine yansıtıyor. Kaydederken VACUUM ile tekrar veritabanı dosyasına yazarak hallediyorum: https://www.sqlite.org/lang_vacuum.html
    Makul boyuttaki dosyalarda iyi çalışıyor; benim uygulamamda dosyalar her zaman bu aralıkta kalıyor

    • Yardımcı bir uçucu veritabanı neden kullandığınızı anlamıyorum. Kullanıcının bir dosyayı düzenlediği durumda saniyede bir kez bile yazma olmayacaktır, dolayısıyla performans kazancı da büyük olmaz
      Bunun yerine doğrudan veritabanına otomatik kaydetmek ve kaydet düğmesini kaldırmak daha iyi. Çakışmalara dayanıklı olur, tek veritabanı olduğu için kod ve hata sayısı azalır; SQLite yazmaları ya başarılı olur ya başarısız, ara bir durum yoktur. Buna karşılık, belgede alıntılandığı gibi VACUUM INTO, beklenmedik kapanma veya güç kaybı durumunda çıktı veritabanını eksik ya da bozuk bırakabilir
      SQLite'ı baştan tasarlandığı şekilde kullanırsanız, SQLite'ın ömrü boyunca bu kısmı düşünmek zorunda kalmazsınız
    • Normal bir uygulama gibi davranması, uygulama çökerse veya güç giderse kaydedilmemiş verileri kaybedeceğiniz anlamına gelir
      Her işlemden sonra geçici bir konuma, örneğin XDG dizinlerine göre ~/.local/share/application/yourapp gibi bir yere kaydetmek ve kullanıcı kaydet'e bastığında dosyayı istediği konuma kopyalamak çok daha iyi. Güç kaybından sonra uygulamayı yeniden açsanız bile neredeyse aynı noktadan kurtarılır; en fazla son birkaç saniyeyi kaybedersiniz
    • Daha basit olarak, veritabanını açarken WAL moduna geçirip otomatik checkpoint'i kapatmak da yeterli olabilir: https://www.sqlite.org/pragma.html#pragma_wal_autocheckpoint
      Kullanıcı kaydettiğinde checkpoint çalıştırıp WAL içeriğini ana veritabanına birleştirmek yeterli olur
    • Belgeye göre VACUUM, içeriği geçici bir veritabanı dosyasına kopyaladıktan sonra asıl dosyanın üzerine yazar; üzerine yazarken de normal bir transaction gibi rollback journal veya WAL kullanır. Bu yüzden asıl dosyanın yaklaşık iki katı kadar boş alana ihtiyaç duyar
      VACUUM INTO, geçici veritabanı yerine INTO ile belirtilen dosyayı kullanır ve sonra asıl dosyanın üzerine geri kopyalama adımını atlar. Gerçekte kullanılanın güç kesintisine dayanıklı VACUUM mu, yoksa yazma sırasında güç kesintisine karşı hassas olan ve mevcut dosya adı kullanılırsa onu bozabilecek gibi görünen VACUUM INTO mu olduğu önemlidir
    • Benzer bir yöntem kullanmıştım; veritabanını bellekte cache olarak çalıştırıp periyodik olarak diske kaydediyordum ve backup API kullanıyordum: https://www.sqlite.org/backup.html
  • SQLite'ın sorunu standartlaştırılmış bir dosya biçimi olmaması
    İyi belgelenmiş ve yaygın biçimde anlaşılmış olsa da, bir SQLite dosyasının nasıl yorumlanacağını ISO standardı ayrıntılı biçimde tanımlamıyor. Alternatif implementasyonlar için de aynı durum geçerli
    Zip ve XML'in API yüzeyi SQLite'a göre çok daha küçüktür. SQLite'ın API'si birkaç C fonksiyonunun ötesinde SQL dilinin kendisidir; SQL parser, sorgu optimizer, compiler, bytecode sanal makinesi, full-text search engine vb. bileşenleri veri bozulması olmadan uygulamak, bir XML parser yazmaktan çok daha büyük bir iştir
    Birlikte çalışabilirliğin veya ISO standardizasyonunun önemli olmadığı alana özgü kapalı bir uygulamada SQLite iyi bir dosya biçimidir, ancak OpenOffice için bu tür endişelerin gerçekten var olduğunu anlıyorum

    • Bu sorunun neyi işaret ettiği belirsiz. SQLite dosya biçimi public domain ve iyi belgelenmiş durumda; çeşitli dillerde parser'ları var
      SQLite C kütüphanesi de public domain, kaynak kodu tamamen açık, dosya biçimiyle ilgileniyor ve belgelendirme düzeyi çoğu ISO standardından daha yüksek. Başlıca diller için binding'leri de neredeyse tamamen mevcut
      SQLite dosyası içinde saklanacak bir OpenDocument biçiminin henüz oluşturulup belgelenmeyi beklemesi ayrı bir mesele. ISO standardı iyi bir şey, ama dosya biçimlerini ISO'nun tanımlamasını beklemek zorunda kalsaydık kullanabileceğimiz şeyler çok az olurdu
    • Standart dosya biçimi olmak için SQL parser, sorgu optimizer, compiler, bytecode sanal makinesi ve full-text search engine'in tamamını uygulamak gerekmez
      Bu, bir LibreOffice hesap tablosunu okumak için tüm hesap tablosu özelliklerini uygulamak zorunda olmamaya benzer. Gereken şey tabloları yeniden oluşturabilme yeteneğidir; ardından istediğiniz bilgileri seçtiğiniz dilde yazılmış imperative kodla dolaşarak elde edebilirsiniz
    • Bu, ABD Kongre Kütüphanesi için de sorun değil. Kongre Kütüphanesi, SQLite'ı CSV, XML ve JSON ile birlikte veri kümeleri için önerilen saklama biçimi olarak tanımladı
    • Dosya biçimiyle onun kullanım biçimini karıştırıyor gibisiniz. SQLite dosya biçimini kullanan bir uygulama, SQLite kütüphanesini uygulamanın bir parçası olarak kullanabilir
      O kütüphaneyi yeniden uygulamak büyük iş olurdu, ama bu OpenDocument dosya biçimini kullanan kodu yeniden uygulamakla aynı türden bir iştir. Dosya biçiminin kendisi oldukça basittir
    • Standart şart değil. Uygulama ile belge arasındaki tüm etkileşim SQL üzerinden gerçekleşir ve SQL en azından önemli kısımlarda standartlaştırılmıştır
      Uyumluluktan endişe ediliyorsa belge, MySQL gibi başka veritabanlarıyla da erişilebilir olacak şekilde yapılabilir
  • Audacity’nin SQLite’ı benimserse dosya kaydetme işlevinin büyük ölçüde iyileşeceğini sanmıştım, ama pratikte pek çok tuzak varmış
    Linux’ta /etc/fstab ile oluşturulmuş, root’a ait ama herkesin yazabildiği bir NTFS bağlama noktasına yeni dosya olarak kaydedince izin hatası gibi nedenlerle başarısız oluyor; mevcut bir dosyaya kaydedince ise düzgün çalışıyordu
    Bir projeyi düzenlediğiniz anda diskteki dosya değiştiriliyor; Audacity projesini Git’e ikili bir yığın olarak koyarsanız gereksiz Git farkları oluşuyor. Kaydetseniz bile eski ya da silinmiş veriler, proje penceresi kapanana kadar SQLite dosyasında kalıyor; commit’ten önce pencereyi kapatmazsanız depoya girebiliyor. Eskiden .aup3 dosyasına elle VACUUM yapmak gerektiğini hatırlıyorum, şimdi ise pencereyi kapatmak yeterli. Word 2003’ün Fast Save özelliğini andırıyor

    • Audacity çöker ya da anormal kapanırsa temizlik hiç yapılmadığı için uğraştırıyor. Eskiden kurtarma sürecinde yetim bloklar olduğu söylenir, bunları tutmak mı silmek mi istediğiniz sorulurdu
      Birkaç yüz MB olması gereken proje birkaç GB’a çıktığında ve disk alanından tasarruf etmek gerektiğinde, basit tek kanallı işler için Mix and Render çözüm oluyordu. Sesi değiştirmiyordu ama kaydetme ve çıkış sırasında artık verileri temizleyebiliyordu
      Bu SQLite’ın kendi sorunu değil, açıkça uygulama katmanı sorunu. Audacity 2’de geçici çalışma alanı kavramı var gibiydi; Audacity 3 ise .aup3 dosyasının kendisini çalışma alanı olarak kullanıyor gibi görünüyor
      Audacity 3 biçimine bakmıştım; eski .aup dosyasına karşılık gelen proje verilerini tek satırlık bir tabloda XML olarak saklarken, metni olduğu gibi yazmayıp basit bir sözlük kodlayıcıyla kodlaması beni çok şaşırtmıştı. Birlikte çalışabilirliği ve denetlemeyi çok daha zorlaştırıyor, performansa da az da olsa zarar veriyor; alan tasarrufu ise yüzlerce MB’lık ses dosyalarında birkaç KB düzeyinde yuvarlama hatası kadar olur
    • Kullanıcının beklediği işlevler aynen yeniden üretilmeli. Her şey geçici dosyalara kaydedilmeli, özgün dosyanın üzerine yalnızca açık bir kaydetme eylemi olduğunda yazılmalı
      Git açısından fark ve birleştirmesi kolay bir metin biçimi kullanmak daha avantajlı. SQLite dump’ının bu açıdan ne kadar kolay olduğunu bilmiyorum
    • Eşim Audacity’yi bütün gün kullanıyor ve birkaç günde bir bozulmuş SQLite dosyası oluşuyor. Yinelenen anahtar hatası veriyor; Audacity’de bunu düzeltmenin ya da yeniden içe aktarmanın yolunu bilmiyorum
      Önemliyse elle düzeltilebilir ama genelde dosyayı atınca tekrar çalışıyor
  • Güzel yazı. Yine de OpenDocument’ın Zip arşivi içindeki XML dosyaları kümesi olması hoşuma gidiyor
    Belge biçimini bilen ağır bir kütüphane olmadan da elektronik tablo gibi belgeler oldukça kolay üretilebiliyor
    Bir web servisi kullanıcısının tablo satırları olarak dışa aktardığı verileri birden fazla araçta kullanmak istediği durumlar oluyor. UTF-8 CSV açık, geleneksel ve kullanılabilir bir format; ama son kullanıcıya CSV sunmuş olan herkes, elektronik tablo uygulamalarında yaşanan tıkanmanın acısını bilir
    Örnek bir elektronik tabloyu OpenDocument’ın ODS’i ve Microsoft’un XML canavarı OOXML’in XLSX’i olarak kaydettikten sonra XML biçiminin temelini anlamıştım. Zip arşivini yalnızca gerekli öğeler kalacak şekilde küçülttüm, içeriğin geleceği yerleri işaretledim ve istek geldiğinde yeni bir elektronik tablo dosyası ürettim. Artık aynı veriyi CSV, ODS, XLSX ve JSON olarak çıktılayabiliyorum
    SQLite ile de yapılabilir ama biraz daha karmaşık olur ve geliştirme hızını düşürür. Bir ofis paketiyle şablon belge oluşturup kaydedilen dosyanın XML’inin içine dalabilmek niş ama güzel bir özellik
    Özellikle nl_NL gibi yerellerde Excel’in CSV dosyalarında sütun ayırıcısını noktalı virgül olarak sabit kodlanmış gibi davranması sorun. Çünkü Microsoft, Hollandalıların comma separated values dosyalarında virgül kullanmadığına kötü şöhretli bir şekilde karar vermiş

    • Bu davranış tamamen sabit kodlama değil; localeconv()->decimal_point değerine bağlı. Değer , ise Excel hem CSV dosyalarında hem de formül ifade dilinde noktalı virgül kullanıyor
      Eskiden Excel’de CSV/TXT açarken bunu ayarlamak mümkündü ve LibreOffice’te hâlâ mümkün, ama genel UI sadeleştirme sürecinde Data menüsünde/şerit sekmesinde bir yerlere taşındı. Yeni bir çalışma kitabı açıp doğru seçeneği bulmanız gerekiyor; zamandan kazanmak istiyorsanız LibreOffice kullanmak daha iyi
  • Bu kısım gerçekten şaşırtıcıydı. İç içe sorguya gerek olmamasına inanmak zor
    SELECT manifest, versionId, max(checkinTime) FROM version;
    SQLite’ta max(checkinTime) kullanan bu ikinci sorgunun gerçekten düzgün çalıştığı ve tanımlı bir cevap döndürdüğü söyleniyor. Diğer SQL veritabanı motorlarında tanımsız bir cevap döner ya da hata olur; ama SQLite, en büyük checkinTime değerine sahip öğenin manifest ve versionId değerlerini döndürüyor

    • Yararlı bir özellik olabilir, ama dürüst olmak gerekirse böyle bir sorgunun bu şekilde döneceğini beklemezdim
      Bu durumda iç içe sorguya gerek yok; checkinTime’a göre sıralayıp sonucu bire sınırlamak yeterli: select manifest, versionId, checkinTime from version order by checkinTime desc limit 1
      En azından SQLite ve PostgreSQL’de çalışır. Oracle’da where rownum=1 kullanmak gerektiği için iç içe sorgu gerekiyordu diye hatırlıyorum
    • Bu, kabaca GROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1 ifadesinin ya da en büyük checkinTime değerini bulup ardından join yapan bir CTE’nin kısaltması gibi görülebilir
      Ancak aynı en büyük checkinTime değerine sahip birden fazla satır varsa rastlantısallık var ve bu bir ayağın altındaki silaha dönüşebilir; bu yüzden SQLite3’e özgü bu özelliği pek kullanmıyorum. Deterministik olarak en iyi satırı seçmek için yukarıdakine benzer açık bir yöntem gerekir
    • SQL’de genel olarak beklenen davranış bu değil, ama SQLite sık sık beklentilerin dışına çıkar. Bu durumda kullanışlı, fakat standart dışı
    • Uygulamanın kullanışlı bir yan etkisi sonradan resmî davranış olmuş gibi. Python sözlük anahtar sırasına benziyor
      Postgres’te DISTINCT ON sorgusuyla benzer bir şey yapılabilir. SQL’de basit görünen ama bana en zor gelen işlerden biriydi
    • Bunun tanımlı davranış olduğunu iddia edebilmek oldukça şaşırtıcı. Çünkü manifest ve versionId, max(checkinTime) değerine fonksiyonel olarak bağımlı değil
      Örneğin aynı checkinTime değerine sahip iki satır olabilir ve bu değer en büyük değer olabilir
  • Hem SQLite hem de XML dosyaları kullanan bir ürün çıkarmıştım
    İyileştirdiğimiz noktalardan biri, az veri içeren bazı tabloları XML dosyalarına taşımaktı. Dosyalar küçük ve neredeyse hiç kullanılmadığı için veri erişim katmanı ve tanılama basitleşti; bunları birden çok satırlı, sekme girintili XML olarak oluşturduk
    Ürünü tanılaması gereken teknik görevliye SQLite veritabanını açıp bakmasını söylemek büyük bir yük oluyordu. Ancak ürünün ana bölümlerinde SQLite, XML dosyalarından ezici biçimde daha iyiydi. Önceki sürüm XML dosyaları kullanıyordu; XML dosyalarında iyi bir artımlı güncelleme yöntemi olmadığı için ölçeklenebilirlik sorunları vardı
    XML’in insan tarafından okunabilir format olma avantajı, ancak dosya küçük olduğunda ve şema tasarımı okunabilir XML’e uygun yapıldığında gerçekten işe yarıyor. Her seferinde tüm XML dosyasını yeniden yazma zorunluluğu ve özellikler arttıkça ortaya çıkan karmaşıklık, XML’in en büyük avantajını hızla aşındırıyor
    Sıradan kullanıcıların ofis belgelerinin içini doğrudan kurcalaması yeterince nadir bir durum; bu yüzden SQLite okuyucu kullanmayı öğrenmek kabul edilebilir bir giriş engeli sayılır. Dosyanın ortasına rastgele yazma konusunda XML+Zip’in sınırları Moore yasasıyla bile aşılamaz

    • SQLite’ın yerel formatının, Zip olmadan XML+Zip’e benzer boyuta nasıl ulaştığını tam bilmiyorum. SQLite’taki TEXT veya BLOB alanlarının sıkıştırılıp sıkıştırılmadığını ya da çağıranın yazmadan önce BLOB’u sıkıştırmasının varsayılıp varsayılmadığını merak ediyorum
  • ODT, standartlaştırma düşünülerek tasarlanmıştı. Önceki format da çok benzerdi ama XHTML, SVG, CSS gibi mevcut standartlara büyük ölçüde dayanıyordu
    Mevcut standartlara atıf yapılamazsa ODT şartnamesinin kendisi bir anda devasa hale gelirdi. Standardı güncelleme çabası da epey büyük görünüyor ve son birkaç yılda pek ilerleme olmadı
    Gerçekçi olarak SQLite formatı bir seçenek olarak sunulabilir, ancak ofis belge formatları için gemi çoktan kalkmış gibi. Yine de SQLite şartnamesini resmî standart olarak düzenleme savı için iyi bir gerekçe oluşturuyor

    • Şartname çok özlü yazılmış ve etki ile davranıştan çok esas olarak sözdizimini tanımlamasına rağmen 840 sayfa gibi büyük bir hacme sahip
      ooo:rsid özniteliği nedeniyle yerel stillerin ve metin aralıklarının patlaması, seyrek olmayan elektronik tablolar, tablo stillendirmesinin tuhaf mekanizması gibi birkaç kusur dışında, bu tür belge verileri için çok iyi tasarlanmış bir işaretleme. Semantik işaretleme ile kullanıcının gerçekten yapmak istediği görsel ifade arasında iyi bir denge var
      Buna karşılık Office OpenXML’de durum taşıyan, biçimlendirme amaçlı boş etiketler var; DOCX’te ise ardından gelen metnin kalın gösterilip gösterilmeyeceğini açıp kapatıyor
  • Dosya biçimini SQLite’a bağlamak bir şeylerin yanlış olduğu hissini veriyor
    SQLite iyi, ama bu alanda epey benzersiz. Çok fazla iş yaptığı için aynısını yeniden üretmek zor
    Ama bu durumda gerçekten bu kadar çok özelliğe ihtiyaç var mı derseniz, yok. Temel ve güvenli işlem semantiği ile basit tablo yapısı saklama yeteneği yeterli; tüm SQL standardına ya da sorgu iyileştiricisine kadar gerek yok
    Daha iyi bir dosya biçimi olabilir, ama SQLite’tan ayrı bir biçim olursa daha iyi olurdu

    • Neden olmasın bilmiyorum: https://www.sqlite.org/appfileformat.html
      Boyutu 1 MB’tan küçük ve https://sqlite.org/footprint.html, tüm özellikler açıkken bile 750 KB: https://www.sqlite.org/about.html
      Derleme zamanında epey özelliği çıkarabiliyorsunuz; sorgu planlayıcıyı ayarlama ya da küçültme seçenekleri de var gibi görünüyor: https://www.sqlite.org/compile.html
      Üstelik “SQLite istemci/sunucu veritabanlarıyla rekabet etmez. SQLite fopen() ile rekabet eder” diye de bir söz var: https://www.sqlite.org/whentouse.html
      Sonuçta gereken şey veritabanının kendisi değil, veritabanı API’si ve davranışı sağlayan bir kütüphane
    • İşlem tarafı, özellikle eşzamanlı dosya erişiminde sandığımdan daha zordu. O zamanlar SQLITE_BUSY işleme epey zahmetliydi
      İşlem işlemede serileştirme hatalarının beklenen bir şey olduğunu biliyorum, ama SQLite’ta kendi kendine kilitlenme gibi kalıcı hatalarla geçici eşzamanlı güncelleme sorunlarını ayırt etmek zordu. Geçici hataysa işlem işini tanımlayan closure’ı yeniden çalıştırmak yeterli, ama kalıcı hataysa anlamsız
      Sorunun bir kısmı, sqlite3_stmt’nin hem hazırlanmış ifade hem de sonuç kümesi niteliğini birleştirmesi. Derlenmiş bytecode’u önbelleğe almak için uzun süre elde tutuluyor; yinelemenin ortasında durursanız o anda kilidi tutuyor olabilir. Bu da beklenmedik kilit yükseltme hatalarına yol açabilir
      Sonunda sqlite3_next_stmt, sqlite3_stmt_busy, sqlite3_sql ile ayrıntılı hata raporlama oluşturarak sorunu ortadan kaldırdım. Kişisel kullanım içindi, yine de işlem yeniden deneme kodu isteğe bağlı günlükleme ve yorumlarla doluydu. PostgreSQL için işlem yeniden deneme mantığı çok daha kolaydı
      Bir başka şaşırdığım nokta, synchronous=NORMAL olan WAL modunda commit edilmiş işlemlerin güç kaybı veya sistem çökmesi sonrası geri alınabileceğini söyleyen dokümantasyondu: https://sqlite.org/pragma.html#pragma_synchronous
      Benim uygulamam için ilgili değildi
    • SQLite zaten tam olarak bu amaçla kullanılıyor. OGC GeoPackage olarak kullanılıyor; Mapbox/Maptiler veri kümeleri de kullanıyor
    • Bazı biçimler her şeyden önce değişim için tasarlanır. SQLite tarafındaki argüman, uygulama sahibinin SQLite biçimini kullanıcıya dayatarak fiilen standart hâline getirmesi; yasal standart hâline getirme işi ise eksik
      Richard Hipp ve şirketinin asla dışına çıkmadığı bir ISO/IEC/ANSI/ETSI SQLite standardı, etki edecek patent olmadığına dair hukuki inceleme ve tüm avantajları koruyan birden fazla uyumlu SQLite uygulaması gösterilirse, o zaman dosya biçimi olarak önerilmesini tartışabiliriz. Aksi hâlde bu, tek kaynaklı bir uygulamaya güçlü bir bağımlılık koyup bunu kullanıcıya da yükleyin demek
      XML, ASN.1, JFIF resmî standartlar; ZIP de OpenDocument standartlaştırma sürecinde ISO/IEC 21320-1:2015 olarak kabul edilmiş resmî bir standart
      Belgelerde en önemli şey, başka herkesin okuyabilmesidir. Disk güncelleme süresini azaltmak ikincil bir iştir. Microsoft’un bağımlılığı sürdürmek için standart kuruluşlarını nasıl çarpıttığından hiçbir şey öğrenmemiş olmamalıyız: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
    • Apple uygulamalarına bakınca çoğu SQLite’ı depolama biçimi olarak kullanıyor. iMovie, iPhoto, Voice recording vb.; Docker da aynı şekilde
      Bu kadar yanlış bir seçim olamaz
  • Başka bir örnek de raster harita karoları. Aslında sayıları milyonlara kadar çıkabilen küçük kare görseller
    Zip, tar, dosya sistemi ve SQLite’ın hepsini denedik; SQLite en hızlı ve en küçük olanıydı, hatta ek yükü olmayan sıradan arşivlerden bile daha iyiydi

    • Birçok dosya sistemi tek bir dizinde on binlerce dosyadan fazlası olduğunda sorun yaşar; harita karolarında da tam olarak bu durum ortaya çıkar. SQLite’ın daha hızlı olması şaşırtıcı değil
    • SQLite daha hızlıysa sorun kullanılan Zip kütüphanesindedir
      SQLite’ın büyük bir dezavantajı var. Veritabanından alınan BLOB mmap edilemez; başka bir yere kopyalanması gerekir. Zip dosyaları ise sıkıştırılmamışsa ya da PVRTC gibi özel bir kodlamayla sıkıştırılmışsa doğrudan mmap edilebilir
  • OpenDocument sıkıştırılmış görsellerden ve XML’den oluşur. Sonuçta bu, tüm biçimi ayrıştırıp belleğe almak anlamına gelir
    SQLite’ın bunu nasıl iyileştirdiğini pek anlayamıyorum. XML ideal olmasa da Zip ile sıkıştırıldığı için boyut cezası da büyük değil
    SQLite yazısında sıralanan tüm avantajlar, SQLite’ı belgenin çalışma zamanı modeli olarak kullanırsanız uygulanabilir. Bu hem diskte hem bellekte mümkün, ancak SQLite’ın aktarım biçimi olması gerekmez
    Aksine SQLite mevcut biçimden daha büyük hale gelebilir. Değişikliklerden sonra kullanılmayan alan oluşur; parçalanabilir ve seyrekleşebilir. Her seferinde optimize etmek gerekiyorsa hızlı kaydetme gibi avantajlar da ortadan kalkar
    Delta güncellemelerine ve hızlı dizin aramalarına ihtiyaç duyan, dosyanın tamamını belleğe almaması gereken biçimler gerçekten de SQLite’ı dosya biçimi olarak sıkça kullanıyor. Ancak OpenDocument’ın bu varsayımsal senaryoda SQLite hedefi olarak seçilmek için kötü bir örnek olduğunu düşünüyorum

    • XML ve Zip artımlı güncellemeleri doğru düzgün yapamaz. Kaydederken tüm uygulama dosyasının yazılması gerekir ve yazma sırasında bir sorun çıkarsa bozulma oluşabilir
      SQLite’ı disk biçimi olarak kullanıp uygulamayı doğru şekilde uygularsanız bozuk bir durumda kalmayabilir
      XML/Zip ile de yeniden adlandırma hilesiyle benzer bir sonuç elde edilebilir, ama SQLite bunu tek bir disk dosyası içinde sunar. SQLite’ı zaten bellek modeli olarak kullanıyorsanız, disk/aktarım biçimi olarak da kullanmamak için bir neden yok. O noktada neredeyse bedavaya gelir
      Dosya boyutu sorunu VACUUM ile çözülebilir gibi görünüyor