private function handle(TaskExecutionInterface $execution)
    {
        $this->eventDispatcher->dispatch(
            Events::TASK_BEFORE,
            new TaskExecutionEvent($execution->getTask(), $execution)
        );

        try {
            return $this->executor->execute($execution);
        } catch (RetryException $exception) {
            // this find is necessary because the storage could be
            // invalid (clear in doctrine) after handling an execution.
            $execution = $this->taskExecutionRepository->findByUuid($execution->getUuid());

            if ($execution->getAttempts() === $exception->getMaximumAttempts()) {
                throw $exception->getPrevious();
            }

            $execution->reset();
            $execution->incrementAttempts();

            $this->eventDispatcher->dispatch(
                Events::TASK_RETRIED,
                new TaskExecutionEvent($execution->getTask(), $execution)
            );

            $this->taskExecutionRepository->save($execution);

            throw new ExitException();
        } finally {
            $this->eventDispatcher->dispatch(
                Events::TASK_AFTER,
                new TaskExecutionEvent($execution->getTask(), $execution)
            );
        }
    }