J'ai créé un système comme celui-ci pour une application il y a environ 8 ans, et je peux partager quelques façons dont il a évolué à mesure que l'utilisation de l'application augmentait.
J'ai commencé par enregistrer chaque modification (insertion, mise à jour ou suppression) de n'importe quel appareil dans un tableau "historique". Ainsi, si, par exemple, quelqu'un change son numéro de téléphone dans le tableau "contact", le système éditera le champ contact.phone et ajoutera également un enregistrement d'historique avec action = update, field = phone, record = [contact ID], valeur = [nouveau numéro de téléphone]. Puis, chaque fois qu'un appareil se synchronise, il télécharge les éléments d'historique depuis la dernière synchronisation et les applique à sa base de données locale. Cela ressemble au modèle de «réplication de transaction» décrit ci-dessus.
Un problème est de garder les ID uniques lorsque des éléments peuvent être créés sur différents appareils. Je ne connaissais pas les UUID lorsque j'ai commencé, j'ai donc utilisé des ID auto-incrémentés et écrit du code alambiqué qui s'exécute sur le serveur central pour vérifier les nouveaux ID téléchargés à partir des appareils, les changer en un ID unique en cas de conflit, et dire au périphérique source de changer l'ID dans sa base de données locale. Changer simplement les ID des nouveaux enregistrements n'était pas si mal, mais si je crée, par exemple, un nouvel élément dans la table des contacts, puis crée un nouvel élément connexe dans la table des événements, maintenant j'ai des clés étrangères que je dois également vérifier et mettre à jour.
Finalement, j'ai appris que les UUID pouvaient éviter cela, mais ma base de données devenait alors assez volumineuse et j'avais peur qu'une implémentation complète des UUID crée un problème de performances. Donc, au lieu d'utiliser des UUID complets, j'ai commencé à utiliser des clés alphanumériques à 8 caractères générées de manière aléatoire comme ID, et j'ai laissé mon code existant en place pour gérer les conflits. Quelque part entre mes clés à 8 caractères actuelles et les 36 caractères d'un UUID, il doit y avoir un point idéal qui éliminerait les conflits sans ballonnement inutile, mais puisque j'ai déjà le code de résolution des conflits, il n'a pas été une priorité d'expérimenter avec cela .
Le problème suivant était que la table d'historique était environ 10 fois plus grande que le reste de la base de données. Cela rend le stockage coûteux et toute maintenance sur la table d'historique peut être pénible. Garder cette table entière permet aux utilisateurs d'annuler toute modification précédente, mais cela a commencé à sembler exagéré. J'ai donc ajouté une routine au processus de synchronisation où si l'élément d'historique téléchargé en dernier par un appareil n'existe plus dans la table d'historique, le serveur ne lui donne pas les éléments d'historique récents, mais lui donne à la place un fichier contenant toutes les données pour ce compte. Ensuite, j'ai ajouté un cronjob pour supprimer les éléments d'historique de plus de 90 jours. Cela signifie que les utilisateurs peuvent toujours annuler les modifications datant de moins de 90 jours, et s'ils se synchronisent au moins une fois tous les 90 jours, les mises à jour seront incrémentielles comme auparavant. Mais s'ils attendent plus de 90 jours,
Cette modification a réduit la taille de la table d'historique de près de 90%. Par conséquent, la maintenance de la table d'historique ne fait que rendre la base de données deux fois plus grande au lieu de dix fois plus grande. Un autre avantage de ce système est que la synchronisation pourrait toujours fonctionner sans la table d'historique si nécessaire - comme si j'avais besoin de faire une maintenance qui la mettait hors ligne temporairement. Ou je pourrais proposer différentes périodes de restauration pour les comptes à différents prix. Et s'il y a plus de 90 jours de modifications à télécharger, le fichier complet est généralement plus efficace que le format incrémentiel.
Si je recommençais aujourd'hui, je sauterais la vérification des conflits d'ID et viserais simplement une longueur de clé suffisante pour éliminer les conflits, avec une sorte de vérification d'erreur au cas où. Mais la table d'historique et la combinaison de téléchargements incrémentiels pour les mises à jour récentes ou un téléchargement complet en cas de besoin ont bien fonctionné.