Přejít k obsahu

Úvod

merql je knihovna v čistém PHP, která provádí třícestná sloučení stavů databáze. Pořizuje snímky tabulek, vypočítává sady změn mezi snímky, slučuje dvě sady změn vůči společnému základu a zjišťuje konflikty na úrovni sloupců. Používá stejný algoritmus jako git u zdrojových souborů, ale pracuje s řádky a sloupci místo řádků textu.

Bez externích nástrojů. Podporuje MySQL a SQLite prostřednictvím připojitelných adaptérů připojení a ovladačů dialektů.

Rychlý příklad

Sloučení vyžaduje tři stavy stejných dat: základní stav, ze kterého vycházely obě kopie, a obě kopie po samostatných změnách. Zde byla izolovaná kopie vytvořena z ostré databáze a od té doby byly upraveny obě.

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

Každý snímek se pořizuje z databáze, která obsahuje příslušný stav. Pořízení všech tří snímků postupně z jedné databáze nepopisuje dvě odlišné kopie: theirs by už obsahoval změny z ours a každý řádek vložený v ours by byl označen jako konflikt insert_insert.

Výsledek uvádí operace, které převedou základní stav na sloučený, včetně změn z obou stran. Každá operace je označena zdrojem (ours, theirs nebo merged). Chcete-li zapsat změny izolované kopie do ostré databáze, použijte Podmíněné použití změn pouze pro operace, které v ostré databázi ještě nejsou.

Nebo z CLI, který se pro každý snapshot připojí k databázi uvedené v MERQL_DB_DSN:

Shell
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

Proč merql

Většina nástrojů pro porovnávání stavu databází pracuje na úrovni řádků: pokud se mezi dvěma verzemi liší kterýkoli sloupec řádku, celý řádek se označí jako konflikt. Tím vznikají falešné konflikty, když dva lidé upraví různé sloupce téhož řádku.

merql slučuje na úrovni sloupců. Pokud jedna strana změnila title a druhá status, změny se sloučí bez konfliktu. Konflikt vzniká pouze tehdy, když obě strany změní stejný sloupec na různé hodnoty.

U sloupců TEXT a JSON může merql jít ještě o úroveň níž a slučovat obsah buněk. Dvě úpravy různých řádků textového sloupce nebo různých klíčů sloupce JSON lze sloučit stejným třícestným algoritmem. Slučování buněk je volitelné: předejte CellMergeConfig do ThreeWayMerge. Rozhraní Merql a CLI slučují pouze na úrovni sloupců. Viz Sloučení na úrovni buněk.

Model sloučení

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

Tři stavy, stejně jako v git. Base je společný předek. Ours a theirs jsou dvě kopie po rozdělení, každá zachycená z vlastní databáze. Pravidla sloučení zacházejí s oběma stranami stejně. Tyto názvy se objevují v poli source každé operace, v typech konfliktů (update_delete znamená, že ours aktualizovalo a theirs odstranilo) i v zásadách řešení OursWins a TheirsWins. Bez společného základu nelze rozlišit mezi „přidáno“ a „nezměněno“.

Samotné sloučení nic nezapisuje. Jeho operace převedou základní stav na sloučený, takže Merql::apply() je bezpečně provede pouze v databázi, která stále obsahuje základní stav. Chcete-li přenést změny jedné strany do databáze, která už obsahuje změny druhé strany, například úpravy izolované kopie do ostré databáze, použijte jen operace, které v ní chybí; viz Podmíněné použití změn.

Požadavky

  • PHP 8.2 nebo novější.
  • ext-mbstring a ext-zlib, které vyžaduje závislost pitmaster/pitmaster poskytující rozdíly textu.
  • Rozšíření databáze pro použitý adaptér: ext-pdo s ext-pdo_sqlite nebo ext-pdo_mysql, případně ext-mysqli. composer.json v merql pouze doporučuje ext-pdo a ext-mysqli, proto Composer jejich přítomnost nekontroluje.
  • Databázi MySQL nebo SQLite. Ostatní databázové stroje vyžadují vlastní ovladač.

Co merql není

merql není nástroj pro migraci schématu. Nereplikuje databáze ani je nesynchronizuje v reálném čase. Řeší jeden konkrétní problém: ze tří stavů databáze vytvoří čtvrtý, sloučený stav a označí konflikty.

Dokumentace