Développer DataBridge : synchroniser 20 systèmes 4D indépendants avec un backend MySQL central
Certains projets logiciels sont spectaculaires. Celui-ci ne l’est pas. Il fonctionne en arrière-plan, travaille pendant que tout le monde dort et ne se fait remarquer que lorsqu’il échoue. C’est précisément ce qui le rend essentiel.
Nous poursuivons actuellement le développement de notre couche DataBridge : un moteur de synchronisation qui relie environ 20 installations 4D indépendantes à un backend MySQL central alimentant le site web et les services en ligne associés. Le défi dépasse le simple transfert de données d’un point A à un point B. Il faut le faire de manière sûre, répétée et prévisible dans un environnement de production où les utilisateurs travaillent, les enregistrements changent et les connexions peuvent disparaître, sans qu’aucune machine ne désorganise l’ensemble.
Au cœur de ce travail se trouve une classe dérivée de DBStructure, qui coordonne la synchronisation. Elle sait si celle-ci est active, où se trouve le point d’accès distant, si les déclencheurs sont activés, si un cycle est déjà en cours, quelle était la dernière erreur HTTP et quels enregistrements ont changé. Elle conserve aussi une collection d’abonnés aux événements, afin que les fenêtres ouvertes dans l’application 4D réagissent à l’arrivée de changements distants. C’est donc à la fois une couche de transport et une couche de coordination entre la base de données, le réseau et l’interface.
L’architecture suit un modèle simple mais rigoureux. Les changements locaux sont d’abord capturés par des déclencheurs. Lorsqu’un enregistrement est créé, modifié ou supprimé, le déclencheur ne tente pas immédiatement de contacter le serveur distant : il ajoute une entrée dans une file locale RecordSync. Ce choix dissocie les modifications de la base des communications réseau. L’utilisateur qui enregistre une fiche n’a donc pas à attendre un serveur distant, un appel HTTP ou la résolution d’un problème de connexion. Le système local reste réactif et la synchronisation devient une tâche asynchrone en arrière-plan, sans bloquer le travail en cours.
Lorsque la boucle de synchronisation se réveille, elle vérifie d’abord que le point d’accès distant est joignable. Si la connexion fonctionne, la passerelle effectue deux phases distinctes : elle transmet les changements locaux en attente, puis récupère les changements distants. Ces flux sont volontairement séparés. L’un gère les opérations émises par la machine 4D locale ; l’autre, l’état reçu du backend central. Cette séparation facilite la compréhension du code et la maîtrise du comportement en cas d’échec.
La phase d’envoi lit les lignes de RecordSync dans l’ordre des numéros de séquence, construit les données à transmettre et les envoie par lots. Chaque ensemble contient le nom de la table, l’état de l’enregistrement, sa clé primaire et, si nécessaire, les données sérialisées de l’entité. Une suppression ne nécessite que l’identité et l’état ; une création ou modification exige un instantané complet de l’objet. Une fois un lot confirmé, les entrées correspondantes sont supprimées de la file. Si l’envoi échoue, elles restent en place pour une nouvelle tentative au cycle suivant. Cette ingénierie est discrète, mais elle garantit que le système reste fidèle à la réalité des opérations.
La réception est tout aussi soigneuse. La passerelle demande au serveur combien de changements attendent après le dernier numéro de séquence connu, les récupère par blocs et les applique un à un dans les données locales 4D. Chaque changement correspond à une création, modification ou suppression. Les nouveaux enregistrements sont créés s’ils sont absents, les modifications sont rechargées dans l’entité correspondante et les enregistrements supprimés sont effacés lorsque c’est possible. Après traitement, le numéro de séquence local est avancé : la machine sait exactement où elle en est dans le flux.
Tout cela paraît simple jusqu’à rencontrer le plus ancien ennemi des moteurs de synchronisation : la récursion. Si une modification distante enregistrée localement déclenche un nouvel envoi, le système peut entrer dans une boucle sans fin. La passerelle utilise donc une triggerKey pour marquer les enregistrements en cours d’application depuis la synchronisation distante. Lorsque le déclencheur local correspondant s’exécute, il reconnaît que le changement vient de la passerelle et l’ignore. Cette idée évite au moteur de se renvoyer indéfiniment ses propres modifications.
Autre détail discret mais essentiel : le verrouillage. Un utilisateur peut déjà avoir ouvert un enregistrement lorsqu’une mise à jour distante arrive. Au lieu de forcer l’enregistrement, d’écraser les données ou de provoquer un conflit, la passerelle tente d’abord de verrouiller l’entité. Si elle n’y parvient pas, elle consigne la situation et reporte l’élément à un prochain cycle. C’est un choix délibéré : différer en toute sécurité plutôt que nier les problèmes d’accès concurrents. Les systèmes distribués pardonnent mal un excès d’optimisme.
L’implémentation soigne également le suivi du fonctionnement. La couche de synchronisation affiche sa progression dans une fenêtre de palette, compte les enregistrements traités, conserve les erreurs HTTP et consigne chaque événement important dans un journal. Les développeurs d’infrastructures en arrière-plan le savent : lorsqu’un problème survient à 03:12, la qualité des journaux fait souvent la différence entre cinq minutes de correction et quatre heures d’enquête.
Au-delà de la synchronisation incrémentale courante, la passerelle fournit des outils d’exploitation. Elle peut envoyer des tables complètes vers le backend, récupérer des tables distantes dans 4D, créer des tables à distance et comparer les structures locales et distantes. Elle sert donc aussi à l’installation, à la migration, au diagnostic et à la reprise contrôlée. Un moteur de synchronisation devient bien plus utile lorsqu’il aide à comprendre le système, en plus de transférer les données.
Pour les développeurs, ce projet se situe à la rencontre du logiciel métier classique et de l’ingénierie des systèmes distribués. D’un côté, 4D : mature, local, transactionnel et éprouvé. De l’autre, une plateforme web centrale sur MySQL qui exige la cohérence entre de nombreuses machines et de nombreux utilisateurs. DataBridge assure le passage entre ces deux mondes. Il gère les données, mais aussi le rythme des opérations, les états, les échecs, les nouvelles tentatives, l’ordre des séquences et la confiance dans le résultat.
Il reste du travail. Les moteurs de synchronisation ne sont jamais vraiment terminés : on les affine, on les renforce et on leur apprend à traiter de nouveaux cas particuliers. Mais chaque amélioration rapproche l’infrastructure de ce que les développeurs en attendent : une fiabilité suffisante pour ne plus avoir à y penser. C’est finalement le plus beau compliment pour ce type de composant : aucune ovation, simplement le silence.