Μετάβαση στο περιεχόμενο

Συγχώνευση τριών κατευθύνσεων

Μια συγχώνευση τριών κατευθύνσεων συγκρίνει δύο σύνολα αλλαγών με βάση έναν κοινό πρόγονο και τα συνδυάζει σε ένα αποτέλεσμα. Το merql εφαρμόζει αυτόν τον αλγόριθμο σε γραμμές βάσης δεδομένων αντί για γραμμές αρχείων.

PHP
use Merql\Merge\ThreeWayMerge;

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

echo $result->operationCount(); // resolved operations
echo $result->conflictCount();  // unresolved conflicts

Οι τρεις καταστάσεις

Κάθε συγχώνευση απαιτεί ακριβώς τρία στιγμιότυπα:

  • Βάση -- η κατάσταση της βάσης δεδομένων τη στιγμή που διακλαδώθηκαν και οι δύο κλάδοι.
  • Δική μας πλευρά -- ένας κλάδος μετά τις αλλαγές του, για παράδειγμα ένα επεξεργασμένο δοκιμαστικό αντίγραφο.
  • Άλλη πλευρά -- ο άλλος κλάδος μετά τις αλλαγές του, για παράδειγμα η ενεργή βάση δεδομένων που συνέχισε να λειτουργεί.

Η δική μας πλευρά και η άλλη πλευρά πρέπει να καταγράφονται η καθεμία από τη βάση δεδομένων που περιέχει τον αντίστοιχο κλάδο. Οι κανόνες συγχώνευσης αντιμετωπίζουν και τις δύο πλευρές ισότιμα· τα ονόματα καθορίζουν την source κάθε πράξης, την ονομασία των τύπων διένεξης (το update_delete σημαίνει ότι η δική μας πλευρά ενημέρωσε τη γραμμή και η άλλη την διέγραψε) και ποια πλευρά ευνοούν τα ConflictPolicy::OursWins και TheirsWins.

Κώδικας
         Base (snapshot at fork time)
        /                              \
   Ours (our changes)                   Theirs (their changes)
        \                              /
         --------- MERGE ----------
                     |
              Merged result
              + conflicts (if any)

Χωρίς κοινή βάση, δεν μπορείτε να διακρίνετε το «προστέθηκε» από το «δεν άλλαξε». Η σύγκριση δύο πλευρών δείχνει ότι η πλευρά A έχει μια τιμή και η πλευρά B όχι, αλλά δεν μπορεί να προσδιορίσει αν την πρόσθεσε η A ή αν τη διέγραψε η B. Η βάση απαντά σε αυτό το ερώτημα.

Πώς υπολογίζονται τα σύνολα αλλαγών

Το Differ συγκρίνει κάθε στιγμιότυπο με τη βάση για να παράγει δύο σύνολα αλλαγών. Κάθε σύνολο αλλαγών περιέχει τρία είδη πράξεων:

Βάση Τρέχουσα κατάσταση Πράξη
Η γραμμή υπάρχει Η γραμμή υπάρχει, ίδιο αποτύπωμα Καμία αλλαγή
Η γραμμή υπάρχει Η γραμμή υπάρχει, διαφορετικό αποτύπωμα UPDATE (διαφορά ανά στήλη)
Η γραμμή υπάρχει Η γραμμή λείπει DELETE
Η γραμμή λείπει Η γραμμή υπάρχει INSERT

Τα αποτυπώματα (κατακερματισμοί SHA-256 κανονικοποιημένου περιεχομένου γραμμής) προσφέρουν μια γρήγορη διαδρομή. Αν δύο αποτυπώματα ταιριάζουν, η γραμμή δεν έχει αλλάξει και δεν χρειάζεται σύγκριση σε επίπεδο στηλών.

PHP
use Merql\Diff\Differ;

$differ = new Differ();
$oursChangeset = $differ->diff($base, $ours);
$theirsChangeset = $differ->diff($base, $theirs);

Κανόνες συγχώνευσης

Στη συνέχεια, τα δύο σύνολα αλλαγών συγχωνεύονται πράξη προς πράξη. Αυτός ο πίνακας ορίζει το αποτέλεσμα για κάθε συνδυασμό:

Δική μας πλευρά Άλλη πλευρά Αποτέλεσμα
Καμία αλλαγή Καμία αλλαγή Διατήρηση της βάσης
Καμία αλλαγή Ενημερώθηκε Αποδοχή αλλαγής άλλης πλευράς
Ενημερώθηκε Καμία αλλαγή Αποδοχή δικής μας αλλαγής
Ενημερώθηκε (ίδια τιμή) Ενημερώθηκε (ίδια τιμή) Αποδοχή (συμφωνούν και οι δύο)
Ενημερώθηκε (διαφορετική τιμή) Ενημερώθηκε (διαφορετική τιμή) Διένεξη
Καμία αλλαγή Διαγράφηκε Αποδοχή διαγραφής
Διαγράφηκε Καμία αλλαγή Αποδοχή διαγραφής
Διαγράφηκε Διαγράφηκε Αποδοχή διαγραφής (συμφωνούν και οι δύο)
Ενημερώθηκε Διαγράφηκε Διένεξη
Διαγράφηκε Ενημερώθηκε Διένεξη
Προστέθηκε (μόνο στη δική μας πλευρά) -- Αποδοχή προσθήκης
-- Προστέθηκε (μόνο στην άλλη πλευρά) Αποδοχή προσθήκης
Προστέθηκε (ίδιο PK) Προστέθηκε (ίδιο PK) Διένεξη

Όταν και οι δύο πλευρές ενημερώνουν την ίδια γραμμή, το merql δεν δηλώνει αμέσως διένεξη. Προχωρά στη συγχώνευση σε επίπεδο στήλης και συγκρίνει κάθε στήλη ανεξάρτητα. Για λεπτομέρειες, δείτε τη Συγχώνευση σε επίπεδο στήλης.

Χρήση της πρόσοψης

Η πρόσοψη Merql λειτουργεί με ονομασμένα στιγμιότυπα που έχουν αποθηκευτεί στον δίσκο. Καλέστε το Merql::init() με τη βάση δεδομένων που περιέχει κάθε κατάσταση πριν από τη δημιουργία στιγμιότυπου:

PHP
use Merql\Connection;
use Merql\Merql;

$live    = Connection::sqlite('/data/live.sqlite');
$sandbox = Connection::sqlite('/data/sandbox.sqlite');

Merql::init($live);
Merql::snapshot('base');      // before the sandbox is copied from live

// ... copy live.sqlite to sandbox.sqlite, then both databases change ...

Merql::init($sandbox);
Merql::snapshot('ours');
Merql::init($live);
Merql::snapshot('theirs');

$result = Merql::merge('base', 'ours', 'theirs');

Μην πάρετε και τα τρία στιγμιότυπα διαδοχικά από μία βάση δεδομένων. Τότε η άλλη πλευρά θα περιέχει και τις αλλαγές της δικής μας πλευράς, με αποτέλεσμα κάθε γραμμή που πρόσθεσε η δική μας πλευρά να φαίνεται ότι προστέθηκε και από τις δύο πλευρές και να αναφέρεται ως διένεξη insert_insert.

Τα Merql::merge(), Merql::patch() και Merql::plan() δημιουργούν ένα ThreeWayMerge χωρίς CellMergeConfig, επομένως συγχωνεύουν μόνο σε επίπεδο στηλών. Για συγχώνευση σε επίπεδο κελιού, φορτώστε τα στιγμιότυπα και χρησιμοποιήστε απευθείας την κλάση, όπως παρακάτω.

Άμεση χρήση των κλάσεων

Για μεγαλύτερο έλεγχο, δημιουργήστε εσείς τα επιμέρους αντικείμενα. Το Snapshotter::capture() δεν αποθηκεύει τίποτα, οπότε διατηρήστε τα στιγμιότυπα στη μνήμη ή αποθηκεύστε τα με το SnapshotStore::save():

PHP
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Snapshot\Snapshotter;
use Merql\Snapshot\SnapshotStore;

$base   = (new Snapshotter($live))->capture('base');
// ... copy live to the sandbox, then both databases change ...
$ours   = (new Snapshotter($sandbox))->capture('ours');
$theirs = (new Snapshotter($live))->capture('theirs');

$merge = new ThreeWayMerge();
$result = $merge->merge($base, $ours, $theirs);

// Snapshots saved earlier, merged with cell-level merge for TEXT and JSON columns.
$merge = new ThreeWayMerge(CellMergeConfig::auto());
$result = $merge->merge(
    SnapshotStore::load('base'),
    SnapshotStore::load('ours'),
    SnapshotStore::load('theirs'),
);

Η συντομότερη διαδρομή με patch

Όταν μόνο η μία πλευρά έχει αλλαγές (χωρίς ταυτόχρονες τροποποιήσεις), χρησιμοποιήστε το patch(). Πρόκειται για συγχώνευση δύο κατευθύνσεων όπου η δική μας πλευρά είναι ίδια με τη βάση, επομένως δεν μπορούν να προκύψουν διενέξεις:

PHP
$result = $merge->patch($base, $changes);
// Always clean: ours is identical to base.

Μέσω της πρόσοψης:

PHP
$result = Merql::patch('base', 'changes');

Το patch() είναι ισοδύναμο με το merge($base, $base, $changes). Αφού η δική μας πλευρά δεν αποκλίνει ποτέ από τη βάση, κάθε αλλαγή της άλλης πλευράς γίνεται δεκτή χωρίς διένεξη.

Εντοπισμός ασυμφωνιών σχήματος

Αν προστεθούν ή αφαιρεθούν στήλες από έναν πίνακα μεταξύ στιγμιότυπων, η συγχώνευση συνεχίζεται αλλά καταγράφει τις ασυμφωνίες. Οι αλλαγές στους τύπους στηλών δεν συγκρίνονται, ενώ μια μετονομασμένη στήλη εμφανίζεται ως μία αφαιρεμένη και μία προστιθέμενη· δείτε την Επικύρωση σχήματος.

PHP
$result = $merge->merge($base, $ours, $theirs);

if ($result->hasSchemaMismatches()) {
    foreach ($result->schemaMismatches() as $mismatch) {
        echo $mismatch->getMessage() . "\n";
    }
}

Το merql δεν μεταφέρει αυτόματα τα σχήματα σε νέα έκδοση. Οι αλλαγές σχήματος είναι ευθύνη του καλούντος. Η συγχώνευση τις αναφέρει ώστε να αποφασίσετε πώς θα προχωρήσετε.

Το MergeResult

Το αντικείμενο αποτελέσματος περιέχει όλα όσα χρειάζεστε:

PHP
$result->isClean();            // true if no conflicts
$result->operations();         // list of MergeOperation objects
$result->conflicts();          // list of Conflict objects
$result->operationCount();     // number of resolved operations
$result->conflictCount();      // number of unresolved conflicts
$result->hasSchemaMismatches(); // true if schemas differ
$result->baseSnapshot();       // the base snapshot used for the merge

Κάθε MergeOperation έχει έναν τύπο (insert, update ή delete), όνομα πίνακα, κλειδί γραμμής, τιμές στηλών και προέλευση (ours, theirs ή merged). Το merged επισημαίνει μια γραμμή που ενημέρωσαν και οι δύο πλευρές (με συνδυασμό ανά στήλη) ή διέγραψαν και οι δύο.

Πού εφαρμόζεται ένα αποτέλεσμα συγχώνευσης

Ένα αποτέλεσμα συγχώνευσης περιγράφει πώς μετατρέπεται η βάση στην κατάσταση συγχώνευσης. Οι πράξεις του περιλαμβάνουν τις αλλαγές και των δύο πλευρών: μια εισαγωγή της δικής μας πλευράς και μια εισαγωγή της άλλης πλευράς εμφανίζονται και οι δύο ως εισαγωγές.

  • Το Merql::apply() (ή το Applier) εκτελεί κάθε πράξη στην συνδεδεμένη βάση δεδομένων. Εφαρμόζεται ομαλά σε βάση δεδομένων που εξακολουθεί να περιέχει τη βάση, όπως ένα νέο αντίγραφο της αρχικής κατάστασης. Αν εφαρμοστεί σε βάση δεδομένων που περιέχει ήδη τις αλλαγές της μίας πλευράς, η εισαγωγή μιας ήδη υπάρχουσας γραμμής αποτυγχάνει και η συναλλαγή επαναφέρεται.
  • Για να περάσετε τις αλλαγές της δικής μας πλευράς σε βάση δεδομένων που περιέχει ήδη τις αλλαγές της άλλης πλευράς, όπως κατά την προώθηση αλλαγών δοκιμαστικού περιβάλλοντος στο ενεργό, εφαρμόστε μόνο τις πράξεις που λείπουν από αυτήν τη βάση: εκείνες με προέλευση ours και τις ενημερώσεις merged. Χρησιμοποιήστε ένα σχέδιο συγχώνευσης για να τις επιλέξετε και το Guarded Apply για να τις γράψετε με προϋποθέσεις στις ενεργές γραμμές.