Перейти до вмісту

Вступ

merql — це бібліотека на PHP для тристороннього злиття станів баз даних. Вона створює знімки таблиць, обчислює набори змін між знімками, об’єднує два набори змін відносно спільної бази та виявляє конфлікти на рівні стовпців. Той самий алгоритм, який git використовує для файлів із вихідним кодом, застосовано до рядків і стовпців замість рядків тексту.

Без зовнішніх інструментів. Підтримує MySQL і SQLite через адаптери підключення та драйвери діалектів, які можна підключати.

Короткий приклад

Для злиття потрібні три стани тих самих даних: base, з якого почали обидві копії, і дві копії після незалежних змін. У цьому прикладі ізольоване середовище скопіювали з робочої бази даних, і відтоді обидві копії редагували.

PHP
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";
}

Кожен знімок створюють із бази даних, у якій зберігається відповідний стан. Три знімки, послідовно створені з однієї бази даних, не описують дві розгалужені копії: theirs уже міститиме зміни ours, а кожен вставлений ours рядок буде визначено як конфлікт insert_insert.

Результат містить операції, які перетворюють base на об’єднаний стан і походять від обох сторін; кожна позначена своїм джерелом (ours, theirs або merged). Щоб записати зміни ізольованого середовища в робочу базу, застосуйте операції, яких у ній ще немає, за допомогою Захищене застосування.

Або з CLI, який підключається до бази даних, указаної в MERQL_DB_DSN для кожного 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

Чому merql

Більшість інструментів для порівняння стану баз даних працюють на рівні рядків: якщо в двох версіях відрізняється хоча б один стовпець рядка, весь рядок позначається як конфлікт. Через це виникають хибні конфлікти, коли дві людини редагують різні стовпці того самого рядка.

merql виконує злиття на рівні стовпців. Якщо одна сторона змінила title, а інша — status, ці зміни можна об’єднати без конфлікту. Конфлікт виникає лише тоді, коли обидві сторони змінюють той самий стовпець на різні значення.

Для стовпців TEXT і JSON merql може перейти ще на один рівень глибше за допомогою злиття на рівні комірок. Дві правки різних рядків у текстовому стовпці або різних ключів стовпця JSON можна об’єднати тим самим тристороннім алгоритмом. Злиття на рівні комірок вмикається окремо: передайте CellMergeConfig у ThreeWayMerge. Фасад Merql і CLI виконують злиття лише на рівні стовпців. Див. Злиття на рівні комірок.

Модель злиття

Код
         Base (snapshot at fork time)
        /                              \
   Ours (one copy's changes)            Theirs (the other copy's changes)
        \                              /
         --------- MERGE ----------
                     |
              Merged result
              + conflicts (if any)

Три стани, як у git. Base — спільний предок. Ours і theirs — дві копії після розгалуження, кожна знята зі своєї бази даних. Правила злиття однаково застосовуються до обох сторін. Ці назви фігурують у source кожної операції, у типах конфліктів (update_delete означає, що ours оновлено, а theirs видалено), а також у політиках розв’язання OursWins і TheirsWins. Без спільної бази неможливо відрізнити «додано» від «не змінено».

Злиття саме по собі нічого не записує. Його операції перетворюють base на об’єднаний стан, тому Merql::apply() без помилок виконує їх лише для бази даних, у якій досі зберігається base. Щоб перенести зміни однієї сторони до бази даних, яка вже містить зміни іншої, наприклад зміни в ізольованому середовищі — у робочу базу, застосуйте лише відсутні в ній операції; див. Захищене застосування.

Вимоги

  • PHP 8.2 або новіша версія.
  • ext-mbstring і ext-zlib, потрібні залежності pitmaster/pitmaster, яка забезпечує порівняння тексту.
  • Розширення бази даних для використовуваного адаптера: ext-pdo з ext-pdo_sqlite або ext-pdo_mysql чи ext-mysqli. composer.json merql лише рекомендує ext-pdo і ext-mysqli, тому Composer не перевіряє їх наявність.
  • База даних MySQL або SQLite. Для інших рушіїв потрібен власний драйвер.

Чим цей інструмент не є

merql не є інструментом для міграції схеми. Він не реплікує бази даних і не синхронізує їх у реальному часі. Він розв’язує одну конкретну задачу: на основі трьох станів бази даних створює четвертий, об’єднаний стан із зазначенням конфліктів.

Документація