Bluesky Bermigrasi ke SQLite Tenant Tunggal
(github.com/bluesky-social)- PR refactoring PDS untuk Bluesky atproto #1705 mengubah PDS agar menggunakan datastore SQLite tenant tunggal, serta menyimpan repo per pengguna dan status akun privat masing-masing ke file SQLite tersendiri
- DB pengguna disimpan dengan struktur path
/${dbDirectory}/${sha256Hex(did).slice(0,2)}/${did}, dan kunci tanda tangan setiap repo disimpan berdampingan dengan file SQLite tersebut - Abstraksi akses data pengguna yang lama digantikan oleh ActorStore; karena SQLite tidak mendukung transaksi bersamaan, operasi tulis harus secara eksplisit membuat transaksi dengan store
- Handle file DB yang terbuka dan kunci tanda tangan dikelola dengan LRUCache, mempertahankan hingga 30 ribu handle file terbuka dan 30 ribu kunci di memori, serta menutup handle file ketika DB terdepak dari cache
- Untuk manajemen status layanan, diperkenalkan 3 DB SQLite terpisah dan dijalankan dalam mode WAL agar pembacaan bersamaan dan replikasi streaming dimungkinkan; distribusi PDS direncanakan menyertakan Litestream atau alat serupa
Perubahan inti PR
- PR #1705 merefaktor PDS berbasis datastore SQLite tenant tunggal
- Setiap pengguna memiliki file SQLite khusus sendiri, yang menyimpan repo pengguna tersebut dan status akun privatnya
- DB pengguna disimpan pada path hierarkis menggunakan hash DID
- Format path:
/${dbDirectory}/${sha256Hex(did).slice(0,2)}/${did}
- Format path:
- repo signing key setiap repo disimpan di lokasi yang sama dengan file SQLite
ActorStore dan model transaksi
- Abstraksi akses data pengguna berubah dari “services” yang lama menjadi ActorStore
- Perbedaan utama ActorStore adalah kelas untuk membaca dan menulis dipisahkan
- Karena SQLite tidak mendukung transaksi bersamaan, operasi tulis harus secara jelas membuat transaksi dengan store
- Log commit mencakup pengerjaan ulang reader dan transactor, penanganan race pada transaksi actor store, perapihan antarmuka store, dan lainnya
Manajemen cache dan handle file
- LRUCache dipertahankan untuk kunci tanda tangan dan database
- Batas yang ditetapkan adalah sebagai berikut
- Maksimum handle file terbuka 30 ribu
- Maksimum kunci yang dipertahankan di memori 30 ribu
- Ketika database terdepak dari cache, handle file ditutup
- Commit terkait mencakup
actor store in lru cachedanfix open handles
3 DB SQLite untuk status layanan
- Selain DB per pengguna, diperkenalkan 3 database SQLite terpisah untuk manajemen status layanan
- service DB: mengelola informasi akun, kode undangan, refresh token, dan lainnya
- did cache DB: hanya berisi satu tabel untuk caching DID resolution
- sequencer DB: hanya berisi satu tabel yang mengelola urutan semua pembaruan repo dalam satu layanan
- Setiap file SQLite dijalankan dalam WAL mode
- Tujuan WAL mode adalah memungkinkan pembacaan bersamaan dan replikasi streaming
- Distribusi PDS direncanakan menyertakan Litestream atau alat serupa
Status review dan merge
- PR ini terdiri dari total 143 commit dan di-merge dari branch
pds-sqlite-refactorke branchpds-v2 - Tanggal merge adalah 1 November 2023, dengan commit merge
8449ceb - Reviewer devinivy meninggalkan beberapa catatan dan komentar sebelum menyetujui perubahan
- devinivy menilai refactoring ini memiliki “banyak penyederhanaan yang sangat baik” dan secara keseluruhan terasa rapi
- Setelah merge, branch
pds-sqlite-refactordihapus
Pertanyaan setelahnya
- Pada 28 Februari 2025, npetrangelo melihat skala perubahan PR ini dan meminta ringkasan trade-off antara arsitektur Postgres sebelumnya dan arsitektur SQLite yang diperkenalkan oleh PR ini
- Teks yang diberikan tidak menyertakan jawaban dari pihak Bluesky atas pertanyaan tersebut
1 komentar
Komentar Hacker News
Saya suka SQLite, tetapi pendekatan memisahkan skema atau database untuk tiap tenant umumnya punya banyak kesulitan
Jika memakai keamanan tingkat baris (RLS) pada instance bersama, seluruh rollback bisa dilakukan bila migrasi gagal. Namun pada skema per tenant, jika migrasi data gagal karena data yang tidak terduga, pengguna akan tertinggal di versi skema yang berbeda sampai penyebabnya ditemukan
Saat mencapai skala sharding, hal serupa mungkin tetap terjadi, tetapi sebelum itu satu database tunggal adalah yang paling mudah, dan nantinya mungkin perlu menggabungkan data atau memindahkan kepemilikan resource secara atomik
Saya tidak menentang konfigurasi ini dan memang ada tempat penggunaannya, tetapi di perusahaan kami sedang bergerak secepat mungkin keluar dari skema per tenant. Kalau tidak diinvestasikan dengan benar, masalahnya terlalu banyak, dan menurut saya jarang sekali orang sudah siap untuk itu saat pertama kali memunculkan idenya
Yang menarik, sekitar 10 tahun lalu aplikasi dimulai dengan SQLite per tenant, lalu pindah ke skema per tenant di PostgreSQL, dan sekarang sedang menuju satu skema tunggal dengan RLS, jadi arahnya benar-benar berlawanan
Dari sudut pandang pernah menangani database raksasa di produksi, saya tidak ingin melakukannya lagi
Begitu bebannya cukup besar, setiap perubahan menjadi berisiko, karena tidak mungkin menguji semua kondisi ekstrem performa secara menyeluruh
Pengguna tier gratis menemukan jalur kode tanpa indeks lalu merusak produksi juga pola yang umum
Sebagian pengguna tertinggal di versi skema berbeda karena kegagalan migrasi data mungkin bukan masalah besar
Jika layanannya sebesar dan sekompleks itu, biasanya upgrade skema dilakukan bertahap: 1. membuat kode kompatibel dengan skema masa depan, 2. memigrasikan data, 3. menghapus dukungan skema lama
Jadi biasanya harus aman beroperasi lama dalam kondisi antara tahap 1 dan 2. Tentu bug baru adalah pengecualian, tetapi dari sudut pandang operasional, selama prosedur seperti ini dipakai, sistem yang kembali ke keadaan perantara migrasi juga saya anggap baik-baik saja
Jika pelanggan produk kurang dari 100 orang, berada di versi skema berbeda untuk tiap pengguna justru bisa lebih baik
Tiap pelanggan bisa memiliki jadwal dan kebutuhan upgrade yang berbeda, dan saya juga tahu bisnis yang melakukan pekerjaan kustom untuk sebagian pelanggan sampai-sampai pada dasarnya tidak menjalankan kode yang sama
Pada akhirnya itu bergantung pada struktur bisnis
Secara adil, 10 tahun lalu RLS belum ada. Fitur itu muncul di PostgreSQL 9.5 pada 2016
https://blog.turso.tech/introducing-embedded-replicas-deploy...
https://electric-sql.com/
Saya tidak tahu apa maksud pernyataan “SQLite tidak mendukung transaksi konkuren”
Setahu saya itu didukung selama file
.dbtidak diakses melalui file sharing seperti UNC atau NFS: https://www.sqlite.org/wal.htmlSaya pernah memakainya untuk membaca dan memperbarui database dari beberapa thread/proses di mesin yang sama, dan jika butuh tampilan yang konsisten atau tidak ingin menahan transaksi terlalu lama, snapshot juga bisa dibuat dengan sqlite backup API
Mungkin ada sesuatu yang saya lewatkan, dan saya tidak sepenuhnya yakin karena sudah beberapa tahun tidak menyentuh SQLite
Ternyata tidak. Saya keliru. Sebenarnya lebih dekat ke multi-read, single-write
Sepertinya selama ini saya hanya mengasumsikannya dan tidak memeriksanya cukup teliti. Namun sebagian besar database yang saya buat dengan SQLite memang lebih banyak membaca daripada menulis
Saya koreksi
Kalau menunggu, hctree [1] akan menjadi stabil, dan kita akan bisa memilih antara mekanisme backend tradisional dan backend baru yang diimplementasikan dengan dukungan konkurensi
[1] https://sqlite.org/hctree/doc/hctree/doc/hctree/index.html
Menurut dokumentasi, penulis hanya menambahkan konten baru ke akhir file WAL sehingga baca dan tulis bisa berjalan bersamaan, tetapi karena hanya ada satu file WAL, hanya ada satu penulis yang bisa menulis pada saat yang sama
Sepertinya yang dimaksud tulisan asli adalah operasi update harus dijalankan secara berurutan
Jika traffic rendah, ini berjalan, tetapi jika transaksi membesar atau jumlah penulisan konkuren meningkat, meski WAL diaktifkan, pada titik tertentu akan muncul masalah database locked
Sampai batas tertentu bisa diakali di level aplikasi, tetapi umumnya jika sudah mencapai titik itu, sebaiknya serius mempertimbangkan backend database lain
Setidaknya saat terakhir saya mengecek, kemungkinan maksudnya adalah tidak ada penguncian tingkat baris, dan penguncian tingkat tabel pun sangat terbatas
Menurut dokumentasi, penulis tetap mengambil lock atas seluruh database
Menarik, dan saya suka strategi 1:1 antara 1 pengguna dan 1 database
Namun saya penasaran bagaimana data yang perlu agregasi antar pengguna ditangani. Jika saya berlangganan pengguna lain dan pengguna itu memposting tulisan, bagaimana database saya diperbarui dengan tulisan baru itu? Atau apakah ini hanya untuk data persisten seperti data profil atau relasi follow, sementara data interaksi seperti feed ditangani secara terpisah?
Saya juga suka bahwa “connection pooling” hanyalah membatasi jumlah handle terbuka dengan cache LRU. Menarik juga bahwa karena tiap koneksi DB bersifat single-thread, konkurensi ditangani di tingkat tenancy, bukan di tingkat koneksi
Sepertinya rate limiting per database juga bisa dengan mudah ditambahkan di atas ini untuk mencegah penyalahgunaan oleh pengguna tertentu
Saya juga penasaran apakah ada cara sederhana untuk mengonfigurasi Litestream bagi jumlah database yang arbitrer
Selalu menyenangkan melihat adopsi SQLite/Litestream di server makin meningkat. Kami juga memakainya saat membuat aplikasi baru
SQLite + Litestream adalah pilihan yang lebih baik untuk database tenant, dan biaya replikasi serta backup ke S3/R2 jauh lebih murah daripada database terkelola cloud yang mahal [1]
Hingga 3900% lebih murah dibanding SQLServer di Azure
[1] https://docs.servicestack.net/ormlite/litestream
Tidak mengerti apa maksudnya 3900% lebih murah
Di tempat kerja fintech sebelumnya, perusahaan menyimpan akun pelanggan sebagai file sqlite3 terenkripsi di blob storage, dan itu cukup cocok dengan pola aksesnya
Sekilas dari luar, ini terlihat seperti kombinasi antara yang terburuk dan yang mengerikan
Semoga ada yang menulis artikel bagus yang menjelaskan kelebihannya dengan angka nyata dan menganalisis kekurangan yang diperkirakan. Kalau dipelajari dengan benar, ini bisa jadi topik yang sangat menarik
Sekilas, terutama jika diasumsikan sedang membuat sistem terdistribusi yang akan dijalankan dan dideploy oleh banyak pengguna yang bukan administrator sistem profesional, ini terlihat sebagai pilihan yang cukup masuk akal
Sepertinya memang itu juga tujuannya, dan saya menduga tujuan desainnya adalah menghindari kebutuhan untuk menyiapkan, mengonfigurasi, dan mengelola database tambahan atau server lain
Semoga ada orang yang lebih memahami Bluesky menjelaskan data apa yang disimpan di SQLite dan data apa yang tidak
Saya berasumsi ini bukan hal seperti pesan antar pengguna
Bayangkan email. Jika Anda mengirim email dan men-CC lima orang, tujuh orang masing-masing menyimpan salinan email yang sama di server email mereka sendiri
Jadi strukturnya bukan ada database pusat yang menyimpan satu email lalu dirujuk oleh orang lain
Sharding database relasional pada dasarnya juga bekerja seperti ini
Denormalisasi data seperti ini hampir wajib ketika aplikasi makin besar, terutama pada aplikasi many-to-many dengan rasio baca terhadap tulis yang tinggi
Jika rasio baca terhadap tulis rendah, struktur database relasional dengan satu master dan beberapa slave pun bisa menangani jumlah request dan data yang mengejutkan besarnya
Saat ini Bluesky pada dasarnya meng-host sendiri satu-satunya PDS, tetapi tujuan akhirnya adalah setiap pengguna akhir memiliki PDS-nya sendiri
Inrupt/SOLID menyebut konsep ini “pod”
Dalam praktiknya, kemarin mereka meng-onboarding PDS produksi kedua, jadi ada kemajuan
Yang ada hanya pesan publik yang disiarkan ke seluruh dunia
Saya belum menelusuri apakah ada rencana untuk pesan langsung
Kenapa pengguna di-hash dengan sha256 lalu dibagi ke direktori target dua karakter?
Bukankah md5 jauh lebih cepat dan menyelesaikan masalah yang sama?
Nilai lebih besarnya adalah tidak perlu menjawab pertanyaan “kenapa memakai hash yang tidak aman”, sekaligus menghilangkan atau setidaknya meminimalkan satu kelas potensi masalah keamanan
Atau mungkin seperti saya, mereka terkubur oleh tool keamanan perusahaan, sehingga tidak ingin membuat pengecualian tersendiri untuk setiap penggunaan md5
Kalau tidak membutuhkan hash yang aman, ada banyak hash non-kriptografis yang cepat
Apakah Bluesky masih berbasis undangan?
Itu cara untuk membatasi pertumbuhan sementara mereka menskalakan sistem dari sisi backend dan pencegahan penyalahgunaan
Ada antrean khusus untuk developer, dan Anda bisa mendapatkan akses cukup cepat: https://atproto.com/blog/call-for-developers