Skip to content

Commit 88279c4

Browse files
committed
Remove $retryProducer from WorkerInterface::process()
It was only ever supplied by `SynchronousPushHandler`, so that a message failed under `SyncQueueProducer` could be re-queued into the producer that executed it. With no broker behind that producer, such a retry is an immediate recursive re-execution in the same call stack: it blocks the caller for the whole attempt budget, grows the stack by one frame per attempt, and silently drops the backoff, since `DelayEnvelope` needs an adapter to be honored. Retry destinations are now resolved uniformly in both modes: an explicit `targetQueue`, or `QueueProducerProviderInterface` keyed by the execution queue name. When neither is configured, retry fails with `InvalidQueueConfigException` instead of degrading silently. `SynchronousPushHandler` now takes the queue name instead of the producer, which also removes the self-reference from `SyncQueueProducer`.
1 parent ae44e2b commit 88279c4

17 files changed

Lines changed: 266 additions & 56 deletions

docs/guide/en/error-handling-advanced.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ This document covers advanced internals of the failure handling pipeline, built-
1919
- the message
2020
- the caught exception
2121
- the logical queue name
22-
- an optional direct retry producer (provided for synchronous producer execution)
2322

2423
4. A failure pipeline is selected by queue name
2524

@@ -84,7 +83,7 @@ This interface has the only method `processFailure` with these parameters:
8483
- [`FailureHandlingRequest $request`](../../../src/Middleware/FailureHandling/FailureHandlingRequest.php) - a request for a message handling. It consists of
8584
- a [message](../../../src/Message/MessageInterface.php)
8685
- a `Throwable $exception` object thrown on the `request` handling
87-
- the logical queue name the message came from and, when available, a direct retry producer
86+
- the logical queue name the message came from
8887
- `FailureHandlerInterface $handler` - failure strategy pipeline continuation. Your Middleware should call `$handler->handleFailure($request)` when the middleware itself should not interrupt failure pipeline execution.
8988

9089
> Note: your strategy have to check by its own if it should be applied. Look into [`SendAgainMiddleware::suits()`](../../../src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php#L54) for an example.

docs/guide/en/error-handling.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ Failures of messages that arrived in the `failed-messages` queue directly (bypas
8787

8888
- `id` - A unique string. Allows to use this strategy more than once for the same message, just like in example above.
8989
- `maxAttempts` - Maximum attempts count for this strategy with the given $id before it will give up.
90-
- `targetQueue` - An optional `QueueProducerInterface` for an explicit retry destination. When it is `null`, synchronous execution supplies its originating producer; asynchronous execution resolves the originating queue name through `producerProvider`.
91-
- `producerProvider` - The `QueueProducerProviderInterface` used to resolve the source producer for asynchronous retries when no `targetQueue` is supplied. Configure it, or provide `targetQueue`; otherwise retry fails with a configuration error.
90+
- `targetQueue` - An optional `QueueProducerInterface` for an explicit retry destination. When it is `null`, the originating queue name is resolved through `producerProvider`.
91+
- `producerProvider` - The `QueueProducerProviderInterface` used to resolve the source producer when no `targetQueue` is supplied. Configure it, or provide `targetQueue`; otherwise retry fails with a configuration error.
9292

9393
State tracking:
9494

@@ -106,8 +106,8 @@ It's configured via constructor parameters, too. Here they are:
106106
- `delayInitial` - The initial delay that will be applied to a message for the first time. It must be a positive float.
107107
- `delayMaximum` - The maximum delay which can be applied to a single message. Must be above the `delayInitial`.
108108
- `exponent` - Message handling delay will be multiplied by exponent each time it fails.
109-
- `queue` - An optional `QueueProducerInterface` retry destination. When it is `null`, synchronous execution supplies its originating producer; asynchronous execution resolves the originating queue name through `producerProvider`.
110-
- `producerProvider` - The `QueueProducerProviderInterface` used for that asynchronous source-producer lookup.
109+
- `queue` - An optional `QueueProducerInterface` retry destination. When it is `null`, the originating queue name is resolved through `producerProvider`.
110+
- `producerProvider` - The `QueueProducerProviderInterface` used for that source-producer lookup.
111111

112112
Requirements:
113113

docs/guide/en/queue-capabilities.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ $definitions = [
2020

2121
`QueueFactoryProvider` accepts factory definitions in each role. `PredefinedQueueProvider` uses the same outer shape but each role value must already be its respective interface instance. A raw definition such as `'orders' => ['class' => AsyncQueueProducer::class]`, an empty role map, and unknown role keys are invalid.
2222

23-
`QueueInterface`, `Queue`, and `QueueProviderInterface` were removed before release. Replace them with `QueueProducerInterface`, `SyncQueueProducer` / `AsyncQueueProducer` / `QueueConsumer`, and the relevant typed provider. Synchronous consumers retain no-op `run()` and `listen()` behavior when no adapter is configured. Default retry of an asynchronously consumed message resolves a producer for the execution queue name through a configured producer provider; if none is available it fails with an actionable configuration error rather than dropping the message.
23+
`QueueInterface`, `Queue`, and `QueueProviderInterface` were removed before release. Replace them with `QueueProducerInterface`, `SyncQueueProducer` / `AsyncQueueProducer` / `QueueConsumer`, and the relevant typed provider. Synchronous consumers retain no-op `run()` and `listen()` behavior when no adapter is configured. Default retry resolves a producer for the execution queue name through a configured producer provider, in both synchronous and asynchronous execution; if none is available it fails with an actionable configuration error rather than dropping the message.

src/Debug/QueueWorkerInterfaceProxy.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Yiisoft\Queue\Debug;
66

77
use Yiisoft\Queue\Message\MessageInterface;
8-
use Yiisoft\Queue\QueueProducerInterface;
98
use Yiisoft\Queue\Worker\WorkerInterface;
109

1110
/**
@@ -18,12 +17,9 @@ public function __construct(
1817
private readonly QueueCollector $collector,
1918
) {}
2019

21-
public function process(
22-
MessageInterface $message,
23-
string $queueName,
24-
?QueueProducerInterface $retryProducer = null,
25-
): void {
20+
public function process(MessageInterface $message, string $queueName): void
21+
{
2622
$this->collector->collectWorkerProcessing($message, $queueName);
27-
$this->worker->process($message, $queueName, $retryProducer);
23+
$this->worker->process($message, $queueName);
2824
}
2925
}

src/Middleware/FailureHandling/FailureHandlingRequest.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@
66

77
use Throwable;
88
use Yiisoft\Queue\Message\MessageInterface;
9-
use Yiisoft\Queue\QueueProducerInterface;
109

1110
final class FailureHandlingRequest
1211
{
1312
public function __construct(
1413
private MessageInterface $message,
1514
private Throwable $exception,
1615
private string $queueName,
17-
private ?QueueProducerInterface $retryProducer = null,
1816
) {}
1917

2018
public function getMessage(): MessageInterface
@@ -33,12 +31,6 @@ public function getQueueName(): string
3331
return $this->queueName;
3432
}
3533

36-
/** Direct retry target used by synchronous producer execution, if any. */
37-
public function getRetryProducer(): ?QueueProducerInterface
38-
{
39-
return $this->retryProducer;
40-
}
41-
4234
public function withMessage(MessageInterface $message): self
4335
{
4436
$instance = clone $this;

src/Middleware/FailureHandling/Implementation/ExponentialDelayMiddleware.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,6 @@ private function producer(FailureHandlingRequest $request): QueueProducerInterfa
6363
if ($this->queue !== null) {
6464
return $this->queue;
6565
}
66-
if ($request->getRetryProducer() !== null) {
67-
return $request->getRetryProducer();
68-
}
6966
if ($this->producerProvider === null) {
7067
throw new InvalidQueueConfigException(sprintf('Cannot retry queue "%s": configure a producer target or QueueProducerProviderInterface.', $request->getQueueName()));
7168
}

src/Middleware/FailureHandling/Implementation/SendAgainMiddleware.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function processFailure(FailureHandlingRequest $request, FailureHandlerIn
4040
return $handler->handleFailure($request);
4141
}
4242
$envelope = new FailureEnvelope($message, [$this->getMetaKey() => $this->getAttempts($message) + 1]);
43-
$producer = $this->targetQueue ?? $request->getRetryProducer() ?? $this->sourceProducer($request);
43+
$producer = $this->targetQueue ?? $this->sourceProducer($request);
4444
$envelope = $producer->push($envelope);
4545
return $request->withMessage($envelope);
4646
}

src/Middleware/Push/SynchronousPushHandler.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
namespace Yiisoft\Queue\Middleware\Push;
66

77
use Yiisoft\Queue\Message\MessageInterface;
8-
use Yiisoft\Queue\QueueProducerInterface;
98
use Yiisoft\Queue\Worker\WorkerInterface;
109

1110
/**
@@ -15,12 +14,12 @@ final class SynchronousPushHandler implements PushHandlerInterface
1514
{
1615
public function __construct(
1716
private readonly WorkerInterface $worker,
18-
private readonly QueueProducerInterface $queue,
17+
private readonly string $queueName,
1918
) {}
2019

2120
public function handlePush(MessageInterface $message): MessageInterface
2221
{
23-
$this->worker->process($message, $this->queue->getQueueName(), $this->queue);
22+
$this->worker->process($message, $this->queueName);
2423

2524
return $message;
2625
}

src/SyncQueueProducer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function __construct(
3434
$this->dispatcher = new PushMiddlewareDispatcher(
3535
middlewareFactory: $middlewareConfig->middlewareFactory,
3636
middlewareDefinitions: [...$middlewareConfig->commonMiddlewareDefinitions, ...$middlewareDefinitions],
37-
finalHandler: new SynchronousPushHandler($worker, $this),
37+
finalHandler: new SynchronousPushHandler($worker, $this->queueName),
3838
);
3939
}
4040

src/Worker/Worker.php

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Yiisoft\Queue\Middleware\FailureHandling\FailureFinalHandler;
1616
use Yiisoft\Queue\Middleware\FailureHandling\FailureHandlingRequest;
1717
use Yiisoft\Queue\Middleware\FailureHandling\FailureMiddlewareDispatcher;
18-
use Yiisoft\Queue\QueueProducerInterface;
1918
use Yiisoft\Queue\Message\IdEnvelope;
2019

2120
final class Worker implements WorkerInterface
@@ -30,11 +29,8 @@ public function __construct(
3029
/**
3130
* @throws Throwable
3231
*/
33-
public function process(
34-
MessageInterface $message,
35-
string $queueName,
36-
?QueueProducerInterface $retryProducer = null,
37-
): void {
32+
public function process(MessageInterface $message, string $queueName): void
33+
{
3834
$messageId = IdEnvelope::fromMessage($message)->getId();
3935
if ($messageId === null) {
4036
$this->logger->info('Processing message without ID.');
@@ -48,7 +44,7 @@ public function process(
4844
$finalHandler = new ConsumeFinalHandler($handler->handle(...));
4945
$this->consumeMiddlewareDispatcher->dispatch($request, $finalHandler);
5046
} catch (Throwable $exception) {
51-
$request = new FailureHandlingRequest($request->getMessage(), $exception, $request->getQueueName(), $retryProducer);
47+
$request = new FailureHandlingRequest($request->getMessage(), $exception, $request->getQueueName());
5248

5349
try {
5450
$this->failureMiddlewareDispatcher->dispatch($request, new FailureFinalHandler());

0 commit comments

Comments
 (0)