Petunjuk dan konteks
node:sqlite di Node.js 24 mengekspos API sinkron. DatabaseSync mewakili satu koneksi SQLite; createSession() melacak perubahan setelah sesi dimulai; session.changeset() mengembalikan changeset biner; dan target dapat memanggil applyChangeset() dengan pengendali konflik (conflict handler) yang mengabaikan (omit), mengganti (replace), atau membatalkan (abort). Rancang protokol sinkronisasi inkremental antara klien offline dan server.
Masalah utamanya adalah penyebaran perubahan basis data yang aman: primary key, pemeriksaan nilai lama, kebijakan konflik, idempoten, batas transaksi, dan izin akses. Changeset bukanlah SQL yang dapat dieksekusi dan bukan penggabungan lintas basis data secara otomatis.
Apa yang sedang diuji oleh pewawancara
Jawaban yang kuat memisahkan antara pembuatan di sisi sumber, pengiriman yang andal, penerapan target secara atomik, dan keputusan konflik bisnis. Bersiaplah untuk pertanyaan tentang masa pakai Session, changeset versus patchset, SQLITE_CHANGESET_DATA dan tipe konflik terkait, penerapan duplikat, evolusi skema, serta bagaimana pekerjaan DatabaseSync yang sinkron memengaruhi event loop Node.js.
Pendekatan “Serialisasi dan timpa” akan menghilangkan pembaruan independen pada target. Selalu mengembalikan SQLITE_CHANGESET_REPLACE secara diam-diam akan menimpa data bisnis dan tidak memberikan jaminan kebenaran data.
Pertanyaan klarifikasi awal
Topologi dan otoritas
Tentukan apakah sinkronisasi bersifat satu arah, dua arah, atau agregasi multi-klien, serta salinan mana yang menjadi sumber otoritatif untuk setiap entitas. Jika kedua belah pihak mengedit baris yang sama, gunakan versi, ID perangkat, atau event bisnis untuk menengahi; callback default SQLite bukanlah aturan bisnis.
Siklus hidup versi dan skema
Pastikan bahwa sumber dan target berbagi skema, primary key, dan tipe kolom yang sama. Changeset bergantung pada struktur tabel. Migrasi harus selesai dan menyertakan versi protokol sebelum changeset lama diputar ulang (replayed).
Latensi dan batas keamanan
Perjelas ukuran payload, jendela percobaan ulang (retry window), durasi offline, dan bidang-bidang sensitif. Payload biner memerlukan integritas, autentikasi, perlindungan replay (replay protection), dan enkripsi; payload tersebut bukanlah SQL yang tepercaya.
Jawaban 30 detik
“Saya menetapkan ID dan kursor versi sumber untuk setiap batch. Sumber membuat Session, melakukan commit transaksi lokal, dan mengekspor changeset. Server memvalidasi skema, tanda tangan, urutan, dan idempoten, lalu memanggil applyChangeset di dalam transaksi target. Pengendali konflik membatalkan (abort) secara default dan mencatat baris, kolom, serta alasannya; tindakan omit atau replace hanya diizinkan berdasarkan kebijakan bisnis yang eksplisit. Batch yang berhasil akan memajukan status persisten, kegagalan diulang dengan bounded backoff, dan batch duplikat mengembalikan hasil sebelumnya. Pekerjaan SQLite sinkron berukuran besar dijalankan di balik worker atau antrean agar tidak memblokir event loop Node.”
Solusi langkah demi langkah
Langkah 1: Buat batch yang dapat dilacak
Setelah membuat Session, sertakan satu cakupan transaksi lokal eksplisit dalam sebuah batch. Catat batchId, perangkat sumber, kursor awal, versi skema, hash changeset, dan waktu pembuatan. Ekspor Uint8Array dari session.changeset(), lalu tutup atau gunakan kembali Session agar riwayat tidak menumpuk tanpa batas.
Langkah 2: Pilih changeset atau patchset
Changeset menyertakan informasi nilai lama yang berguna untuk memeriksa apakah baris target masih cocok dengan ekspektasi. Patchset berukuran lebih kecil tetapi mengekspos lebih sedikit konteks nilai lama. Pilihlah berdasarkan kebutuhan bandwidth dan audit; ukuran payload saja bukan alasan yang cukup untuk kehilangan kemampuan diagnostik.
Langkah 3: Validasi dan kirimkan secara idempoten
Gunakan TLS, identitas perangkat, dan tanda tangan payload. Validasi ukuran, hash, versi skema, dan asal data. Simpan catatan unik dengan kunci batchId dan kursor sumber; duplikat dari batch yang sudah diterapkan akan mengembalikan hasil yang telah dicatat. Terapkan batch berikutnya sesuai urutan kursor, lalu tempatkan batch yang hilang ke dalam antrean tunggu alih-alih melewatinya.
Langkah 4: Terapkan dalam transaksi dan klasifikasikan konflik
Buka transaksi target dan panggil applyChangeset. Pengendali konflik mengumpulkan informasi tabel, primary key, jenis konflik, dan nilai target ke dalam buffer audit. Kembalikan SQLITE_CHANGESET_ABORT secara default agar batch melakukan rollback secara atomik. Hanya kolom yang disetujui oleh kebijakan yang boleh mengembalikan OMIT atau REPLACE, dan keputusan tersebut harus dicatat dalam log yang dapat diputar ulang.
Langkah 5: Tentukan penggabungan bisnis (business merges)
Data yang dapat digabungkan pada tingkat kolom dapat menggunakan stempel waktu, versi monotonik, atau set union. Data uang, inventaris, dan izin tidak aman untuk penggabungan buta (blind merging); kirimkan data tersebut ke antrean manual manusia atau compensation event. Jangan memutasi changeset asli setelah keputusan dibuat. Buat child batch yang berisi parent batch dan alasan keputusan untuk menjaga rantai audit.
Langkah 6: Tangani tipe data dan presisi integer
Node.js dan SQLite mendukung himpunan tipe data yang berbeda. Jika SQLite INTEGER melebihi batas integer aman JavaScript dan readBigInts dinonaktifkan, membacanya dapat memicu ERR_OUT_OF_RANGE. Standarkan pada BigInt, string, atau rentang yang eksplisit. Gunakan Uint8Array untuk nilai BLOB dan tolak objek arbitrer.
Langkah 7: Kendalikan biaya sinkron
Panggilan DatabaseSync dieksekusi secara sinkron. Transaksi yang panjang, changeset yang sangat besar, atau panggilan applyChangeset yang sering dapat memblokir event loop. Pindahkan sinkronisasi ke worker, proses terpisah, atau antrean terkontrol; batasi payload dan baris per batch; pantau durasi penerapan, konflik, rollback, dan usia antrean.
Contoh jawaban berkualitas tinggi
Saya memperlakukan setiap batch sinkronisasi sebagai event yang tidak dapat diubah (immutable). Perangkat membuat ID, kursor, versi skema, dan hash; Session hanya mencakup transaksi lokal yang sudah di-commit; dan changeset dikirimkan melalui saluran yang terautentikasi dan terlindung dari replay. Server memvalidasi struktur dan urutan, melakukan deduplikasi berdasarkan ID batch, dan memanggil applyChangeset di dalam transaksi SQLite target.
Callback konflik membatalkan (abort) secara default, melakukan rollback secara atomik, dan mencatat primary key, tipe konflik, serta nilai target. Data inventaris, uang, dan izin diarahkan ke penggabungan bisnis atau kompensasi alih-alih penggantian generik; hanya kolom yang secara eksplisit aman yang boleh diabaikan atau diganti. Status batch persisten hanya memajukan kursor setelah commit berhasil. Karena DatabaseSync memblokir secara sinkron, saya menjalankan batch besar di worker dan menguji pengiriman duplikat, batch yang hilang, migrasi skema, luapan integer (overflow), serta pemulihan dari crash proses.
Kesalahan umum
- Kesalahan: Menimpa basis data target pada setiap sinkronisasi. → Mengapa gagal: Pembaruan independen pada target akan hilang. → Perbaikan: Kirim changeset, bandingkan nilai lama, dan catat keputusannya.
- Kesalahan: Mengembalikan
REPLACEuntuk setiap konflik. → Mengapa gagal: Data bisnis ditimpa secara diam-diam. → Perbaikan: Lakukan abort secara default dan izinkan penggantian berdasarkan kolom dan invarian tertentu. - Kesalahan: Membuat batch baru untuk setiap percobaan ulang tanpa kunci idempoten. → Mengapa gagal: Penerapan duplikat atau lompatan kursor menciptakan efek samping berulang. → Perbaikan: Lakukan deduplikasi dengan ID batch, kursor sumber, dan status aplikasi.
- Kesalahan: Menerapkan changeset besar pada event loop utama. → Mengapa gagal: Pekerjaan
DatabaseSyncyang sinkron memblokir HTTP dan tugas terjadwal. → Perbaikan: Isolasi dalam worker atau antrean dan batasi ukuran batch.
Pertanyaan lanjutan dan jawabannya
Lanjutan 1: Kapan Anda akan menggunakan patchset?
Gunakan patchset ketika bandwidth terbatas dan target memiliki konteks yang cukup, dengan kebutuhan audit konflik yang sederhana. Gunakan changeset ketika nilai lama diperlukan untuk menjelaskan konflik atau melakukan penggabungan yang dapat diaudit, dengan konsekuensi ukuran payload yang lebih besar.
Lanjutan 2: Bagaimana jika skema target kekurangan kolom?
Tolak batch dengan kesalahan kompatibilitas; jangan biarkan aplikasi menebak pemetaan. Jalankan migrasi target, verifikasi versi skema dan primary key, lalu putar ulang. Jika beberapa versi harus hidup berdampingan, lakukan konversi berdasarkan versi protokol dengan tetap mempertahankan payload asli.
Lanjutan 3: Bagaimana Anda mencegah efek samping dalam callback konflik?
Callback hanya bertugas mengumpulkan data konflik terstruktur dan mengembalikan keputusan konstan. Callback tidak boleh memanggil layanan eksternal atau memutasi tabel lain. Tulis catatan audit atau notifikasi setelah commit selesai sehingga rollback tidak meninggalkan inkonsistensi pada status eksternal.
Lanjutan 4: Mengapa tidak mengirimkan SQL secara langsung?
SQL tidak memiliki informasi nilai lama dari sumber, konteks skema, dan batas batch. Percobaan ulang tidak dapat menentukan secara andal apakah SQL tersebut sudah diterapkan, dan pernyataan yang tidak sah dapat masuk ke target. Changeset yang dihasilkan SQLite dapat memproses setiap konflik di target, menjadikannya primitif yang jauh lebih baik untuk sinkronisasi terkontrol.
Lanjutan 5: Bagaimana Anda memverifikasi paritas protokol untuk integer dan BLOB?
Buat fixture lintas bahasa untuk integer aman terbesar, integer di luar rentang, nilai negatif, NULL, teks UTF-8, dan payload biner. Catat tipe SQLite, opsi baca/tulis Node, dan representasi serial untuk masing-masing, lalu pastikan byte dan nilainya cocok sebelum dan sesudah diputar ulang.