
# Palautus

Palautussuunnitelmat kuvaavat, miten valitut yhdistämisoperaatiot perutaan niiden käyttöönoton jälkeen. Ne luodaan ennen käyttöönottoa käyttäen reaaliaikaisia rivejä täsmälleen sellaisina kuin ne ovat juuri ennen kirjoitusta.

Älä luo palautusta pelkän pohjatilan tilannevedoksen perusteella. Pohja on yhteinen edeltäjä, eikä se välttämättä kuvaa ylikirjoitettavaa reaaliaikaista tilaa.

## Palautussuunnitelman luonti

```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'],
);
```

Suunnitelma tallentaa seuraavat tiedot:

- yhdistämissuunnitelman tunnus,
- valittujen operaatioiden tunnukset,
- käsitellyt taulut,
- käänteiset operaatiot,
- rivit ennen muutosta,
- odotetut rivit muutoksen jälkeen,
- metatiedot,
- palautuksen tiiviste,
- skeemaversio.

## Muutosten tarkistus

Ennen palautuksen käyttöönottoa vertaa nykyisiä reaaliaikaisia rivejä palautussuunnitelman odotettuihin jälkitilan riveihin. Jos jokin rivi on muuttunut muutosten siirron jälkeen, keskeytä oletusarvoisesti.

```php
use Merql\Rollback\RollbackDrift;

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

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

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

Muutosten tarkistus vertaa rivien sormenjälkiä, joten sarakkeiden järjestyksellä ei ole merkitystä.

## Palautuksen käyttöönotto

Kun muutoksia ei ole, ota käänteiset operaatiot käyttöön.

```php
use Merql\Rollback\RollbackPlanApplier;

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

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

Palautus ottaa käyttöön tavallisen `MergeResult`-tuloksen, joka on luotu käänteisistä operaatioista. Se suoritetaan samassa tapahtumallisessa sovelluspolussa kuin tavallinen yhdistämisen käyttöönotto.

## Sarjallistaminen

Tallenna palautussuunnitelmat ennen eteenpäin suuntautuvan yhdistämisen käyttöönottoa. Tämä aineisto toimii myöhemmän palautuksen pysyvänä perustana.

```php
use Merql\Rollback\RollbackPlanSerializer;

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

Sovellusten tulisi tallentaa sarjallistettu suunnitelma reaaliaikaisen kirjoituksen tehneen operaation yhteyteen sekä mahdolliset isäntäkohtaiset auditointimetatiedot.
