Pereiti prie turinio

Darbo pradžia

Šiame vadove aprašoma, kaip įdiegti programą, prisijungti prie duomenų bazės, kurti momentines kopijas, apskaičiuoti skirtumus ir atlikti trijų krypčių sujungimą.

Diegimas

Shell
composer require merql/merql

merql reikia PHP 8.2+ su ext-mbstring ir ext-zlib (abi reikalingos pitmaster/pitmaster priklausomybei). Taip pat reikia jūsų naudojamo adapterio plėtinio: ext-pdo su ext-pdo_sqlite arba ext-pdo_mysql PDO adapteriui arba ext-mysqli mysqli adapteriui. Composer tik pateikia rekomendacijas, todėl patikrinkite, ar reikiamas plėtinys įdiegtas.

Paketas įdiegia CLI kaip vendor/bin/merql. Naudokite merql 0.4.1 ar naujesnę versiją, kad veiktų CLI; ankstesnėse versijose dvejetainis failas negalėjo rasti autokrautuvo, kai merql buvo įdiegtas kaip priklausomybė.

Prisijungimas prie duomenų bazės

merql veikia naudodama nedidelę ryšio adapterio sąsają. Pateikiami integruoti PDO ir mysqli adapteriai bei MySQL ir SQLite dialektų tvarkyklės.

PHP
use Merql\Connection;

// SQLite (in-memory or file-based, through PDO).
$connection = Connection::sqlite(':memory:');
$connection = Connection::sqlite('/path/to/database.sqlite');

// MySQL through PDO.
$connection = Connection::mysql('127.0.0.1', 'my_database', 'root', 'password');

// MySQL through mysqli.
$connection = Connection::mysqli('127.0.0.1', 'my_database', 'root', 'password');

// Any MySQL or SQLite PDO DSN.
$connection = Connection::fromDsn('mysql:host=127.0.0.1;dbname=my_database;charset=utf8mb4', 'root', 'password');

fromDsn() atidaro bet kurį PDO palaikomą DSN, tačiau merql pateikiamos tik MySQL ir SQLite dialektų tvarkyklės. Naudojant kitą sistemą, pavyzdžiui, pgsql:, ryšys užmezgamas, tačiau Merql::init() pateikia Unsupported database driver: pgsql klaidą, kol jai neužregistruojate tvarkyklės. Žr. Duomenų bazių tvarkyklės.

Momentinės kopijos kūrimas

Momentinėje kopijoje užfiksuojamos visos kiekvienos lentelės eilutės (arba jų poaibis) kaip kontrolinės sumos ir duomenys. Kontrolinės sumos leidžia greitai aptikti pakeitimus; išsamūs eilučių duomenys pasiekiami, kai reikia apskaičiuoti skirtumą.

PHP
use Merql\Merql;

Merql::init($connection);

// Snapshot all tables.
$snapshot = Merql::snapshot('baseline');

// Snapshot specific tables.
$snapshot = Merql::snapshot('baseline', ['posts', 'comments', 'users']);

Momentinės kopijos pagal numatytuosius nustatymus išsaugomos kaip JSON failai kataloge .merql/snapshots/. Šią vietą galite pakeisti:

PHP
use Merql\Snapshot\SnapshotStore;

SnapshotStore::setDirectory('/var/data/merql-snapshots');

Skirtumo apskaičiavimas

Skirtumas palygina dvi momentines kopijas ir pateikia įterpimų, atnaujinimų bei ištrynimų pakeitimų rinkinį. Abi kopijos turi būti jau sukurtos; čia current yra antroji momentinė kopija, sukurta pasikeitus duomenų bazei.

PHP
$changeset = Merql::diff('baseline', 'current');

echo "Inserts: " . count($changeset->inserts()) . "\n";
echo "Updates: " . count($changeset->updates()) . "\n";
echo "Deletes: " . count($changeset->deletes()) . "\n";

// Inspect changes for a single table.
$postChanges = $changeset->forTable('posts');

// Check per-column diffs on updates.
foreach ($changeset->updates() as $update) {
    echo "Row {$update->rowKey} in {$update->table}:\n";
    foreach ($update->columnDiffs as $diff) {
        echo "  {$diff->column}: {$diff->oldValue} -> {$diff->newValue}\n";
    }
}

Trijų krypčių sujungimas

Pagrindinė operacija. Paimamos trys pavadintos momentinės kopijos (bazinė, mūsų ir jų) ir sujungiami du pakeitimų rinkiniai.

Bazinė kopija yra būsena, nuo kurios pradėjo abi kopijos. Mūsų ir jų kopijos yra dvi nepriklausomai pakeistos kopijos, todėl kiekviena turi būti užfiksuota iš ją turinčios duomenų bazės. Pavyzdžiui, kai bandomoji aplinka nukopijuota iš veikiančios duomenų bazės:

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');      // the sandbox with our edits
Merql::init($live);
Merql::snapshot('theirs');    // live with its own changes

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

echo "Operations: {$result->operationCount()}\n";
echo "Conflicts:  {$result->conflictCount()}\n";
echo "Clean:      " . ($result->isClean() ? 'yes' : 'no') . "\n";

Nedarykite visų trijų momentinių kopijų iš eilės iš tos pačios duomenų bazės. Tada jų kopijoje jau bus mūsų pakeitimai, todėl kiekviena mūsų įterpta eilutė bus nurodyta kaip insert_insert konfliktas.

Merql::merge() sujungia stulpelių lygmeniu. Norėdami papildomai sujungti TEXT ir JSON reikšmes, naudokite ThreeWayMerge su CellMergeConfig; žr. Sujungimas langelių lygmeniu.

Konfliktų sprendimas

Kai abi pusės tą patį stulpelį pakeičia skirtingomis reikšmėmis, merql pateikia konfliktą. Konfliktus galite išspręsti automatiškai arba patikrinti rankiniu būdu.

PHP
use Merql\Merge\ConflictPolicy;
use Merql\Merge\ConflictResolver;

if (!$result->isClean()) {
    // Auto-resolve: one side wins.
    $resolved = ConflictResolver::resolve($result, ConflictPolicy::TheirsWins);

    // Or inspect each conflict.
    foreach ($result->conflicts() as $conflict) {
        echo "Conflict on {$conflict->table()} row {$conflict->rowKey()}:\n";
        echo "  Type:   {$conflict->type()}\n";
        echo "  Column: {$conflict->column()}\n";
        echo "  Ours:   " . var_export($conflict->oursValue(), true) . "\n";
        echo "  Theirs: " . var_export($conflict->theirsValue(), true) . "\n";
    }
}

Sujungimo pritaikymas

Kai turite švarų sujungimo rezultatą (neišspręstų konfliktų nėra), pritaikykite jį duomenų bazei, nurodytai Merql::init().

Sujungimo rezultato operacijos paverčia bazinę būseną sujungta būsena ir apima abiejų pusių pakeitimus. Merql::apply() vykdo visas šias operacijas, todėl jos sėkmingai pritaikomos duomenų bazei, kurioje tebėra bazinė būsena. Pritaikius jas duomenų bazei, kurioje jau yra vienos pusės pakeitimų, tos pusės įterpimas susidurs su jau esančia eilute, nepavyks ir bus atšauktas visas sujungimas. Norėdami perkelti vienos pusės pakeitimus į duomenų bazę, kurioje jau yra kitos pusės pakeitimų (pavyzdžiui, bandomosios aplinkos pakeitimus į veikiančią aplinką), naudokite Apsaugotą pritaikymą ir pritaikykite tik trūkstamas operacijas.

PHP
$applied = Merql::apply($result);

echo "Rows affected: {$applied->rowsAffected()}\n";

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo "Error: {$error}\n";
    }
}

Pritaikymo programa visas operacijas vykdo vienoje transakcijoje. Jei kuris nors sakinys nepavyksta, visas sujungimas atšaukiamas, o klaida pateikiama errors(). Tokiu atveju rowsAffected() vis tiek skaičiuoja prieš klaidą vykdytus sakinius, nors jie buvo atšaukti (#4); pirmiausia patikrinkite hasErrors().

Bandomasis vykdymas

Peržiūrėkite SQL, kuriuos sujungimas sugeneruotų, jų nevykdydami.

PHP
use Merql\Apply\DryRun;

$statements = DryRun::generate($result);

foreach ($statements as $sql) {
    echo "{$sql};\n";
}

Dviejų krypčių pataisa

Jei turite tik bazinę kopiją ir pakeitimų rinkinį (be lygiagrečių „mūsų“ pakeitimų), naudokite patch(). Tai atitinka sujungimą, kai mūsų kopija nuo bazinės nesiskiria.

PHP
$result = Merql::patch('base', 'changes');
// Always clean: no concurrent edits means no conflicts.
Merql::apply($result);

CLI naudojimas

Visas šias operacijas taip pat galima atlikti komandinėje eilutėje. Išsamią informaciją rasite CLI nuorodoje.

Shell
export MERQL_DB_DSN="sqlite:mydb.sqlite"

vendor/bin/merql snapshot base
# ... change the database ...
vendor/bin/merql snapshot current
vendor/bin/merql diff base current

Trijų krypčių sujungimui sukurkite kiekvieną momentinę kopiją iš duomenų bazės, kurioje yra atitinkama būsena, o tada sujunkite:

Shell
MERQL_DB_DSN="sqlite:/data/live.sqlite" vendor/bin/merql snapshot base
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

Lentelių ir stulpelių filtravimas

Kuriant momentines kopijas galima neįtraukti lentelių (pvz., talpyklos ar seanso lentelių) ir ignoruoti dažnai kintančius stulpelius (pvz., updated_at laiko žymas).

PHP
use Merql\Filter\TableFilter;
use Merql\Filter\ColumnFilter;
use Merql\Snapshot\Snapshotter;

$snapshotter = new Snapshotter($connection);

$snapshot = $snapshotter->capture('baseline', filters: [
    TableFilter::exclude(['sessions', 'cache_*']),
    ColumnFilter::ignore(['updated_at', 'modified_date']),
]);

Tolesni veiksmai