fix(events): close deferred scope after listener failure
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -60,6 +60,7 @@ final class EventDispatcher implements EventDispatcherInterface, DeferredEventPr
|
|||||||
throw new \LogicException('Cannot process deferred events for an inactive execution.');
|
throw new \LogicException('Cannot process deferred events for an inactive execution.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
$processed = 0;
|
$processed = 0;
|
||||||
$deadline = microtime(true) + 1.0;
|
$deadline = microtime(true) + 1.0;
|
||||||
$deadlineExceeded = false;
|
$deadlineExceeded = false;
|
||||||
@@ -78,16 +79,15 @@ final class EventDispatcher implements EventDispatcherInterface, DeferredEventPr
|
|||||||
$processed += $this->invoke($event, DeliveryMode::Deferred);
|
$processed += $this->invoke($event, DeliveryMode::Deferred);
|
||||||
}
|
}
|
||||||
|
|
||||||
$remaining = count($this->deferred[$executionId]);
|
|
||||||
unset($this->deferred[$executionId]);
|
|
||||||
$this->activeExecution = null;
|
|
||||||
|
|
||||||
return new DeferredProcessingResult(
|
return new DeferredProcessingResult(
|
||||||
$processed,
|
$processed,
|
||||||
$remaining,
|
count($this->deferred[$executionId]),
|
||||||
$deadlineExceeded,
|
$deadlineExceeded,
|
||||||
$limitExceeded,
|
$limitExceeded,
|
||||||
);
|
);
|
||||||
|
} finally {
|
||||||
|
$this->discardDeferred($executionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function discardDeferred(string $executionId): void
|
public function discardDeferred(string $executionId): void
|
||||||
|
|||||||
@@ -167,6 +167,43 @@ final class EventDispatcherTest extends TestCase
|
|||||||
$dispatcher->dispatch(new Event('test.event'));
|
$dispatcher->dispatch(new Event('test.event'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Test]
|
||||||
|
#[TestDox('Propagated deferred failures close their execution scope')]
|
||||||
|
public function recoversFromDeferredFailure(): void
|
||||||
|
{
|
||||||
|
$registry = new EventListenerRegistry();
|
||||||
|
$registry->listen(
|
||||||
|
'test',
|
||||||
|
'test.event',
|
||||||
|
FailingListener::class,
|
||||||
|
'fail',
|
||||||
|
DeliveryMode::Deferred,
|
||||||
|
failurePolicy: FailurePolicy::Propagate,
|
||||||
|
);
|
||||||
|
$registry->freeze();
|
||||||
|
|
||||||
|
$dispatcher = new EventDispatcher(
|
||||||
|
$registry,
|
||||||
|
new RecordingContainer([FailingListener::class => new FailingListener()]),
|
||||||
|
new NullLogger(),
|
||||||
|
);
|
||||||
|
$dispatcher->beginExecution('failed');
|
||||||
|
$dispatcher->dispatch(new Event('test.event'));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$dispatcher->processDeferred('failed');
|
||||||
|
self::fail('Expected deferred listener failure to propagate.');
|
||||||
|
} catch (\RuntimeException $error) {
|
||||||
|
self::assertSame('Listener failed.', $error->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
$dispatcher->beginExecution('next');
|
||||||
|
$result = $dispatcher->processDeferred('next');
|
||||||
|
|
||||||
|
self::assertSame(0, $result->processed);
|
||||||
|
self::assertSame(0, $result->remaining);
|
||||||
|
}
|
||||||
|
|
||||||
#[Test]
|
#[Test]
|
||||||
#[TestDox('Deferred processing stops at its configured count limit')]
|
#[TestDox('Deferred processing stops at its configured count limit')]
|
||||||
public function boundsDeferredWork(): void
|
public function boundsDeferredWork(): void
|
||||||
|
|||||||
Reference in New Issue
Block a user