Saya membangun sistem seperti ini untuk aplikasi sekitar 8 tahun yang lalu, dan saya dapat berbagi beberapa cara bahwa itu telah berevolusi karena penggunaan aplikasi telah berkembang.
Saya mulai dengan mencatat setiap perubahan (masukkan, perbarui atau hapus) dari perangkat apa pun ke tabel "riwayat". Jadi jika, misalnya, seseorang mengubah nomor telepon mereka di tabel "kontak", sistem akan mengedit bidang contact.phone, dan juga menambahkan catatan riwayat dengan action = update, field = phone, record = [ID kontak], value = [nomor telepon baru]. Kemudian kapan pun suatu perangkat disinkronkan, ia mengunduh item sejarah sejak sinkronisasi terakhir dan menerapkannya ke basis data lokalnya. Ini terdengar seperti pola "replikasi transaksi" yang dijelaskan di atas.
Satu masalah adalah menjaga ID unik ketika item dapat dibuat pada perangkat yang berbeda. Saya tidak tahu tentang UUID ketika saya memulai ini, jadi saya menggunakan ID penambahan-otomatis dan menulis beberapa kode berbelit-belit yang berjalan di server pusat untuk memeriksa ID baru yang diunggah dari perangkat, mengubahnya ke ID unik jika ada konflik, dan beri tahu perangkat sumber untuk mengubah ID di database lokalnya. Hanya mengubah ID catatan baru tidak seburuk itu, tetapi jika saya membuat, misalnya, item baru di tabel kontak, lalu membuat item terkait baru di tabel acara, sekarang saya memiliki kunci asing yang saya juga perlu periksa dan perbarui.
Akhirnya saya mengetahui bahwa UUID dapat menghindari hal ini, tetapi pada saat itu basis data saya semakin besar dan saya takut implementasi penuh UUID akan menciptakan masalah kinerja. Jadi alih-alih menggunakan UUID penuh, saya mulai menggunakan kunci alfanumerik 8 karakter yang dibuat secara acak sebagai ID, dan saya meninggalkan kode saya yang ada untuk menangani konflik. Di suatu tempat antara kunci 8-karakter saya saat ini dan 36 karakter UUID harus ada sweet spot yang akan menghilangkan konflik tanpa mengasapi yang tidak perlu, tetapi karena saya sudah memiliki kode resolusi konflik, itu belum menjadi prioritas untuk bereksperimen dengan itu .
Masalah berikutnya adalah bahwa tabel sejarah sekitar 10 kali lebih besar dari seluruh database. Ini membuat penyimpanan menjadi mahal, dan perawatan apa pun di atas tabel riwayat bisa jadi menyakitkan. Menjaga seluruh tabel memungkinkan pengguna untuk memutar kembali perubahan sebelumnya, tetapi itu mulai terasa seperti berlebihan. Jadi saya menambahkan rutin ke proses sinkronisasi di mana jika item riwayat yang terakhir diunduh perangkat tidak lagi ada di tabel riwayat, server tidak memberikannya item riwayat baru-baru ini, tetapi sebaliknya memberikannya file yang berisi semua data untuk akun itu. Kemudian saya menambahkan cronjob untuk menghapus item riwayat yang lebih lama dari 90 hari. Ini berarti pengguna masih dapat memutar kembali perubahan yang berumur kurang dari 90 hari, dan jika mereka menyinkronkan setidaknya sekali setiap 90 hari, pembaruan akan menjadi tambahan seperti sebelumnya. Tetapi jika mereka menunggu lebih dari 90 hari,
Perubahan itu mengurangi ukuran tabel riwayat hampir 90%, jadi sekarang mempertahankan tabel sejarah hanya membuat database dua kali lebih besar daripada sepuluh kali lebih besar. Manfaat lain dari sistem ini adalah bahwa sinkronisasi masih dapat bekerja tanpa tabel histori jika diperlukan - seperti jika saya perlu melakukan beberapa pemeliharaan yang menjadikannya offline untuk sementara. Atau saya dapat menawarkan periode waktu pengembalian yang berbeda untuk akun pada titik harga yang berbeda. Dan jika ada lebih dari 90 hari perubahan untuk diunduh, file lengkap biasanya lebih efisien daripada format inkremental.
Jika saya memulai hari ini, saya akan melewatkan pemeriksaan konflik ID dan hanya bertujuan untuk panjang kunci yang cukup untuk menghilangkan konflik, dengan semacam pengecekan kesalahan untuk berjaga-jaga. Tetapi tabel riwayat dan kombinasi unduhan tambahan untuk pembaruan terbaru atau unduhan lengkap saat diperlukan telah berfungsi dengan baik.