
# 롤백

롤백 계획은 선택한 병합 작업을 적용한 후 되돌리는 방법을 설명합니다. 쓰기 직전의 정확한 라이브 행을 사용해 적용 전에 만듭니다.

기준 스냅샷만으로 롤백을 생성하지 마세요. 기준은 공통 조상이며, 덮어쓰려는 라이브 상태와 반드시 같지는 않습니다.

## 롤백 계획 만들기

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

계획에는 다음이 저장됩니다:

- 병합 계획 ID,
- 선택한 작업 ID,
- 영향을 받은 테이블,
- 역작업,
- 이전 행,
- 예상 이후 행,
- 메타데이터,
- 롤백 해시,
- 스키마 버전.

## 변경 확인

롤백을 적용하기 전에 현재 라이브 행을 롤백 계획의 예상 이후 행과 비교합니다. 승격 후 행이 하나라도 변경되었다면 기본적으로 중단합니다.

```php
use Merql\Rollback\RollbackDrift;

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

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

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

변경 확인은 행 지문을 비교하므로 열 순서는 중요하지 않습니다.

## 롤백 적용

변경이 없다면 역작업을 적용합니다.

```php
use Merql\Rollback\RollbackPlanApplier;

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

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

롤백은 역작업에서 만든 일반 `MergeResult`을 적용합니다. 일반 병합 적용과 동일한 트랜잭션 적용 경로로 실행됩니다.

## 직렬화

정방향 병합을 적용하기 전에 롤백 계획을 저장하세요. 이 산출물은 나중에 롤백할 때 지속적으로 사용할 기반입니다.

```php
use Merql\Rollback\RollbackPlanSerializer;

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

애플리케이션은 라이브 쓰기를 수행한 작업과 함께 직렬화된 계획 및 호스트별 감사 메타데이터를 저장해야 합니다.
