
# Atšaukimas

Atšaukimo planuose aprašoma, kaip atšaukti pasirinktas sujungimo operacijas jas pritaikius. Jie sudaromi prieš pritaikymą, naudojant tikslias tiesiogines eilutes, kurios egzistuoja prieš pat įrašymą.

Nesudarykite atšaukimo plano vien iš bazinės momentinės kopijos. Bazinė versija yra bendras protėvis, o ne būtinai tiesioginė būsena, kuri bus perrašyta.

## Atšaukimo plano sudarymas

```php
use Merql\Plan\ChangeGroupSelection;
use Merql\Rollback\RollbackPlanBuilder;
use Merql\Snapshot\SnapshotStore;

$selection = ChangeGroupSelection::fromIds([$plan->changeGroups[0]->id]);
$operationSelection = $selection->toOperationSelection($plan);
$expectedLive = SnapshotStore::load($plan->theirsSnapshot);

$liveBeforeRows = [];
foreach ($plan->operations as $operation) {
    if (!$operationSelection->contains($operation->id)) {
        continue;
    }

    $liveBeforeRows[$operation->id] = $expectedLive
        ->getTable($operation->table)
        ?->getRow($operation->rowKey);
}

$rollback = (new RollbackPlanBuilder())->build(
    'rollback-1',
    $plan,
    $operationSelection,
    $liveBeforeRows,
    metadata: ['promotion_id' => 'promotion-1'],
);
```

Plane saugoma:

- sujungimo plano ID,
- pasirinktų operacijų ID,
- paveiktos lentelės,
- atvirkštinės operacijos,
- eilutės prieš pakeitimą,
- numatytos eilutės po pakeitimo,
- metaduomenys,
- atšaukimo maiša,
- schemos versija.

## Neatitikimų patikra

Prieš pritaikydami atšaukimą, palyginkite dabartines tiesiogines eilutes su atšaukimo plane numatytomis eilutėmis po pakeitimo. Jei po perkėlimo kuri nors eilutė pasikeitė, pagal numatytąją nuostatą nutraukite operaciją.

```php
use Merql\Rollback\RollbackDrift;

$currentRows = [
    $operationId => $currentLiveRow,
];

$drifted = (new RollbackDrift())->driftedOperationIds($rollback, $currentRows);

if ($drifted !== []) {
    throw new RuntimeException('Rollback drifted: ' . implode(', ', $drifted));
}
```

Neatitikimų patikra lygina eilučių kontrolines žymas, todėl stulpelių tvarka nesvarbi.

## Atšaukimo pritaikymas

Jei neatitikimų nėra, pritaikykite atvirkštines operacijas.

```php
use Merql\Rollback\RollbackPlanApplier;

$applied = (new RollbackPlanApplier($connection))->apply($rollback);

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

Atšaukimas pritaiko įprastą `MergeResult`, sudarytą iš atvirkštinių operacijų. Jis vykdomas tuo pačiu operacijų taikytojo operacijų su transakcijomis keliu kaip įprastas sujungimo pritaikymas.

## Serializavimas

Prieš pritaikydami sujungimą į priekį, išsaugokite atšaukimo planus. Šis artefaktas yra patikimas pagrindas vėlesniam atšaukimui.

```php
use Merql\Rollback\RollbackPlanSerializer;

$json = RollbackPlanSerializer::toJson($rollback);
$restored = RollbackPlanSerializer::fromJson($json);
```

Programos turėtų išsaugoti serializuotą planą kartu su operacija, kuri atliko įrašymą į tiesioginę duomenų bazę, ir visais konkrečiai pagrindinei programai skirtais audito metaduomenimis.
