2 poin oleh GN⁺ 2023-09-19 | 1 komentar | Bagikan ke WhatsApp
  • Jika OpenDocument Presentation (ODP) disimpan dalam kontainer SQLite alih-alih arsip ZIP, cara penyimpanan, pembukaan, dan pemulihan dokumen dapat dirancang agar lebih aman dan lebih cepat
  • Saat ini ODP memiliki struktur yang menggabungkan file XML dan gambar dalam arsip ZIP, dan file presentasi contoh berisi 49 slide terdiri dari total 78 entri termasuk content.xml, styles.xml, meta.xml, settings.xml, dan gambar
  • Dalam struktur berbasis ZIP, bahkan perubahan kecil pun mudah menyebabkan seluruh arsip ditulis ulang sehingga pembaruan inkremental sulit dilakukan, yang berujung pada jeda File/Save dan meningkatnya jumlah penulisan ke SSD
  • Jika diganti ke SQLite, file dapat disimpan sebagai baris tabel, dan lebih jauh lagi konten serta versi dapat dipisahkan per slide sehingga memungkinkan hanya membaca slide pertama atau hanya menyimpan slide yang berubah
  • Ini bukan kritik terhadap OpenDocument atau usulan untuk mengubahnya, melainkan contoh yang menunjukkan bahwa SQLite dapat memudahkan penerapan penyimpanan atomik, aksesibilitas, manajemen versi, dan pemulihan dalam format file aplikasi

Cakupan dan objek eksperimen pemikiran

  • Objek yang dibahas adalah OpenDocument, khususnya format dokumen presentasi ODP (OpenDocument Presentation)
  • Tujuannya bukan benar-benar mengubah OpenDocument, melainkan meninjau pendekatan penggunaan SQLite sebagai kontainer dalam perancangan format file di masa depan
  • Manfaat yang diharapkan adalah dokumen yang lebih kecil, File/Save yang lebih cepat, pembukaan yang lebih cepat, penggunaan memori yang lebih sedikit, manajemen versi dokumen, dan pengalaman pengguna yang lebih baik

Struktur file ODP saat ini

  • File ODP adalah arsip ZIP yang berisi file XML dan sumber daya gambar
  • Sebagai contoh, file presentasi SQLite 49 slide dari SouthEast LinuxFest 2014 memiliki total 78 entri pada output zip -l
    • Empat file XML, yaitu content.xml, styles.xml, meta.xml, dan settings.xml, mendefinisikan tata letak slide, konten teks, dan gaya
    • File presentasi itu menyimpan 62 gambar sebagai file terpisah, mulai dari foto layar penuh hingga ikon kecil
    • File mimetype berisi satu baris: application/vnd.oasis.opendocument.presentation
  • File OpenDocument untuk pengolah kata dan spreadsheet juga memiliki struktur serupa, tetapi fokus analisis di sini adalah ODP

Keterbatasan ODP berbasis ZIP

  • Arsip ZIP lebih mirip basis data key/value yang dioptimalkan untuk satu kali tulis dan banyak kali baca, dan cocok untuk struktur dengan sedikit key yang memiliki nilai BLOB besar
  • Karena sulit memperbarui entri individual, saat pengguna memilih File/Save, biasanya seluruh arsip ZIP ditulis ulang
    • Memang mungkin memperbarui entri individual tanpa merusak seluruh dokumen saat terjadi mati listrik atau crash, tetapi itu cukup sulit sehingga dalam praktik hampir tidak digunakan
    • Pada file presentasi 50MB, mengubah satu karakter pun bisa menyebabkan seluruh 50MB ditulis ulang
  • Waktu pembukaan bisa menjadi lambat
    • ODP menyimpan seluruh konten slide dalam satu file XML besar bernama content.xml
    • LibreOffice membaca dan mem-parsing seluruh file ini hanya untuk menampilkan slide pertama
    • Gambar juga tampaknya semuanya dibaca ke memori, sehingga saat file diklik dua kali yang muncul adalah progress bar, bukan slide pertama
  • Penggunaan memori menjadi besar
    • Struktur ZIP mendorong implementasi yang membaca seluruh dokumen ke memori saat mulai, melakukan pengeditan di memori, lalu menulis ulang seluruh dokumen ke disk saat menyimpan
    • File presentasi 50MB dapat memakai RAM lebih dari 200MB
    • Jika beberapa file presentasi dibuka bersamaan sambil menggunakan browser dan aplikasi desktop lain, swapping bisa terjadi
  • Pemulihan crash menjadi merepotkan
    • Keluarga aplikasi OpenOffice secara berkala mencadangkan dokumen di memori untuk mengantisipasi crash
    • Saat pencadangan, aplikasi bisa berhenti selama beberapa detik, dan setelah dijalankan ulang pengguna harus melewati dialog pemulihan terpisah
  • Aksesibilitas konten rendah
    • Gambar memang bisa diekstrak dengan alat ZIP, tetapi teks slide sulit diekstrak atau diubah dengan alat umum
    • Pada file contoh, content.xml memiliki deklarasi XML di baris pertama, dan baris kedua berisi 211.792 karakter XML dalam satu baris

Perbaikan pertama: mengganti ZIP dengan SQLite

  • Langkah pertama adalah struktur sederhana yang mengganti entri ZIP dengan baris dalam tabel SQLite
CREATE TABLE OpenDocTree(
  filename TEXT PRIMARY KEY,
  filesize BIGINT,
  content BLOB
);
  • Pada tahap ini, sisa struktur format file tidak diubah
    • Strukturnya tetap berupa “sekumpulan file”, tetapi tiap file menjadi baris dalam basis data SQLite, bukan entri ZIP
  • Perbandingan ukuran file SQLite yang dikemas ulang dengan utilitas SQLAR dari isi yang sama seperti self2014.odp buatan NeoOffice adalah sebagai berikut
    • self2014.odp: 10,514,994 byte
    • self2014.sqlar: 10,464,256 byte
    • zip.odp yang dikompresi ulang dengan zip baris perintah: 10,416,644 byte
  • File SQLite sekitar 0,5% lebih kecil daripada ODP yang dibuat NeoOffice
    • File ZIP yang dikompresi dengan baik menggunakan zip baris perintah masih sekitar 0,5% lebih kecil daripada SQLite
    • Basis data SQLite tetap kompetitif dibanding arsip ZIP dari sisi ukuran
  • SQLite menyediakan penulisan atomik, sehingga perubahan inkremental dapat disimpan tanpa risiko merusak dokumen saat crash atau mati listrik
    • Keterbatasan bahwa seluruh content.xml tetap harus ditulis ulang masih ada
    • Meski begitu, 77 file lainnya bisa dibiarkan apa adanya sehingga File/Save lebih cepat dan jumlah penulisan ke SSD berkurang

Perbaikan kedua: memecah konten menjadi bagian-bagian kecil

  • Karena SQLite dapat menyimpan bukan hanya blok besar tetapi juga banyak potongan kecil secara efisien, kita bisa memiliki tabel konten per slide
CREATE TABLE slide(
  pageNumber INTEGER,
  slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
  • Untuk menampilkan layar pertama, aplikasi hanya perlu membaca slide pertama
SELECT slideContent FROM slide WHERE pageNumber=1;
  • Dalam struktur ini, hanya konten slide pertama yang dapat diambil, di-parse, dan ditampilkan dengan cepat, sehingga tidak perlu membaca seluruh content.xml saat mulai
  • Pilihan implementasi juga bertambah
    • Setelah menampilkan slide pertama, halaman lainnya dapat dibaca di thread latar belakang
    • Hanya satu slide yang sedang aktif bisa disimpan di memori
    • Atau hanya slide saat ini dan slide berikutnya yang disimpan di memori untuk perpindahan yang cepat
  • Penyimpanan juga menjadi lebih cepat karena hanya halaman yang berubah yang perlu ditulis ulang saat File/Save
  • Pada potongan teks pendek, efisiensi kompresi bisa menurun sehingga ukuran dokumen dapat membesar
    • Namun karena sebagian besar ruang dokumen ditempati gambar, penurunan efisiensi kompresi teks dapat dianggap biaya kecil dibanding peningkatan pengalaman pengguna

Perbaikan ketiga: manajemen versi

  • Jika slide disimpan sebagai objek individual, riwayat versi dapat disimpan dalam dokumen yang sama
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
);
  • Setiap slide memiliki slideId unik, bukan nomor halaman, dan urutannya ditentukan oleh daftar slideId yang disimpan dalam manifest pada tabel version
  • Saat mulai, aplikasi memilih dulu versi yang akan ditampilkan, dan biasanya dapat mengambil versi terbaru
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
  • Query untuk mengambil versi terbaru berdasarkan checkinTime juga dimungkinkan
SELECT manifest, versionId, max(checkinTime) FROM version;
  • Di SQLite, query max(checkinTime) di atas mengembalikan hasil yang terdefinisi, tetapi di banyak basis data SQL lain hasilnya bisa tidak terdefinisi atau memicu error
  • Saat pengguna menjalankan File/Save, hanya slide yang diubah yang perlu ditambahkan sebagai baris baru ke tabel slide, lalu dibuat baris version baru dengan manifest yang telah diperbarui
  • Tabel version menyimpan waktu check-in, komentar pengguna, dan versi induk untuk mempertahankan riwayat perubahan
  • Bahkan dimungkinkan menyimpan beberapa materi presentasi dalam dokumen yang sama
  • Alih-alih file cadangan terpisah, sebuah versi khusus pending dapat digunakan untuk sering mencatat perubahan yang belum disimpan secara diam-diam
    • Karena yang ditulis hanya perubahan, bukan seluruh dokumen, operasinya menjadi penulisan beberapa KB alih-alih beberapa MB
    • Waktu penyimpanan bisa menjadi hitungan milidetik, bukan detik
    • Bahkan setelah reboot akibat crash, sebagian besar atau hampir semua pekerjaan pengguna tetap dapat dipertahankan
    • Jika pengguna ingin membuang perubahan yang belum disimpan, cukup kembali ke versi sebelumnya

Fitur lain yang dimungkinkan dengan format file SQLite

  • Kontainer SQLite dapat menambahkan fitur penting pada format file aplikasi hanya dengan tiga tabel
  • Selain itu, skema, indeks, trigger, view, dan constraint dapat dimanfaatkan untuk meningkatkan performa, kemudahan, dan konsistensi
  • Beberapa ide pengembangan tambahan adalah sebagai berikut
    • Menyimpan stack undo/redo otomatis dalam tabel basis data agar pembatalan bisa dilakukan hingga sesi pengeditan sebelumnya
    • Menambahkan pencarian full-text pada satu deck slide atau beberapa deck slide
    • Memecah settings.xml menjadi tabel SQL agar lebih mudah dilihat dan diedit oleh aplikasi lain
    • Memisahkan catatan presenter tiap slide ke tabel terpisah agar mudah diakses oleh aplikasi atau skrip pihak ketiga
    • Mendukung struktur presentasi yang melampaui urutan slide linear sederhana, dengan jalur dan alur alternatif tergantung respons audiens

Keengganan umum terhadap SQLite dan sanggahannya

  • Karena pengalaman dengan basis data SQL enterprise, orang bisa merasa enggan menggunakan SQLite sebagai format file aplikasi
  • Banyak basis data enterprise menyarankan agar string besar atau BLOB tidak dimasukkan ke basis data dan disimpan sebagai file terpisah, tetapi SQLite berbeda
    • Kolom apa pun di SQLite dapat menyimpan string atau BLOB hingga sekitar 1GB
    • Untuk string dan BLOB di bawah 100KB, kinerja I/O lebih baik daripada menyimpannya sebagai file terpisah
  • Ada juga anggapan bahwa semua skema SQL harus mengikuti Bentuk Normal Ketiga (3NF) dan hanya menyimpan tipe primitif kecil
    • Teori relasional memang penting, tetapi dalam format file nyata, menyimpan informasi kompleks seperti XML atau JSON di field teks juga merupakan pilihan yang masuk akal

SQLite sebagai format file aplikasi

  • File basis data SQLite hampir sama ukurannya dengan arsip ZIP yang memuat informasi yang sama, dan dalam beberapa kasus bisa lebih kecil
  • Berkat pembaruan atomik, perubahan kecil dapat dicatat dengan aman ke dokumen, mengurangi I/O disk dan meningkatkan performa File/Save
  • Aplikasi dapat membaca hanya konten yang diperlukan untuk layar pertama sehingga waktu mulai berkurang
  • Hanya konten yang terkait dengan tampilan saat ini yang perlu disimpan di memori, sementara sisanya tetap di disk, sehingga penggunaan memori bisa jauh lebih rendah
  • Skema SQL dapat merepresentasikan informasi secara lebih langsung dan ringkas dibanding struktur key/value seperti ZIP
    • Aksesibilitas untuk aplikasi dan skrip pihak ketiga menjadi lebih baik
    • Fitur lanjutan seperti manajemen versi dokumen bawaan dan pemulihan pekerjaan setelah crash menjadi lebih mudah diimplementasikan
  • OpenDocument sudah merupakan format yang mapan dan dirancang dengan baik, dan karena SQLite hadir setelah OpenDocument, ini bukan kritik terhadap pilihan yang dibuat saat itu
  • Dokumen Application File Format memberikan ide tambahan tentang penggunaan SQLite sebagai format file aplikasi

1 komentar

 
GN⁺ 2023-09-19
Pendapat Hacker News
  • Saya sedang membuat aplikasi yang memakai SQLite sebagai format file
    Karena ingin mempertahankan alur umum di mana file hanya berubah saat pengguna mengedit dokumen lalu menyimpannya, saat membuka file saya menyalinnya ke database :memory:: https://www.sqlite.org/inmemorydb.html
    Pengguna bisa memanipulasinya sesuka hati, dan aplikasi langsung merefleksikannya ke format database tanpa model dokumen terpisah. Saat menyimpan, saya menanganinya dengan menulis kembali ke file database menggunakan VACUUM: https://www.sqlite.org/lang_vacuum.html
    Ini bekerja baik untuk file berukuran wajar, dan di aplikasi saya selalu berada dalam kisaran itu

    • Saya tidak mengerti mengapa memakai database volatil tambahan. Dalam situasi pengguna mengedit file, penulisan bahkan mungkin tidak sampai 1 kali per detik, jadi keuntungan performanya juga tidak besar
      Lebih baik langsung melakukan autosave ke database dan menghapus tombol simpan. Ini tangguh terhadap crash, hanya ada satu database sehingga kode dan bug berkurang, dan penulisan SQLite hanya berhasil atau gagal, tanpa keadaan di tengah. Sebaliknya, seperti dikutip dalam dokumen, VACUUM INTO dapat menghasilkan database keluaran yang tidak lengkap atau rusak jika terjadi penghentian tak terduga atau listrik mati
      Jika memakai SQLite sesuai cara yang memang dimaksudkan, Anda tidak perlu memikirkan bagian ini sepanjang masa pakai SQLite
    • Bertindak seperti aplikasi biasa berarti jika aplikasi mati atau listrik padam, data yang belum disimpan akan hilang
      Jauh lebih baik menyimpan ke lokasi sementara setelah setiap operasi, misalnya tempat seperti ~/.local/share/application/yourapp berdasarkan direktori XDG, lalu ketika pengguna menekan simpan, salin file ke lokasi yang diinginkan. Saat aplikasi dibuka kembali setelah listrik mati, hampir bisa dipulihkan pada titik yang sama, dan mungkin hanya kehilangan beberapa detik terakhir
    • Cara yang lebih sederhana mungkin cukup dengan mengubah database ke mode WAL saat dibuka dan mematikan checkpoint otomatis: https://www.sqlite.org/pragma.html#pragma_wal_autocheckpoint
      Saat pengguna menyimpan, lakukan checkpoint untuk menggabungkan isi WAL ke database utama
    • Menurut dokumen, VACUUM menyalin isi ke file database sementara lalu menimpa file asli, dan saat menimpa ia menggunakan rollback journal atau WAL seperti transaksi biasa. Jadi dibutuhkan ruang kosong sekitar dua kali ukuran asli
      VACUUM INTO menggunakan file yang ditentukan di INTO alih-alih database sementara, dan melewati langkah menyalin kembali ke atas file asli. Yang penting adalah apakah yang sebenarnya dipakai adalah VACUUM yang tahan terhadap pemadaman listrik, atau VACUUM INTO yang tampaknya rentan terhadap pemadaman saat sedang menulis dan bisa merusak jika memakai nama file yang sudah ada
    • Saya pernah memakai pendekatan serupa: menjalankan database di memori sebagai cache, menyimpannya ke disk secara berkala, dan menggunakan backup API: https://www.sqlite.org/backup.html
  • Masalah SQLite adalah ia bukan format file terstandar
    Ia terdokumentasi dengan baik dan dipahami secara luas, tetapi standar ISO tidak mendefinisikan secara rinci cara menafsirkan file SQLite. Implementasi alternatif pun sama
    Zip dan XML memiliki permukaan API yang jauh lebih kecil daripada SQLite. API SQLite melampaui beberapa fungsi C; ia adalah bahasa SQL itu sendiri, dan mengimplementasikan parser SQL, pengoptimal kueri, compiler, mesin virtual bytecode, mesin pencarian teks penuh, dan sebagainya tanpa merusak data adalah pekerjaan yang jauh lebih besar daripada parser XML
    Untuk aplikasi tertutup yang spesifik domain, ketika interoperabilitas atau standardisasi ISO tidak penting, SQLite adalah format file yang bagus, tetapi saya memahami bahwa kekhawatiran semacam itu memang ada pada OpenOffice

    • Agak tidak jelas masalah ini merujuk ke apa. Format file SQLite berada di domain publik, terdokumentasi dengan baik, dan ada parser untuk berbagai bahasa
      Library C SQLite juga berada di domain publik, sumbernya sepenuhnya terbuka, menangani format file tersebut, dan tingkat dokumentasinya lebih tinggi daripada kebanyakan standar ISO. Binding untuk bahasa-bahasa utama juga hampir semuanya ada
      Jika masalahnya adalah format OpenDocument apa pun yang akan disimpan di dalam file SQLite masih harus dibuat dan didokumentasikan, itu hal lain. Standar ISO memang bagus, tetapi jika kita harus menunggu ISO mendefinisikan format file, akan terlalu sedikit hal yang bisa kita gunakan
    • Untuk menjadi format file standar, tidak perlu mengimplementasikan seluruh parser SQL, pengoptimal kueri, compiler, mesin virtual bytecode, dan mesin pencarian teks penuh
      Sama seperti tidak perlu mengimplementasikan semua fitur spreadsheet untuk membaca spreadsheet LibreOffice. Yang dibutuhkan adalah kemampuan merekonstruksi tabel, lalu setelah itu informasi yang diinginkan bisa didapat dengan menelusurinya menggunakan kode imperatif yang ditulis dalam bahasa pilihan
    • Ini juga bukan masalah bagi Library of Congress Amerika Serikat. Library of Congress mendefinisikan SQLite sebagai format penyimpanan yang direkomendasikan untuk dataset, bersama CSV, XML, dan JSON
    • Sepertinya format file dan cara penggunaannya dicampuradukkan. Aplikasi yang memakai format file SQLite cukup menggunakan library SQLite sebagai bagian dari aplikasi
      Mengimplementasikan ulang library itu memang pekerjaan besar, tetapi jenisnya sama seperti mengimplementasikan ulang kode yang menggunakan format file OpenDocument. Format filenya sendiri cukup sederhana
    • Standar tidak selalu diperlukan. Semua interaksi antara aplikasi dan dokumen dilakukan melalui SQL, dan SQL setidaknya sudah distandardisasi pada bagian-bagian penting
      Jika khawatir soal kompatibilitas, buat saja agar dokumen juga bisa diakses melalui database lain seperti MySQL
  • Saya kira jika Audacity mengadopsi SQLite, fungsi penyimpanan file akan menjadi jauh lebih baik, tetapi ternyata dalam praktiknya ada banyak jebakan.
    Di Linux, jika menyimpan sebagai file baru ke mount NTFS yang dibuat dengan /etc/fstab, dimiliki root tetapi bisa ditulis semua pengguna, prosesnya gagal karena alasan seperti kesalahan izin; kalau menyimpan ke file yang sudah ada, berfungsi normal.
    Begitu proyek diedit, file di disk ikut diubah, sehingga jika proyek Audacity dimasukkan ke Git sebagai gumpalan biner, akan muncul diff Git yang tidak perlu. Bahkan setelah disimpan, data lama atau yang sudah dihapus tetap berada di file SQLite sampai jendela proyek ditutup, sehingga bisa masuk ke repositori jika jendela tidak ditutup sebelum commit. Dulu seingat saya file .aup3 harus di-VACUUM secara manual, tetapi sekarang menutup jendela saja sudah cukup. Masih terasa seperti Fast Save di Word 2003.

    • Jika Audacity crash atau berhenti tidak normal, pembersihan sama sekali tidak terjadi, sehingga merepotkan. Dulu, saat proses pemulihan, aplikasi memberi tahu bahwa ada blok yatim dan kita bisa memilih untuk mempertahankan atau menghapusnya.
      Ketika proyek yang semestinya hanya beberapa ratus MB membengkak menjadi beberapa GB dan ruang disk harus dihemat, untuk pekerjaan sederhana satu track, Mix and Render menjadi solusinya. Audionya tidak berubah, tetapi sampah bisa dibersihkan saat menyimpan dan keluar.
      Ini bukan masalah SQLite itu sendiri, melainkan jelas masalah di lapisan aplikasi. Sepertinya Audacity 2 punya konsep ruang kerja sementara, sedangkan Audacity 3 tampaknya memakai file .aup3 itu sendiri sebagai ruang kerja.
      Saya pernah melihat format Audacity 3; data proyek yang dulu setara dengan file .aup disimpan sebagai XML di tabel satu baris, tetapi anehnya tidak ditulis sebagai teks apa adanya, melainkan dienkode dengan coder kamus sederhana. Ini sangat membingungkan. Hal itu membuat interoperabilitas dan inspeksi jauh lebih sulit, sedikit pun tetap merugikan performa, dan penghematan ruangnya mungkin hanya beberapa KB—sekadar galat pembulatan—di antara file audio ratusan MB.
    • Aplikasi harus mereplikasi persis fungsi yang diharapkan pengguna. Semuanya harus disimpan di file sementara, dan file asli hanya boleh ditimpa saat ada tindakan simpan eksplisit.
      Dari sudut pandang Git, lebih menguntungkan memakai format teks yang mudah di-diff dan di-merge. Saya tidak tahu seberapa mudah dump SQLite dalam hal ini.
    • Istri saya memakai Audacity sepanjang hari, dan setiap beberapa hari muncul file SQLite yang rusak. Terjadi kesalahan kunci duplikat, dan saya tidak tahu cara memperbaikinya atau mengimpornya kembali di Audacity.
      Kalau penting, bisa diperbaiki secara manual, tetapi biasanya kalau file itu dibuang, Audacity kembali berjalan.
  • Tulisan yang bagus. Namun saya suka bahwa OpenDocument adalah sekumpulan file XML di dalam arsip Zip.
    Dokumen seperti spreadsheet bisa dibuat dengan cukup mudah tanpa pustaka berat yang memahami format dokumen.
    Ada kalanya pengguna layanan web ingin memakai data yang diekspor sebagai baris tabel di berbagai alat. CSV UTF-8 itu terbuka, konvensional, dan layak dipakai, tetapi siapa pun yang pernah menyediakan CSV kepada pengguna akhir tahu rasa sakit saat tersandung di aplikasi spreadsheet.
    Saya menyimpan contoh spreadsheet sebagai ODS milik OpenDocument dan XLSX milik OOXML, monster XML dari Microsoft, lalu hanya memahami dasar-dasar format XML-nya. Arsip Zip saya kecilkan hingga hanya menyisakan elemen wajib, saya tandai tempat konten akan dimasukkan, lalu saat ada permintaan, dibuat file spreadsheet baru. Sekarang data yang sama bisa dikeluarkan sebagai CSV, ODS, XLSX, dan JSON.
    Ini juga mungkin dengan SQLite, tetapi akan sedikit lebih rumit dan pengembangannya lebih lambat. Kemampuan membuat dokumen templat dengan suite office lalu menggali XML dari file simpanannya adalah fitur yang niche tetapi bagus.
    Masalahnya terutama Excel pada locale seperti nl_NL berperilaku seolah-olah pemisah kolom file CSV di-hardcode sebagai titik koma. Itu karena Microsoft secara terkenal memutuskan bahwa orang Belanda tidak memakai koma dalam file comma separated values.

    • Perilaku itu tidak sepenuhnya hardcoded; ia bergantung pada nilai localeconv()->decimal_point. Jika nilainya ,, Excel memakai titik koma baik untuk file CSV maupun bahasa ekspresi rumus.
      Dulu opsi ini bisa diatur saat membuka CSV/TXT di Excel, dan di LibreOffice masih bisa, tetapi dalam proses penyederhanaan UI secara umum, opsi tersebut dipindahkan ke suatu tempat di menu/tab ribbon Data. Anda harus membuka workbook baru dan mencari opsi yang benar; kalau ingin menghemat waktu, lebih baik pakai LibreOffice.
  • Bagian ini benar-benar mengejutkan. Sulit dipercaya bahwa query bersarang tidak diperlukan.
    SELECT manifest, versionId, max(checkinTime) FROM version;
    Katanya di SQLite, query kedua yang memakai max(checkinTime) ini benar-benar bekerja dengan baik dan mengembalikan jawaban yang terdefinisi. Di mesin database SQL lain, ini mengembalikan jawaban yang tidak terdefinisi atau error, tetapi di SQLite ia mengembalikan manifest dan versionId dari entri yang memiliki checkinTime maksimum.

    • Ini mungkin fitur yang berguna, tetapi jujur saja saya tidak akan mengharapkan query seperti itu mengembalikan hasil seperti itu.
      Dalam kasus ini, query bersarang tidak diperlukan; cukup urutkan berdasarkan checkinTime lalu batasi satu baris: select manifest, versionId, checkinTime from version order by checkinTime desc limit 1
      Setidaknya ini akan bekerja di SQLite dan PostgreSQL. Seingat saya, di Oracle harus memakai where rownum=1, jadi perlu query bersarang.
    • Ini kira-kira bisa dilihat sebagai bentuk singkat dari GROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1 atau CTE yang mencari checkinTime maksimum lalu melakukan join.
      Namun jika ada beberapa baris dengan checkinTime maksimum yang sama, ada unsur acak sehingga bisa menjadi senjata makan tuan; karena itu saya tidak terlalu memakai fitur khas SQLite3 ini. Untuk memilih baris terbaik secara deterministik, diperlukan cara eksplisit yang mirip dengan di atas.
    • Ini bukan perilaku yang umumnya diharapkan dalam SQL, tetapi SQLite sering keluar dari ekspektasi. Dalam kasus ini nyaman, tetapi nonstandar.
    • Sepertinya efek samping implementasi yang praktis kemudian menjadi perilaku resmi. Mirip urutan kunci dictionary Python.
      Di Postgres, hal serupa bisa dilakukan dengan query DISTINCT ON. Ini salah satu pekerjaan yang tampak sederhana di SQL tetapi terasa paling sulit bagi saya.
    • Cukup mengejutkan bahwa ini bisa diklaim sebagai perilaku yang terdefinisi. Alasannya, manifest dan versionId tidak bergantung secara fungsional pada max(checkinTime).
      Misalnya, bisa saja ada dua baris dengan nilai checkinTime yang sama, dan nilai itu adalah maksimum.
  • Pernah merilis produk yang menggunakan SQLite sekaligus file XML
    Salah satu perbaikannya adalah memindahkan beberapa tabel dengan data sedikit ke file XML. Karena filenya kecil dan hampir tidak digunakan, lapisan akses data dan diagnostik menjadi lebih sederhana, dan dibuat sebagai XML dengan indentasi tab multi-baris
    Meminta staf teknis yang harus mendiagnosis produk untuk membuka basis data SQLite terasa cukup memberatkan. Namun untuk bagian utama produk, SQLite jauh lebih unggul daripada file XML. Versi sebelumnya memakai file XML, tetapi file XML tidak punya cara pembaruan inkremental yang baik sehingga ada masalah skalabilitas
    Keunggulan XML sebagai format yang mudah dibaca manusia hanya benar-benar bekerja ketika filenya kecil dan desain skemanya disesuaikan agar XML mudah dibaca. Keharusan menulis ulang seluruh file XML setiap kali, serta kompleksitas yang muncul saat fitur bertambah, dengan cepat mengikis keunggulan terbesar XML
    Kasus pengguna biasa perlu mengutak-atik langsung bagian dalam dokumen office cukup jarang, sehingga mempelajari cara memakai pembaca SQLite masih merupakan hambatan masuk yang dapat diterima. Keterbatasan XML+Zip untuk penulisan acak di tengah file tidak bisa diatasi bahkan oleh Hukum Moore

    • Saya tidak begitu paham bagaimana format native SQLite bisa mencapai ukuran yang mirip dengan XML+Zip tanpa Zip. Saya penasaran apakah field TEXT atau BLOB SQLite dikompresi, atau apakah diasumsikan pemanggil mengompresi BLOB sebelum menulisnya
  • ODT dirancang dengan standardisasi dalam pikiran. Format sebelumnya juga sangat mirip, tetapi sangat bergantung pada standar yang sudah ada seperti XHTML, SVG, dan CSS
    Jika tidak bisa merujuk standar yang sudah ada, spesifikasi ODT sendiri tiba-tiba akan menjadi sangat besar. Upaya memperbarui standarnya juga tampak cukup besar, dan dalam beberapa tahun terakhir tidak banyak kemajuan
    Secara realistis, format SQLite mungkin bisa disediakan sebagai opsi, tetapi kapal format dokumen office tampaknya sudah terlanjur berlayar. Meski begitu, ini menjadi dasar yang bagus untuk argumen agar spesifikasi SQLite disusun sebagai standar resmi

    • Spesifikasinya ditulis sangat ringkas dan terutama hanya menetapkan sintaks, bukan efek dan perilaku, tetapi tetap sangat tebal, 840 halaman
      Terlepas dari beberapa kekurangan, misalnya masalah atribut ooo:rsid yang membuat style lokal dan rentang teks membengkak, spreadsheet yang tidak sparse, serta mekanisme aneh untuk styling tabel, ini adalah markup yang dirancang sangat baik untuk data dokumen semacam ini. Keseimbangannya bagus antara markup semantik dan ekspresi yang benar-benar ingin dibuat pengguna
      Sebaliknya, Office OpenXML memiliki tag kosong untuk pemformatan yang bersifat stateful, dan di DOCX tag itu men-toggle apakah teks berikutnya akan ditampilkan tebal
  • Menggabungkan format file dengan SQLite terasa ada yang salah
    SQLite memang bagus, tetapi cukup unik di area ini. Karena ia melakukan banyak hal, sulit untuk mereplikasinya begitu saja
    Namun dalam kasus ini, apakah fitur sebanyak itu diperlukan? Tidak. Yang dibutuhkan hanya semantik transaksi dasar yang aman dan kemampuan menyimpan struktur tabel sederhana; tidak perlu seluruh standar SQL atau bahkan pengoptimal kueri
    Mungkin ada format file yang lebih baik, tetapi akan lebih baik jika itu adalah format yang terpisah dari SQLite

    • Saya tidak paham mengapa tidak bisa: https://www.sqlite.org/appfileformat.html
      Ukurannya di bawah 1 MB dan https://sqlite.org/footprint.html, bahkan dengan semua fitur diaktifkan hanya 750 KB: https://www.sqlite.org/about.html
      Banyak fitur bisa dibuang saat kompilasi, dan tampaknya ada opsi untuk menyesuaikan atau mengurangi query planner: https://www.sqlite.org/compile.html
      Selain itu, ada juga ungkapan “SQLite tidak bersaing dengan database client/server. SQLite bersaing dengan fopen()”: https://www.sqlite.org/whentouse.html
      Pada akhirnya, yang dibutuhkan bukan databasenya sendiri, melainkan library yang menyediakan API dan perilaku database
    • Sisi transaksi ternyata lebih sulit dari perkiraan, terutama untuk akses file secara bersamaan. Saat itu, penanganan SQLITE_BUSY cukup merepotkan
      Saya tahu kegagalan serialisasi memang diharapkan dalam pemrosesan transaksi, tetapi di SQLite sulit membedakan kegagalan terus-menerus seperti deadlock sendiri dengan masalah update bersamaan yang sementara. Jika kegagalannya sementara, cukup jalankan ulang closure yang mendefinisikan pekerjaan transaksi; tetapi jika kegagalannya terus-menerus, itu tidak ada gunanya
      Sebagian masalahnya adalah sqlite3_stmt menggabungkan sifat prepared statement dan result set sekaligus. Karena ingin men-cache bytecode yang sudah dikompilasi, objek ini dipegang cukup lama; jika berhenti di tengah iterasi, ia bisa sedang memegang lock pada saat itu. Ini dapat menyebabkan kegagalan upgrade lock yang tidak terduga
      Pada akhirnya, saya menghilangkan masalah itu dengan membuat pelaporan error terperinci menggunakan sqlite3_next_stmt, sqlite3_stmt_busy, dan sqlite3_sql. Meski hanya untuk penggunaan pribadi, kode retry transaksi saya penuh dengan logging opsional dan komentar. Logika retry transaksi untuk PostgreSQL jauh lebih mudah
      Hal lain yang mengejutkan adalah isi dokumentasi bahwa dalam mode WAL dengan synchronous=NORMAL, transaksi yang sudah di-commit dapat di-rollback setelah kehilangan daya atau crash sistem: https://sqlite.org/pragma.html#pragma_synchronous
      Itu tidak relevan untuk aplikasi saya
    • SQLite sudah digunakan persis untuk tujuan seperti ini. Ia digunakan sebagai OGC GeoPackage, dan dataset Mapbox/Maptiler juga menggunakannya
    • Beberapa format dirancang terutama untuk pertukaran. Argumen dari pihak SQLite adalah agar pemilik aplikasi memaksakan format SQLite kepada pengguna sehingga secara de facto menjadi standar, sementara pekerjaan untuk menjadikannya standar legal tidak ada
      Jika Richard Hipp dan perusahaannya bisa menunjukkan standar SQLite ISO/IEC/ANSI/ETSI yang tidak akan pernah mereka tinggalkan, tinjauan hukum bahwa tidak ada paten yang akan berdampak, serta beberapa implementasi SQLite kompatibel yang tetap mempertahankan semua keunggulannya, barulah kita bisa membahas rekomendasi sebagai format file. Jika tidak, itu berarti menggantungkan diri kuat-kuat pada implementasi dari satu sumber dan membebankannya juga kepada pengguna
      XML, ASN.1, dan JFIF adalah standar resmi, dan ZIP juga merupakan standar resmi yang diadopsi sebagai ISO/IEC 21320-1:2015 dalam proses standardisasi OpenDocument
      Hal terpenting dari dokumen adalah semua pihak lain harus bisa membacanya. Mengurangi waktu update disk adalah hal sekunder. Kita tidak boleh tidak belajar dari upaya Microsoft memelintir badan standardisasi untuk mempertahankan lock-in: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
    • Kalau melihat aplikasi-aplikasi Apple, sebagian besar memakai SQLite sebagai format penyimpanan. iMovie, iPhoto, Voice recording, dan lainnya demikian; Docker juga begitu
      Pilihan itu tidak mungkin sepenuhnya keliru
  • Contoh lain adalah tile peta raster. Pada dasarnya ini adalah gambar-gambar persegi kecil yang jumlahnya bisa mencapai jutaan
    Saya mencoba Zip, tar, sistem file, dan SQLite, dan SQLite yang paling cepat sekaligus paling kecil, bahkan lebih baik daripada arsip biasa tanpa overhead

    • Banyak sistem file bermasalah ketika ada lebih dari puluhan ribu file dalam satu direktori, dan pada tile peta justru situasinya persis seperti itu. Tidak mengherankan jika SQLite lebih cepat
    • Jika SQLite lebih cepat, masalahnya ada pada library Zip yang digunakan
      SQLite punya kelemahan besar. BLOB yang diperoleh dari database tidak bisa di-mmap, sehingga harus disalin ke tempat lain. File Zip, jika tidak dikompresi atau dikompresi dengan encoding khusus seperti PVRTC, bisa langsung di-mmap apa adanya
  • OpenDocument adalah gambar terkompresi dan XML. Artinya, pada akhirnya seluruh format harus di-parse dan dimuat ke memori
    Saya tidak begitu paham bagaimana SQLite memperbaiki hal ini. XML memang tidak ideal, tetapi karena dikompresi dengan Zip, penalti ukurannya juga tidak besar
    Semua keunggulan yang disebutkan dalam tulisan SQLite bisa diimplementasikan jika SQLite dipakai sebagai model runtime dokumen. Itu bisa dilakukan baik di disk maupun di memori, tetapi SQLite tidak harus menjadi format transmisi
    Justru SQLite bisa menjadi lebih besar daripada format saat ini. Setelah mengalami perubahan, bisa muncul ruang yang tidak terpakai, terfragmentasi, dan menjadi sparse. Jika harus dioptimalkan setiap kali, keunggulan seperti penyimpanan cepat juga hilang
    Format yang membutuhkan pembaruan delta, pencarian indeks cepat, dan tidak boleh memuat seluruh file ke memori memang banyak menggunakan SQLite sebagai format file. Namun menurut saya, dalam skenario hipotetis ini OpenDocument adalah contoh yang kurang tepat untuk dijadikan target SQLite

    • XML dan Zip tidak menangani pembaruan inkremental dengan baik. Saat menyimpan, seluruh file aplikasi harus ditulis, dan jika terjadi masalah saat penulisan, bisa terjadi kerusakan
      Jika SQLite digunakan sebagai format disk dan aplikasinya diimplementasikan dengan benar, hasil akhirnya bisa tidak berada dalam keadaan rusak
      XML/Zip juga mungkin bisa mencapai hal serupa dengan trik rename, tetapi SQLite menyediakannya dalam satu file disk. Jika SQLite sudah dipakai sebagai model memori, tidak ada alasan untuk tidak memakainya juga sebagai format disk/transmisi. Pada titik itu, biayanya hampir gratis
      Masalah ukuran file tampaknya bisa ditangani dengan VACUUM