
# Retour arrière

Les plans de retour arrière décrivent comment annuler des opérations de fusion sélectionnées après leur application. Ils sont créés avant l'application, à partir des lignes exactes présentes en temps réel juste avant l'écriture.

Ne générez pas un retour arrière à partir du seul instantané de base. La base est l'ancêtre commun, pas nécessairement l'état en temps réel qui est remplacé.

## Créer un plan de retour arrière

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

Le plan stocke :

- l'ID du plan de fusion,
- les ID des opérations sélectionnées,
- les tables concernées,
- les opérations inverses,
- les lignes avant,
- les lignes après attendues,
- les métadonnées,
- un hachage de restauration,
- une version du schéma.

## Vérification de dérive

Avant d'appliquer la restauration, comparez les lignes actives actuelles aux lignes après attendues du plan de restauration. Si une ligne a changé depuis la promotion, annulez par défaut.

```php
use Merql\Rollback\RollbackDrift;

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

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

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

La vérification de dérive compare les empreintes des lignes ; l'ordre des colonnes n'a donc pas d'importance.

## Appliquer la restauration

En l'absence de dérive, appliquez les opérations inverses.

```php
use Merql\Rollback\RollbackPlanApplier;

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

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

La restauration applique un `MergeResult` normal construit à partir des opérations inverses. Elle suit le même chemin d'application transactionnel que l'application d'une fusion ordinaire.

## Sérialisation

Enregistrez les plans de restauration avant d'appliquer la fusion dans le sens direct. Cet artefact constitue la base durable des restaurations ultérieures.

```php
use Merql\Rollback\RollbackPlanSerializer;

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

Les applications doivent enregistrer le plan sérialisé avec l'opération qui a effectué l'écriture active, ainsi que toute métadonnée d'audit propre à l'hôte.
