
# Компенсации в работни потоци

Компенсацията ви позволява да добавите обработчик за връщане на промените към завършена стъпка на работен поток. Queuety съхранява снимка на публичното състояние при завършване на стъпката, след което изпълнява обработчиците за компенсация в обратен ред при отмяна на работния поток или по избор при неуспешно завършване.

## Методът `compensate_with()`

Извикайте `compensate_with()` непосредствено след стъпката, която трябва да отмени:

```php
use Queuety\Queuety;

Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( SendWelcomeEmailStep::class )
    ->dispatch( [ 'user_id' => 42 ] );
```

Ако работният поток бъде отменен по-късно, се изпълнява Queuety:

1. `CancelSubscriptionCompensation`
2. `DeleteRemoteUserCompensation`

## Обработчици за компенсация

Обработчиците за компенсация реализират `Contracts\Compensation`:

```php
use Queuety\Contracts\Compensation;

final class DeleteRemoteUserCompensation implements Compensation {
    public function handle( array $state ): void {
        remote_api_delete_user( $state['remote_user_id'] );
    }
}
```

Обработчикът получава публичното състояние на работния поток във вида му непосредствено след завършването на компенсираната стъпка. Запазените вътрешни ключове не се включват.

## Отмяна срещу неуспешно завършване

При изрична отмяна компенсациите на завършените стъпки винаги се изпълняват преди незадължителния обработчик `on_cancel()`.

При неуспешно завършване компенсациите се изпълняват само ако изберете тази опция:

```php
Queuety::workflow( 'provision_account' )
    ->then( CreateRemoteUserStep::class )
    ->compensate_with( DeleteRemoteUserCompensation::class )
    ->then( CreateSubscriptionStep::class )
    ->compensate_with( CancelSubscriptionCompensation::class )
    ->then( ChargeCardStep::class )
    ->compensate_on_failure()
    ->dispatch( $payload );
```

Без `compensate_on_failure()` неуспешният работен поток запазва историята на завършените стъпки и може да бъде стартиран отново от неуспешната стъпка.

С `compensate_on_failure()` компенсациите на завършените стъпки се изпълняват след отбелязване на работния поток като неуспешен. След това `retry_workflow()` хвърля изключение, защото работният поток вече е компенсиран.

## Поддържани типове стъпки

Компенсация може да се добави към:

- последователни стъпки `then()`
- групи `parallel()`
- стъпки `for_each()`
- стъпки `run_workflow()`
- стъпки за забавяне `delay()`
- стъпки за сигнал `wait_for_signal()`
- стъпки за зависимост между работни потоци `wait_for_workflow()`

За `run_workflow()` и `for_each()` снимката за компенсация се записва, след като вложената или разклонената работа приключи напълно.

## `on_cancel()` срещу компенсация

Използвайте `compensate_with()`, за да отмените страничните ефекти на завършените стъпки в обратен ред.

Използвайте `on_cancel()` за един обработчик за почистване на ниво работен поток, който трябва да се изпълнява при всяка изрична отмяна.
