- Tiga tahun lalu, Notion berhasil meningkatkan kecepatan aplikasi Notion untuk Mac dan Windows dengan menyimpan data di cache pada klien menggunakan database SQLite
- Kali ini, peningkatan yang sama juga dapat diberikan kepada pengguna yang mengakses Notion melalui browser
- Artikel ini membahas secara mendalam bagaimana Notion meningkatkan performa di browser dengan menggunakan implementasi
sqlite3 berbasis WebAssembly(WASM)
- Dengan menggunakan SQLite, waktu navigasi halaman meningkat 20% di semua browser modern
- Perbedaannya terutama lebih terasa bagi pengguna yang waktu respons API-nya lambat karena faktor eksternal seperti koneksi internet
- Sebagai contoh, waktu navigasi halaman menjadi 28% lebih cepat untuk pengguna di Australia, 31% untuk pengguna di Tiongkok, dan 33% untuk pengguna di India
Teknologi inti: OPFS dan Web Workers
- Library WASM SQLite menggunakan API browser modern bernama Origin Private File System(OPFS) untuk mempertahankan data antar sesi
- OPFS hanya dapat digunakan di Web Workers
- Web Worker dapat dianggap sebagai kode yang berjalan di thread terpisah, berbeda dari thread utama tempat sebagian besar JavaScript di browser dijalankan
- Notion dibundel dengan Webpack, yang menyediakan sintaks yang mudah digunakan untuk memuat Web Worker
- Notion menyiapkan Web Worker agar dapat membuat file database SQLite atau memuat file yang sudah ada menggunakan OPFS, lalu menjalankan kode caching yang sudah ada di Web Worker tersebut
- Library Comlink digunakan untuk memudahkan pengelolaan pengiriman pesan antara thread utama dan Worker
Pendekatan berbasis SharedWorker
- Arsitektur akhirnya didasarkan pada solusi baru yang diajukan Roy Hashimoto dalam diskusi GitHub
- Pendekatan ini memungkinkan hanya satu tab yang mengakses SQLite pada satu waktu, sambil tetap memungkinkan tab lain menjalankan kueri SQLite
- Bagaimana arsitektur baru ini bekerja?
- Singkatnya, setiap tab memiliki Web Worker khusus yang dapat menulis ke SQLite
- Namun, dalam praktiknya hanya satu tab yang benar-benar dapat menggunakan Web Worker tersebut
- SharedWorker bertugas mengelola tab mana yang menjadi "tab aktif"
- Jika tab aktif ditutup, SharedWorker tahu bahwa ia harus memilih tab aktif yang baru
- Untuk menjalankan kueri SQLite, thread utama di setiap tab mengirim kueri tersebut ke SharedWorker, lalu SharedWorker mengarahkannya ke Worker khusus milik tab aktif
- Tab dapat menjalankan kueri SQLite sebanyak yang diinginkan secara bersamaan, dan semuanya selalu diarahkan ke satu tab aktif
- Setiap Web Worker mengakses database SQLite menggunakan implementasi OPFS SyncAccessHandle Pool VFS yang berfungsi di semua browser utama
Mengapa pendekatan yang lebih sederhana tidak berhasil
- Sebelum membangun arsitektur yang dijelaskan di atas, Notion mencoba menjalankan WASM SQLite dengan pendekatan yang lebih sederhana: setiap tab memiliki Web Worker khusus, dan masing-masing Web Worker menulis ke database SQLite
- Namun, tidak ada satu pun yang cukup memenuhi kebutuhan Notion untuk digunakan apa adanya
Hambatan #1: isolasi lintas origin
- Untuk menggunakan OPFS via
sqlite3_vfs, situs harus berada dalam keadaan "isolasi lintas origin"
- Untuk menambahkan isolasi lintas origin ke halaman, perlu menetapkan beberapa header keamanan yang membatasi skrip yang boleh dimuat
- Menetapkan header tersebut bisa menjadi pekerjaan yang cukup besar
- Notion bergantung pada banyak skrip pihak ketiga untuk menjalankan berbagai fungsi infrastruktur web, sehingga untuk mencapai isolasi lintas origin sepenuhnya, mereka harus meminta setiap vendor menetapkan header baru dan mengubah cara kerja iframe — sesuatu yang secara realistis sulit dipenuhi
- Dalam pengujian, Notion tetap bisa memperoleh data performa penting dengan menyediakan varian ini kepada sebagian pengguna melalui Origin Trials untuk SharedArrayBuffer yang tersedia di browser Chrome dan Edge
- Dengan Origin Trials ini, persyaratan isolasi lintas origin dapat dilewati untuk sementara
Hambatan #2: masalah korupsi
- Ketika OPFS via
sqlite3_vfs diberikan kepada sejumlah kecil pengguna, bug serius mulai muncul pada sebagian pengguna
- Pengguna ini melihat data yang salah di halaman
- Misalnya, komentar yang ditugaskan ke rekan kerja yang salah, atau tautan ke halaman baru yang pratinjaunya ternyata berasal dari halaman yang sepenuhnya berbeda
- Saat file database milik pengguna yang terdampak diperiksa, ada pola yang menunjukkan bahwa database SQLite telah rusak dalam suatu bentuk
- Memilih baris dari tabel tertentu menghasilkan error, dan ketika baris itu sendiri diperiksa, ditemukan masalah konsistensi data seperti beberapa baris dengan ID yang sama tetapi isi berbeda
- Mengenai bagaimana database SQLite bisa sampai pada kondisi itu, Notion menduga penyebabnya adalah masalah konkurensi
- Karena beberapa tab terbuka, dan setiap tab memiliki Web Worker khusus dengan koneksi aktif ke database SQLite
- Aplikasi Notion sering menulis ke cache setiap kali menerima pembaruan dari server, artinya beberapa tab dapat menulis ke file yang sama secara bersamaan
- Notion sebenarnya sudah menggunakan pendekatan transaksi untuk membatch kueri SQLite bersama-sama, tetapi mereka sangat menduga korupsi terjadi karena kurangnya penanganan konkurensi di sisi API OPFS
- Karena itu, mereka mulai mencatat error korupsi dan mencoba beberapa pendekatan tambal sulam seperti menambahkan Web Locks dan hanya mengizinkan tab yang sedang fokus untuk menulis ke SQLite
- Penyesuaian ini menurunkan tingkat korupsi, tetapi masih belum cukup untuk menyalakan kembali fitur tersebut di traffic produksi
- Meski begitu, hal ini membantu memastikan bahwa masalah konkurensi berkontribusi besar terhadap korupsi
- Masalah ini tidak terjadi di aplikasi desktop Notion
- Di platform tersebut, hanya ada satu proses induk yang menulis ke SQLite
- Pengguna bisa membuka sebanyak mungkin tab di aplikasi, tetapi selalu hanya ada satu thread yang mengakses file database
Hambatan #3: alternatif hanya bisa berjalan di satu tab pada satu waktu
- Notion juga mengevaluasi varian OPFS SyncAccessHandle Pool VFS
- Varian ini tidak memerlukan SharedArrayBuffer, sehingga dapat digunakan di Safari, Firefox, dan browser lain yang tidak memiliki Origin Trial untuk SharedArrayBuffer
- Kekurangan varian ini adalah ia hanya dapat berjalan di satu tab pada satu waktu
- Jika tab berikutnya mencoba membuka database SQLite, hasilnya hanya error
- Di satu sisi, ini berarti OPFS SyncAccessHandle Pool VFS tidak memiliki masalah konkurensi seperti pada varian OPFS via
sqlite3_vfs
- Hal ini dikonfirmasi karena tidak ditemukan masalah korupsi saat varian tersebut diberikan kepada sejumlah kecil pengguna
- Di sisi lain, Notion ingin semua tab pengguna mendapatkan manfaat caching, sehingga varian ini tidak bisa dirilis begitu saja
Menyelesaikan masalah
- Fakta bahwa tidak ada satu pun varian yang bisa digunakan apa adanya menjadi pemicu dibangunnya arsitektur SharedWorker yang dijelaskan di atas
- Arsitektur ini kompatibel dengan salah satu dari dua varian SQLite tersebut
- Saat menggunakan varian OPFS via
sqlite3_vfs, masalah korupsi dapat dihindari karena hanya satu tab yang menulis pada satu waktu
- Saat menggunakan varian OPFS SyncAccessHandle Pool VFS, SharedWorker memungkinkan caching tetap tersedia di semua tab
- Setelah dipastikan bahwa arsitektur ini bekerja pada kedua varian, menunjukkan peningkatan performa yang terlihat pada metrik, dan tidak menimbulkan masalah korupsi, tibalah saatnya membuat pilihan akhir tentang varian mana yang akan digunakan
- Notion memilih OPFS SyncAccessHandle Pool VFS karena tidak memiliki persyaratan isolasi lintas origin, sehingga tidak menghalangi peluncuran ke browser selain Chrome dan Edge
Mengurangi penurunan performa
- Ketika perbaikan ini mulai diberikan kepada pengguna, ditemukan beberapa penurunan performa yang perlu diperbaiki, seperti waktu muat yang melambat
Muat halaman menjadi lebih lambat
- Temuan pertama adalah perpindahan antar halaman Notion memang menjadi lebih cepat, tetapi muat halaman awal justru menjadi lebih lambat
- Hasil profiling menunjukkan bahwa proses muat halaman biasanya tidak dibatasi oleh pengambilan data
- Kode boot aplikasi Notion menjalankan pekerjaan lain (parsing JS, pengaturan aplikasi, dan sebagainya) sambil menunggu panggilan API selesai, sehingga tidak mendapatkan manfaat caching SQLite sebesar navigasi
- Penyebab perlambatan adalah pengguna harus mengunduh dan memproses library WASM SQLite
- Hal ini memblokir proses muat halaman sehingga pekerjaan muat halaman lainnya tidak bisa berlangsung secara bersamaan
- Karena ukuran library ini mencapai beberapa ratus kilobyte, waktu tambahan tersebut terlihat jelas dalam metrik
- Untuk mengatasinya, Notion sedikit mengubah cara library dimuat
- WASM SQLite dimuat sepenuhnya secara asinkron dan tidak lagi memblokir muat halaman
- Ini berarti data halaman awal hampir tidak pernah dimuat dari SQLite
- Hal itu dinilai tidak masalah, karena secara objektif peningkatan kecepatan dari memuat halaman awal lewat SQLite tidak lebih besar daripada perlambatan akibat mengunduh library tersebut
- Setelah perubahan diterapkan, metrik muat halaman awal menjadi sama antara kelompok uji dan kelompok kontrol dalam eksperimen
Perangkat lambat tidak mendapat manfaat dari caching
- Fenomena lain yang ditemukan dalam metrik adalah waktu median saat berpindah dari satu halaman Notion ke halaman lain menjadi lebih cepat, tetapi waktu persentil ke-95 justru lebih lambat
- Perangkat tertentu, seperti ponsel dengan browser yang membuka Notion, tidak mendapatkan manfaat dari caching dan malah menjadi lebih buruk
- Jawaban atas teka-teki ini ditemukan dalam investigasi sebelumnya yang dilakukan tim mobile
- Ketika caching ini diimplementasikan di aplikasi mobile native, beberapa perangkat seperti ponsel Android lama membaca dari disk dengan sangat lambat
- Karena itu, tidak bisa diasumsikan bahwa memuat data dari cache disk selalu lebih cepat daripada memuat data yang sama dari API
- Hasil investigasi mobile menunjukkan bahwa untuk muat halaman sudah ada logika yang membuat dua permintaan asinkron (SQLite dan API) saling "berlomba"
- Logika ini kemudian cukup diimplementasikan ulang pada jalur kode untuk klik navigasi
- Hasilnya, waktu navigasi persentil ke-95 menjadi sama antara dua kelompok dalam eksperimen
Kesimpulan
- Memberikan peningkatan performa SQLite ke Notion di browser memiliki tantangannya sendiri
- Khususnya terkait teknologi baru, Notion menghadapi serangkaian hal yang belum diketahui dan memetik beberapa pelajaran selama prosesnya:
- OPFS pada dasarnya tidak menangani konkurensi dengan elegan. Developer harus menyadari hal ini dan merancang sistem sesuai kondisi tersebut
- Web Workers dan SharedWorkers (serta kerabatnya, Service Workers, yang tidak dibahas dalam artikel ini) memiliki kemampuan yang berbeda, dan menggabungkannya bisa berguna bila diperlukan
- Per musim semi 2024, menerapkan isolasi lintas origin sepenuhnya pada aplikasi web yang kompleks bukan hal mudah, terutama jika menggunakan skrip pihak ketiga
- Hasil dari menyimpan data pengguna di cache browser dengan SQLite adalah peningkatan waktu navigasi sebesar 20% seperti yang disebutkan sebelumnya, tanpa terlihat penurunan pada metrik lainnya
- Yang penting, tidak ada masalah yang diamati akibat korupsi SQLite
- Keberhasilan dan stabilitas pendekatan akhir ini, menurut Notion, berkat tim yang menangani implementasi WASM resmi SQLite serta Roy Hashimoto yang menyediakan pendekatan eksperimental tersebut kepada publik
6 komentar
Itulah kenapa layanan yang harus bekerja sama dengan pihak ketiga seharusnya sejak rilis pertama langsung mengaktifkan isolasi lintas asal...
Wah, senang bertemu dengan Anda, cometkim.
Kalau saya membuka halaman Notion di Firefox, browser jadi hang dan tidak bisa dipakai. Mungkin ini penyebabnya ya.. (Aplikasi Notion berjalan baik, jadi untuk sementara saya pakai itu)
Mungkin memang begitu. Enda juga tampaknya hanya mendukung penulisan file lokal di Chrome & Edge.
Saya pernah mengalami hal seperti ini di laptop Linux lama; ternyata kalau dibuka dalam mode privat, bisa.