Aplicare cu protecție
GuardedApplier aplică un MergeResult fără conflicte doar dacă baza de date țintă corespunde în continuare instantaneului live așteptat. Este util când un plan de îmbinare a fost revizuit mai devreme, iar rândurile live s-ar putea să se fi schimbat înainte de aplicare.
Configurația tipică promovează modificările dintr-un mediu sandbox într-o bază de date live. „Base” este live în momentul în care a fost copiat mediul sandbox, „ours” este mediul sandbox modificat, iar „theirs” este versiunea live actuală. Ținta este baza de date live, așadar instantaneul live așteptat este theirs, iar operațiile de scris sunt cele care încă lipsesc din versiunea live (consultați Alegerea operațiilor).
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() scrie în baza de date transmisă către Merql::init(). Instantaneul live așteptat trebuie să descrie aceeași bază de date: preluați-l din țintă și asigurați-vă că este suficient de recent pentru ca precondițiile să aibă relevanță.
Precondiții
SQL cu protecție adaugă precondiții pentru rândurile live la instrucțiunile generate:
- actualizările necesită ca identitatea rândului țintă să existe și ca valorile coloanelor care nu țin de identitate să corespundă rândului live așteptat,
- ștergerile necesită ca identitatea rândului țintă să existe și ca valorile coloanelor care nu țin de identitate să corespundă rândului live așteptat,
- inserările necesită ca identitatea țintă să nu existe deja.
Dacă o instrucțiune nu afectează niciun rând, merql consideră planul învechit. Tranzacția este anulată, iar ApplyResult returnat conține eroarea Optimistic precondition failed for guarded merge statement.
Pe MySQL, o actualizare ale cărei valori sunt deja identice cu cele ale rândului live afectează totuși zero rânduri, deoarece MySQL numără rândurile modificate, nu pe cele găsite. Actualizarea eșuează ca învechită, deși rândul corespunde (#2). SQLite numără rândurile găsite și acceptă actualizarea. Acest lucru se poate întâmpla la o actualizare merged când ambele părți au făcut aceeași modificare unui rând.
Alegerea operațiilor
Un rezultat al îmbinării conține operațiile ambelor părți, deoarece transformă „base” în starea îmbinată. Aplicate unei ținte care conține deja „theirs”, operațiile provenite din „theirs” descriu modificări pe care ținta le are deja, iar precondițiile lor eșuează:
| Operație | Starea într-o țintă care conține „theirs” | Selectați |
|---|---|---|
ours inserare, actualizare sau ștergere |
Lipsește | Da |
merged actualizare (ambele părți au modificat rândul) |
Conține coloanele „theirs”, lipsesc cele „ours” | Da |
theirs inserare |
Rândul există, așadar inserarea cu protecție nu afectează niciun rând | Nu |
merged ștergere (ambele părți au șters rândul) |
Rândul a dispărut, așadar ștergerea cu protecție nu afectează niciun rând | Nu |
theirs actualizare sau ștergere |
Deja aplicată | Nu |
Prin urmare, transmiterea întregului rezultat al Merql::merge() către applyGuarded() eșuează de îndată ce „theirs” a inserat un rând sau ambele părți au șters același rând. Selectați mai întâi operațiile, după sursă, ca mai sus, sau cu un ChangeGroupSelection creat pe baza unei UI de revizuire. Omiteți rândurile listate în $plan->conflicts: un rezultat selectat este întotdeauna fără conflicte, iar operația pentru un rând aflat în conflict conține valoarea „ours” în coloana cu conflict (consultați Planuri de îmbinare).
Aplicare selectată
Astfel, o aplicație gazdă poate pregăti un subset de grupuri revizuite și poate confirma totodată că rândurile live nu s-au modificat după crearea planului.
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));
Astfel, o aplicație gazdă poate pregăti un subset de grupuri revizuite și poate confirma în continuare că rândurile live nu s-au modificat după crearea planului.
Previzualizare SQL
Folosiți SqlGenerator::generateGuarded() pentru a inspecta instrucțiunile cu protecție fără să le executați.
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;
$statements = SqlGenerator::generateGuarded(
$selected,
SnapshotStore::load($plan->theirsSnapshot),
);
foreach ($statements as $statement) {
echo $statement['sql'] . "\n";
}
Instrucțiunile generate rămân parametrizate. Precondițiile sunt exprimate prin clauze suplimentare WHERE pentru actualizări/ștergeri și verificări NOT EXISTS pentru inserări.
Aplicare cu protecție sau simplă
Merql::apply() execută aceleași operații fără precondiții. Astfel, actualizările și ștergerile reușesc indiferent dacă rândul live s-a schimbat de la crearea planului, iar inserarea unui rând existent eșuează cu eroarea de cheie duplicată a bazei de date. Folosiți aplicarea simplă pentru o țintă despre care știți că încă reflectă „base”, iar aplicarea cu protecție când este posibil ca alți autori să fi modificat ținta de la crearea instantaneului.