Zastosowanie z kontrolą
GuardedApplier stosuje wynik MergeResult tylko wtedy, gdy docelowa baza danych nadal odpowiada oczekiwanemu zrzutowi bieżącego stanu. Przydaje się to, gdy plan scalania został wcześniej sprawdzony, a bieżące wiersze mogły się zmienić przed zastosowaniem.
Typowa konfiguracja przenosi zmiany z piaskownicy do bazy produkcyjnej. Podstawa to stan produkcyjny z chwili skopiowania go do piaskownicy, „nasze” to zmodyfikowana piaskownica, a „ich” to bieżący stan produkcyjny. Celem jest baza produkcyjna, więc oczekiwanym zrzutem bieżącego stanu jest theirs, a operacje do zapisania to te, których brakuje w bazie produkcyjnej (zob. Wybieranie operacji).
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() zapisuje zmiany w bazie przekazanej do Merql::init(). Oczekiwany zrzut bieżącego stanu musi opisywać tę samą bazę: pobierz go z celu i zadbaj, by był wystarczająco aktualny, aby warunki wstępne miały znaczenie.
Warunki wstępne
Zastosowanie z kontrolą SQL dodaje do generowanych instrukcji warunki wstępne dotyczące bieżących wierszy:
- aktualizacje wymagają, aby wiersz o wskazanej tożsamości istniał w bazie docelowej, a wartości kolumn niebędących kolumnami identyfikacyjnymi odpowiadały oczekiwanemu bieżącemu wierszowi,
- usunięcia wymagają, aby wiersz o wskazanej tożsamości istniał w bazie docelowej, a wartości kolumn niebędących kolumnami identyfikacyjnymi odpowiadały oczekiwanemu bieżącemu wierszowi,
- wstawienia wymagają, aby w bazie docelowej nie istniał jeszcze wiersz o danej tożsamości.
Jeśli dowolna instrukcja zmieni zero wierszy, merql uznaje plan za nieaktualny. Transakcja zostaje wycofana, a zwrócony ApplyResult zawiera błąd Optimistic precondition failed for guarded merge statement.
W MySQL aktualizacja, której wartości są już takie same jak w bieżącym wierszu, również zmienia zero wierszy, ponieważ MySQL zlicza zmienione wiersze, a nie wiersze spełniające warunek. Taka aktualizacja kończy się błędem z powodu nieaktualnego planu, mimo że wiersz jest zgodny (#2). SQLite zlicza wiersze spełniające warunek i akceptuje taką aktualizację. Może się to zdarzyć podczas aktualizacji merged, gdy obie strony wprowadziły tę samą zmianę w wierszu.
Wybieranie operacji
Wynik scalania zawiera operacje obu stron, ponieważ przekształca stan podstawowy w stan scalony. Zastosowane do celu, który zawiera już stan „ich”, operacje pochodzące od nich opisują zmiany, które cel już zawiera, więc ich warunki wstępne nie zostaną spełnione:
| Operacja | Stan w celu zawierającym zmiany „ich” | Wybierz |
|---|---|---|
ours wstawienie, aktualizacja lub usunięcie |
Brak | Tak |
merged aktualizacja (obie strony zmieniły wiersz) |
Zawiera kolumny „ich”, brakuje kolumn „naszych” | Tak |
theirs wstawienie |
Wiersz istnieje, więc wstawienie z kontrolą zmienia zero wierszy | Nie |
merged usunięcie (obie strony usunęły wiersz) |
Wiersz nie istnieje, więc usunięcie z kontrolą zmienia zero wierszy | Nie |
theirs aktualizacja lub usunięcie |
Już zastosowano | Nie |
Przekazanie całego wyniku Merql::merge() do applyGuarded() zakończy się więc błędem, gdy tylko strona „ich” wstawi wiersz lub obie strony go usuną. Najpierw wybierz operacje według źródła, jak powyżej, albo za pomocą ChangeGroupSelection utworzonego na podstawie przeglądu UI. Pomiń wiersze wymienione w $plan->conflicts: wybrany wynik jest zawsze wolny od konfliktów, a operacja dotycząca wiersza w konflikcie zawiera wartość strony „naszej” w kolumnie, której dotyczy konflikt (zob. Plany scalania).
Zastosowanie wybranego wyniku
Zastosowanie z kontrolą działa z wybranymi wynikami scalania utworzonymi na podstawie planu scalania, w tym z wyborami dokonanymi według grup zmian.
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));
Pozwala to aplikacji nadrzędnej przygotować podzbiór sprawdzonych grup i nadal zagwarantować, że bieżące wiersze nie zmieniły się od utworzenia planu.
Podgląd SQL
Użyj SqlGenerator::generateGuarded(), aby sprawdzić instrukcje z kontrolą bez ich wykonywania.
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;
$statements = SqlGenerator::generateGuarded(
$selected,
SnapshotStore::load($plan->theirsSnapshot),
);
foreach ($statements as $statement) {
echo $statement['sql'] . "\n";
}
Wygenerowane instrukcje nadal używają parametrów. Warunki wstępne są wyrażone za pomocą dodatkowych klauzul WHERE dla aktualizacji i usunięć oraz sprawdzeń NOT EXISTS dla wstawień.
Zastosowanie z kontrolą lub zwykłe
Merql::apply() wykonuje te same operacje bez warunków wstępnych. Aktualizacje i usunięcia powiodą się niezależnie od tego, czy bieżący wiersz zmienił się od utworzenia planu, a wstawienie istniejącego wiersza zakończy się błędem duplikatu klucza bazy danych. Użyj zwykłego zastosowania dla celu, który na pewno nadal zawiera stan podstawowy, a zastosowania z kontrolą, gdy inni użytkownicy mogli zmienić cel od czasu utworzenia zrzutu.