Introduction
merql est une bibliothèque PHP pure qui effectue des fusions à trois voies sur l’état d’une base de données. Elle crée des instantanés des tables, calcule les ensembles de modifications entre les instantanés, fusionne deux ensembles de modifications par rapport à une base commune et détecte les conflits au niveau des colonnes. Le même algorithme que git utilise pour les fichiers source, appliqué aux lignes et aux colonnes plutôt qu’aux lignes de texte.
Aucun outil externe. Prend en charge MySQL et SQLite grâce à des adaptateurs de connexion et des pilotes de dialecte enfichables.
Exemple rapide
Une fusion nécessite trois états des mêmes données : la base commune aux deux copies au départ, puis les deux copies après leurs modifications indépendantes. Ici, un environnement de test a été copié depuis la base de données active, et tous deux ont été modifiés depuis.
use Merql\Connection;
use Merql\Merql;
$live = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');
// Base: live at the moment the sandbox was copied from it.
Merql::init($live);
Merql::snapshot('base');
// ... copy live.sqlite to sandbox.sqlite, then both databases change ...
// Ours: the sandbox with our edits. Theirs: live with its own changes.
Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');
$result = Merql::merge('base', 'ours', 'theirs');
if ($result->isClean()) {
foreach ($result->operations() as $operation) {
echo "{$operation->type} {$operation->table} #{$operation->rowKey} ({$operation->source})\n";
}
} else {
echo "Conflicts: {$result->conflictCount()}\n";
}
Chaque instantané est pris depuis la base de données qui contient cet état. Prendre les trois instantanés successivement depuis une seule base de données ne représente pas deux copies divergentes : « theirs » contiendrait déjà les changements de « ours », et chaque ligne insérée par « ours » serait signalée comme un conflit insert_insert.
Le résultat répertorie les opérations qui transforment la base en état fusionné, des deux côtés, chacune indiquant sa source (ours, theirs ou merged). Pour écrire dans l'environnement live les modifications du bac à sable, appliquez les opérations qui manquent à live avec Application protégée.
Ou depuis CLI, qui se connecte à la base de données nommée dans MERQL_DB_DSN pour chaque snapshot :
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
# ... copy live.sqlite to sandbox.sqlite, then both databases change ...
MERQL_DB_DSN="sqlite:/data/sandbox.sqlite" vendor/bin/merql snapshot ours
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot theirs
vendor/bin/merql merge base ours theirs --dry-run
Pourquoi merql
La plupart des outils de base de données qui comparent des états travaillent au niveau des lignes : si une colonne d'une ligne diffère entre deux versions, la ligne entière est signalée comme conflit. Cela provoque de faux conflits lorsque deux personnes modifient des colonnes différentes d'une même ligne.
merql fusionne au niveau des colonnes. Si un côté a modifié title et l'autre status, ces modifications fusionnent sans conflit. Un conflit n'est signalé que lorsque les deux côtés modifient la même colonne avec des valeurs différentes.
Pour les colonnes TEXT et JSON, merql peut aller plus loin avec une fusion au niveau des cellules. Deux modifications portant sur des lignes différentes d'une colonne texte, ou sur des clés différentes d'une colonne JSON, peuvent fusionner proprement grâce au même algorithme à trois voies. La fusion au niveau des cellules est facultative : passez CellMergeConfig à ThreeWayMerge. La façade Merql et la commande de fusion CLI travaillent uniquement au niveau des colonnes. Voir Fusion au niveau des cellules.
Le modèle de fusion
Base (snapshot at fork time)
/ \
Ours (one copy's changes) Theirs (the other copy's changes)
\ /
--------- MERGE ----------
|
Merged result
+ conflicts (if any)
Trois états, comme dans git. La base est l'ancêtre commun. « Nous » et « eux » sont les deux copies après leur divergence, chacune capturée depuis sa propre base de données. Les règles de fusion traitent les deux côtés de la même manière. Ces noms apparaissent dans le source de chaque opération, dans les types de conflit (update_delete signifie que nous avons mis à jour et qu'eux ont supprimé) et dans les politiques de résolution OursWins et TheirsWins. Sans base commune, il est impossible de distinguer « ajouté » de « inchangé ».
Une fusion n'écrit rien à elle seule. Ses opérations transforment la base en état fusionné ; Merql::apply() les exécute donc toutes sans problème uniquement sur une base de données qui contient encore la base. Pour intégrer les modifications d'un côté dans une base de données qui contient déjà l'autre côté, par exemple pour promouvoir dans live les modifications du bac à sable, appliquez uniquement les opérations qui lui manquent ; voir Application protégée.
Prérequis
- PHP 8.2 ou version ultérieure.
ext-mbstringetext-zlib, requis par la dépendancepitmaster/pitmasterqui fournit le diff de texte.- Une extension de base de données pour l'adaptateur utilisé :
ext-pdoavecext-pdo_sqliteouext-pdo_mysql, ouext-mysqli. Lecomposer.jsonde merql ne suggère queext-pdoetext-mysqli; Composer ne vérifie donc pas leur présence. - Une base de données MySQL ou SQLite. Les autres moteurs nécessitent un pilote personnalisé.
Ce que ce n'est pas
merql n'est pas un outil de migration de schéma. Il ne réplique pas les bases de données et ne les synchronise pas en temps réel. Il résout un problème précis : à partir de trois états de base de données, produire un quatrième état fusionné en identifiant les conflits.
Documentation
- Premiers pas -- installation, premier instantané, première fusion.
- CLI Référence -- référence complète de la commande
vendor/bin/merql. - PHP API Référence -- documentation complète API de toutes les classes.
- Plans de fusion -- identifiants d’opération stables, groupes de modifications et sélections.
- Application contrôlée -- écritures sélectionnées avec préconditions sur les lignes en direct.
- Restauration -- artefacts de restauration et vérifications de dérive.