Pāriet uz saturu

Aizsargāta izpilde

GuardedApplier lieto tīru MergeResult tikai tad, ja mērķa datubāze joprojām atbilst paredzētajam tiešsaistes momentuzņēmumam. Tas noder, ja apvienošanas plāns tika pārskatīts iepriekš, bet tiešsaistes rindas pirms izpildes, iespējams, ir mainījušās.

Parasti izolētā vidē veiktās izmaiņas tiek pārnestas uz tiešsaistes datubāzi. Bāze ir tās stāvoklis brīdī, kad no tās tika izveidota izolētā vide; mūsu puse ir rediģētā izolētā vide, bet viņu puse — pašreizējais tiešsaistes stāvoklis. Mērķis ir tiešsaistes datubāze, tāpēc paredzētais tiešsaistes momentuzņēmums ir theirs, un ierakstāmas ir tās darbības, kuru tiešsaistes datubāzē vēl nav (sk. Darbību izvēle).

PHP
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() raksta datubāzē, kas nodota funkcijai Merql::init(). Paredzētajam tiešsaistes momentuzņēmumam jāatbilst tai pašai datubāzei: iegūstiet to no mērķa datubāzes pietiekami nesen, lai priekšnosacījumi būtu jēgpilni.

Priekšnosacījumi

Aizsargāta SQL pievieno ģenerētajiem priekšrakstiem tiešsaistes rindu priekšnosacījumus:

  • atjaunināšanai nepieciešams, lai mērķa rindas identitāte pastāvētu un kolonnas, kas nav identitātes kolonnas, atbilstu paredzētajai tiešsaistes rindai,
  • dzēšanai nepieciešams, lai mērķa rindas identitāte pastāvētu un kolonnas, kas nav identitātes kolonnas, atbilstu paredzētajai tiešsaistes rindai,
  • ievietošanai nepieciešams, lai mērķī šāda identitāte vēl nepastāvētu.

Ja kāds priekšraksts ietekmē nulli rindu, merql uzskata plānu par novecojušu. Darījums tiek atcelts, un atgrieztajā ApplyResult ir kļūda Optimistic precondition failed for guarded merge statement.

MySQL gadījumā atjauninājums, kura vērtības jau atbilst tiešsaistes rindai, arī ietekmē nulli rindu, jo MySQL uzskaita izmainītās, nevis atrastās rindas. Tāpēc šāds atjauninājums tiek uzskatīts par novecojušu, lai gan rinda atbilst (#2). SQLite uzskaita atrastās rindas un to pieņem. Tas var notikt ar merged atjauninājumu, ja abas puses veikušas vienādas izmaiņas rindā.

Darbību izvēle

Apvienošanas rezultātā ir abu pušu darbības, jo tas pārvērš bāzi apvienotajā stāvoklī. Lietojot rezultātu mērķī, kurā jau ir viņu puses stāvoklis, viņu puses darbības apraksta izmaiņas, kas mērķī jau ir veiktas, un to priekšnosacījumi neizpildās:

Darbība Stāvoklis mērķī, kurā ir viņu puses izmaiņas Atlasīt
ours ievietošana, atjaunināšana vai dzēšana Trūkst Jā
merged atjaunināšana (rindu mainījušas abas puses) Ir viņu puses kolonnas, trūkst mūsu puses kolonnu Jā
theirs ievietošana Rinda jau pastāv, tāpēc aizsargātā ievietošana ietekmē nulli rindu Nē
merged dzēšana (abas puses izdzēsušas rindu) Rinda ir dzēsta, tāpēc aizsargātā dzēšana ietekmē nulli rindu Nē
theirs atjaunināšana vai dzēšana Jau lietota Nē

Tāpēc, nododot Merql::merge() visu rezultātu funkcijai applyGuarded(), izpilde neizdodas, tiklīdz viņu puse ir ievietojusi rindu vai abas puses ir to izdzēsušas. Vispirms atlasiet darbības pēc avota, kā parādīts iepriekš, vai ar ChangeGroupSelection, kas izveidota pēc pārskatīšanas UI. Izlaidiet rindas, kas norādītas $plan->conflicts: atlasītais rezultāts vienmēr ir tīrs, un konfliktējošas rindas darbībā ir mūsu puses vērtība konfliktējošajā kolonnā (sk. Apvienošanas plāni).

Atlasīta izpilde

Aizsargāta izpilde darbojas ar atlasītiem apvienošanas rezultātiem, kas iegūti no apvienošanas plāna, tostarp atlasēm pēc izmaiņu grupas.

PHP
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));

Tādējādi resursdatora lietotne var pakāpeniski sagatavot pārskatīto grupu apakškopu un joprojām pārbaudīt, vai tiešsaistes rindas pēc plāna izveides nav mainījušās.

SQL priekšskatījums

Izmantojiet SqlGenerator::generateGuarded(), lai apskatītu aizsargātos priekšrakstus, tos neizpildot.

PHP
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;

$statements = SqlGenerator::generateGuarded(
    $selected,
    SnapshotStore::load($plan->theirsSnapshot),
);

foreach ($statements as $statement) {
    echo $statement['sql'] . "\n";
}

Ģenerētie priekšraksti joprojām ir parametrizēti. Priekšnosacījumi ir izteikti ar papildu WHERE klauzulām atjauninājumiem un dzēšanām, kā arī NOT EXISTS pārbaudēm ievietošanai.

Aizsargāta vai parasta izpilde

Merql::apply() izpilda tās pašas darbības bez priekšnosacījumiem. Atjauninājumi un dzēšanas izdodas neatkarīgi no tā, vai tiešsaistes rinda kopš plāna izveides ir mainījusies, savukārt, ievietojot jau esošu rindu, rodas datubāzes dublētās atslēgas kļūda. Izmantojiet parasto izpildi mērķim, kurā, kā zināt, joprojām ir bāzes stāvoklis, un aizsargāto izpildi, ja citi rakstītāji, iespējams, ir mainījuši mērķi kopš tā momentuzņēmuma iegūšanas.