Apsaugotas pritaikymas
GuardedApplier pritaiko švarų MergeResult tik tuo atveju, jei tikslinė duomenų bazė vis dar atitinka numatytą tiesioginę momentinę kopiją. Tai naudinga, kai sujungimo planas buvo peržiūrėtas anksčiau, o tiesioginės eilutės iki pritaikymo galėjo pasikeisti.
Įprastai smėliadėžės pakeitimai perkeliami į tiesioginę duomenų bazę. Bazinė versija yra tiesioginė duomenų bazė tuo metu, kai iš jos buvo nukopijuota smėliadėžė; mūsų versija yra redaguota smėliadėžė; jų versija – dabartinė tiesioginė duomenų bazė. Tikslas yra tiesioginė duomenų bazė, todėl numatyta tiesioginė momentinė kopija yra theirs, o įrašomos tos operacijos, kurių tiesioginėje duomenų bazėje dar nėra (žr. Operacijų pasirinkimas).
use Merql\Connection;
use Merql\Merql;
use Merql\Plan\OperationSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;
Merql::init(Connection::sqlite('/data/live.sqlite'));
$plan = Merql::plan('promote-1', 'base', 'ours', 'theirs');
// Skip rows with unresolved conflicts.
$conflicted = [];
foreach ($plan->conflicts as $conflict) {
$conflicted[$conflict->table . "\0" . $conflict->rowKey] = true;
}
$ids = [];
foreach ($plan->operations as $operation) {
$missing = $operation->source === 'ours'
|| ($operation->source === 'merged' && $operation->type === 'update');
if ($missing && !isset($conflicted[$operation->table . "\0" . $operation->rowKey])) {
$ids[] = $operation->id;
}
}
$selected = (new SelectedMergeResultFactory())->fromOperationSelection(
$plan,
OperationSelection::fromIds($ids),
SnapshotStore::load('base'),
);
$applied = Merql::applyGuarded($selected, SnapshotStore::load('theirs'));
if ($applied->hasErrors()) {
foreach ($applied->errors() as $error) {
echo $error . "\n";
}
}
Merql::applyGuarded() įrašo į duomenų bazę, perduotą Merql::init(). Numatyta tiesioginė momentinė kopija turi aprašyti tą pačią duomenų bazę: paimkite ją iš tikslo ir užfiksuokite pakankamai neseniai, kad išankstinės sąlygos būtų prasmingos.
Išankstinės sąlygos
Apsaugotas SQL prie generuojamų sakinių prideda tiesioginių eilučių išankstines sąlygas:
- atnaujinant reikalaujama, kad tikslinės eilutės tapatybė egzistuotų, o ne tapatybės stulpelių reikšmės sutaptų su numatyta tiesiogine eilute,
- trinant reikalaujama, kad tikslinės eilutės tapatybė egzistuotų, o ne tapatybės stulpelių reikšmės sutaptų su numatyta tiesiogine eilute,
- įterpiant reikalaujama, kad tikslinė tapatybė dar neegzistuotų.
Jei kuris nors sakinys paveikia nulį eilučių, merql laiko planą pasenusiu. Operacija atšaukiama, o grąžinamame ApplyResult yra klaida Optimistic precondition failed for guarded merge statement..
Naudojant MySQL, atnaujinimas, kurio reikšmės jau sutampa su tiesiogine eilute, taip pat paveikia nulį eilučių, nes MySQL skaičiuoja pakeistas, o ne atrinktas eilutes. Toks atnaujinimas laikomas pasenusiu, nors eilutė sutampa (#2). SQLite skaičiuoja atrinktas eilutes ir jį priima. Taip gali nutikti atliekant merged atnaujinimą, kai abi pusės vienodai pakeitė eilutę.
Operacijų pasirinkimas
Sujungimo rezultate yra abiejų pusių operacijos, nes jos paverčia bazinę versiją sujungta būsena. Pritaikius rezultatą tikslui, kuriame jau yra jų versija, iš jų pusės kilusios operacijos aprašo pakeitimus, kuriuos tikslas jau turi, todėl jų išankstinės sąlygos neįvykdomos:
| Operacija | Tikslinės duomenų bazės, kurioje yra jų versija, būsena | Pasirinkti |
|---|---|---|
ours įterpimas, atnaujinimas arba trynimas |
Nėra | Taip |
merged atnaujinimas (abi pusės pakeitė eilutę) |
Yra jų stulpeliai, trūksta mūsų stulpelių | Taip |
theirs įterpimas |
Eilutė egzistuoja, todėl apsaugotas įterpimas paveikia nulį eilučių | Ne |
merged trynimas (abi pusės ištrynė eilutę) |
Eilutės nebėra, todėl apsaugotas trynimas paveikia nulį eilučių | Ne |
theirs atnaujinimas arba trynimas |
Jau pritaikyta | Ne |
Todėl perduodant visą Merql::merge() rezultatą į applyGuarded(), pritaikymas nepavyksta vos tik jų pusė įterpia eilutę arba abi pusės ją ištrina. Pirmiausia pasirinkite operacijas pagal šaltinį, kaip nurodyta pirmiau, arba naudodami ChangeGroupSelection, sudarytą iš peržiūros UI. Praleiskite $plan->conflicts nurodytas eilutes: pasirinktas rezultatas visada būna be konfliktų, o operacijoje, skirtoje konfliktuojančiai eilutei, konflikto stulpelyje pateikiama mūsų reikšmė (žr. Sujungimo planai).
Pasirinktas pritaikymas
Apsaugotas pritaikymas veikia su pasirinktais sujungimo rezultatais, gautais iš sujungimo plano, įskaitant pakeitimų grupėmis atliktus pasirinkimus.
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;
$selection = ChangeGroupSelection::fromIds([$plan->changeGroups[0]->id]);
$selected = (new SelectedMergeResultFactory())->fromChangeGroupSelection(
$plan,
$selection,
SnapshotStore::load($plan->baseSnapshot),
);
$applied = Merql::applyGuarded($selected, SnapshotStore::load($plan->theirsSnapshot));
Taip pagrindinė programa gali paruošti peržiūrėtų grupių poaibį ir vis tiek patikrinti, kad po plano sukūrimo tiesioginės eilutės nepasikeitė.
SQL peržiūra
Naudokite SqlGenerator::generateGuarded(), kad peržiūrėtumėte apsaugotus sakinius jų nevykdydami.
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;
$statements = SqlGenerator::generateGuarded(
$selected,
SnapshotStore::load($plan->theirsSnapshot),
);
foreach ($statements as $statement) {
echo $statement['sql'] . "\n";
}
Generuojami sakiniai ir toliau parametrizuojami. Atnaujinimų ir trynimų išankstinės sąlygos išreiškiamos papildomomis WHERE sąlygomis, o įterpimų – NOT EXISTS patikromis.
Apsaugotas arba įprastas pritaikymas
Merql::apply() vykdo tas pačias operacijas be išankstinių sąlygų. Tada atnaujinimai ir trynimai pavyksta nepaisant to, ar tiesioginė eilutė pasikeitė po plano sudarymo, o įterpus jau esamą eilutę pateikiama duomenų bazės pasikartojančio rakto klaida. Įprastą pritaikymą naudokite tikslui, kuriame, kaip žinote, vis dar yra bazinė versija, o apsaugotą – kai kiti rašytojai galėjo pakeisti tikslą po jo momentinės kopijos sukūrimo.