
# Ripristino

I piani di ripristino descrivono come annullare le operazioni di fusione selezionate dopo che sono state applicate. Si creano prima dell'applicazione, usando le esatte righe live presenti immediatamente prima della scrittura.

Non generate un ripristino basandovi solo sullo snapshot di base. Base è l'antenato comune, non necessariamente lo stato live che viene sovrascritto.

## Creare un piano di ripristino

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

Il piano memorizza:

- l'ID del piano di fusione,
- gli ID delle operazioni selezionate,
- le tabelle interessate,
- le operazioni inverse,
- le righe precedenti,
- le righe successive attese,
- i metadati,
- un hash di ripristino,
- una versione dello schema.

## Verifica delle modifiche

Prima di applicare il ripristino, confrontate le righe live attuali con le righe successive attese del piano. Se una riga è cambiata dopo la promozione, per impostazione predefinita l'operazione viene annullata.

```php
use Merql\Rollback\RollbackDrift;

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

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

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

La verifica delle modifiche confronta le impronte delle righe, quindi l'ordine delle colonne non è rilevante.

## Applicare il ripristino

Se non ci sono modifiche, applicate le operazioni inverse.

```php
use Merql\Rollback\RollbackPlanApplier;

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

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

Il ripristino applica un normale `MergeResult` creato da operazioni inverse. Viene eseguito nello stesso flusso transazionale di applicazione delle normali fusioni.

## Serializzazione

Salvate i piani di ripristino prima di applicare la fusione diretta. Tale artefatto costituisce la base persistente per un ripristino successivo.

```php
use Merql\Rollback\RollbackPlanSerializer;

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

Le applicazioni dovrebbero salvare il piano serializzato insieme all'operazione che ha eseguito la scrittura live, con tutti i metadati di audit specifici dell'host.
