
# Tilbagerulning

Tilbagerulningsplaner beskriver, hvordan valgte sammenfletningsoperationer fortrydes, efter at de er blevet anvendt. De opbygges før anvendelsen ud fra de nøjagtige live-rækker, der findes umiddelbart før skrivningen.

Generer ikke tilbagerulning alene ud fra basesnapshot. Base er den fælles forfader, ikke nødvendigvis den live-tilstand, der overskrives.

## Opret en tilbagerulningsplan

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

Planen gemmer:

- ID for sammenfletningsplanen,
- ID'er for valgte operationer,
- berørte tabeller,
- inverse operationer,
- rækker før ændringen,
- forventede rækker efter ændringen,
- metadata,
- et tilbagerulningshash,
- en skemaversion.

## Afvigelseskontrol

Før du anvender tilbagerulning, skal du sammenligne de aktuelle live-rækker med de forventede rækker efter planen for tilbagerulning. Hvis en række er ændret siden promoveringen, skal du som standard afbryde.

```php
use Merql\Rollback\RollbackDrift;

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

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

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

Afvigelseskontrollen sammenligner rækkefingeraftryk, så kolonnernes rækkefølge er uden betydning.

## Anvend tilbagerulning

Når der ikke er afvigelser, skal du anvende de inverse handlinger.

```php
use Merql\Rollback\RollbackPlanApplier;

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

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

Tilbagerulning anvender en normal `MergeResult`, der er bygget af inverse handlinger. Den kører gennem samme transaktionelle anvenderforløb som almindelig fletningsanvendelse.

## Serialisering

Gem planer for tilbagerulning, før du anvender den fremadrettede fletning. Dette artefakt er det varige grundlag for senere tilbagerulning.

```php
use Merql\Rollback\RollbackPlanSerializer;

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

Programmer bør gemme den serialiserede plan sammen med den handling, der udførte live-skrivningen, samt eventuelle værtsspecifikke revisionsmetadata.
